aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG16
-rw-r--r--DESCRIPTION2
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/IOpenPgpCallback.aidl45
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/IOpenPgpKeyIdsCallback.aidl39
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/IOpenPgpService.aidl143
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/OpenPgpData.aidl20
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/OpenPgpError.aidl20
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/OpenPgpSignatureResult.aidl20
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/IOpenPgpCallback.aidl45
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/IOpenPgpKeyIdsCallback.aidl39
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/IOpenPgpService.aidl143
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpConstants.java10
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpData.aidl20
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpData.java127
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpError.aidl20
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpHelper.java52
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpSignatureResult.aidl20
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/java/org/sufficientlysecure/keychain/demo/AidlDemoActivity2.java168
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/java/org/sufficientlysecure/keychain/demo/OpenPgpProviderActivity.java352
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/res/layout/aidl_demo2.xml40
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/res/layout/crypto_provider_demo.xml83
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/res/layout/intent_demo.xml93
-rw-r--r--OpenPGP-Keychain-API-Demo/src/main/res/xml/base_preference.xml23
-rw-r--r--OpenPGP-Keychain-API/.gitignore (renamed from OpenPGP-Keychain-API-Demo/.gitignore)0
-rw-r--r--OpenPGP-Keychain-API/build.gradle3
-rw-r--r--OpenPGP-Keychain-API/example-app/build.gradle (renamed from OpenPGP-Keychain-API-Demo/build.gradle)10
-rw-r--r--OpenPGP-Keychain-API/example-app/ic_launcher-web.png (renamed from OpenPGP-Keychain-API-Demo/ic_launcher-web.png)bin23512 -> 23512 bytes
-rw-r--r--OpenPGP-Keychain-API/example-app/src/main/AndroidManifest.xml (renamed from OpenPGP-Keychain-API-Demo/src/main/AndroidManifest.xml)13
-rw-r--r--OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/BaseActivity.java (renamed from OpenPGP-Keychain-API-Demo/src/main/java/org/sufficientlysecure/keychain/demo/BaseActivity.java)30
-rw-r--r--OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/Constants.java (renamed from OpenPGP-Keychain-API-Demo/src/main/java/org/sufficientlysecure/keychain/demo/Constants.java)6
-rw-r--r--OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/IntentActivity.java583
-rw-r--r--OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/OpenPgpProviderActivity.java302
-rw-r--r--OpenPGP-Keychain-API/example-app/src/main/res/drawable-hdpi/ic_launcher.png (renamed from OpenPGP-Keychain-API-Demo/src/main/res/drawable-hdpi/ic_launcher.png)bin2626 -> 2626 bytes
-rw-r--r--OpenPGP-Keychain-API/example-app/src/main/res/drawable-mdpi/ic_launcher.png (renamed from OpenPGP-Keychain-API-Demo/src/main/res/drawable-mdpi/ic_launcher.png)bin1732 -> 1732 bytes
-rw-r--r--OpenPGP-Keychain-API/example-app/src/main/res/drawable-xhdpi/ic_launcher.png (renamed from OpenPGP-Keychain-API-Demo/src/main/res/drawable-xhdpi/ic_launcher.png)bin3517 -> 3517 bytes
-rw-r--r--OpenPGP-Keychain-API/example-app/src/main/res/drawable-xxhdpi/ic_launcher.png (renamed from OpenPGP-Keychain-API-Demo/src/main/res/drawable-xxhdpi/ic_launcher.png)bin5717 -> 5717 bytes
-rw-r--r--OpenPGP-Keychain-API/example-app/src/main/res/layout/openpgp_provider.xml108
-rw-r--r--OpenPGP-Keychain-API/example-app/src/main/res/xml/base_preference.xml18
-rw-r--r--OpenPGP-Keychain-API/example-app/src/main/res/xml/intent_preference.xml30
-rw-r--r--OpenPGP-Keychain-API/gradle/gradle/wrapper/gradle-wrapper.jar (renamed from OpenPGP-Keychain-API-Demo/gradle/wrapper/gradle-wrapper.jar)bin50508 -> 50557 bytes
-rw-r--r--OpenPGP-Keychain-API/gradle/gradle/wrapper/gradle-wrapper.properties (renamed from OpenPGP-Keychain-API-Demo/gradle/wrapper/gradle-wrapper.properties)4
-rwxr-xr-xOpenPGP-Keychain-API/gradle/gradlew (renamed from OpenPGP-Keychain-API-Demo/gradlew)0
-rw-r--r--OpenPGP-Keychain-API/gradle/gradlew.bat (renamed from OpenPGP-Keychain-API-Demo/gradlew.bat)0
-rw-r--r--OpenPGP-Keychain-API/gradle/wrapper/gradle-wrapper.jarbin0 -> 50557 bytes
-rw-r--r--OpenPGP-Keychain-API/gradle/wrapper/gradle-wrapper.properties6
-rwxr-xr-xOpenPGP-Keychain-API/gradlew164
-rw-r--r--OpenPGP-Keychain-API/gradlew.bat90
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/.gitignore29
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/AndroidManifest.xml13
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/LICENSE202
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/build.gradle35
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/project.properties15
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-hdpi/ic_action_cancel_launchersize.pngbin0 -> 1520 bytes
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-mdpi/ic_action_cancel_launchersize.pngbin0 -> 1032 bytes
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-xhdpi/ic_action_cancel_launchersize.pngbin0 -> 1570 bytes
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-xxhdpi/ic_action_cancel_launchersize.pngbin0 -> 2345 bytes
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/res/values/strings.xml6
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/IOpenPgpService.aidl85
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/OpenPgpError.java (renamed from OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpError.java)11
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/OpenPgpSignatureResult.java (renamed from OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpSignatureResult.java)10
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpApi.java198
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpConstants.java54
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpListPreference.java (renamed from OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpListPreference.java)100
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpServiceConnection.java (renamed from OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpServiceConnection.java)35
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpUtils.java64
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java103
-rw-r--r--OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/sufficientlysecure/keychain/api/OpenKeychainIntents.java37
-rw-r--r--OpenPGP-Keychain-API/settings.gradle2
-rw-r--r--OpenPGP-Keychain/build.gradle4
-rw-r--r--OpenPGP-Keychain/src/main/AndroidManifest.xml141
-rw-r--r--OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/IOpenPgpCallback.aidl45
-rw-r--r--OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/IOpenPgpKeyIdsCallback.aidl39
-rw-r--r--OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/IOpenPgpService.aidl143
-rw-r--r--OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/OpenPgpData.aidl20
-rw-r--r--OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/OpenPgpError.aidl20
-rw-r--r--OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/OpenPgpSignatureResult.aidl20
-rw-r--r--OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpConstants.java10
-rw-r--r--OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpData.java127
-rw-r--r--OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpHelper.java52
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java782
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java5
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java10
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpOperation.java1154
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java605
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java5
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java28
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java85
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java10
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/exception/NoUserIdsException.java10
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/exception/UserInteractionRequiredException.java10
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/exception/WrongPassphraseException.java10
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java11
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java833
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java217
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java177
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/exception/WrongPackageSignatureException.java)2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SignKeyActivity.java)26
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java211
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java)13
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java91
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpFragmentHtml.java)19
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java186
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java17
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java15
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java20
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java82
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java42
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java371
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java92
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java313
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java17
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java39
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java8
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java12
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java84
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java13
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java99
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/LookupUnknownKeyDialogFragment.java136
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java9
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml44
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_app_settings_activity.xml26
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml216
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_apps_adapter_list_item.xml20
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/certify_key_activity.xml (renamed from OpenPGP-Keychain/src/main/res/layout/sign_key_activity.xml)4
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml57
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/drawer_list_item.xml25
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_list_item.xml24
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_list_public_item.xml45
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_list_secret_activity.xml10
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_list_secret_item.xml32
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/view_key_activity.xml224
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml38
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml3
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml226
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/view_key_userids_item.xml7
-rw-r--r--OpenPGP-Keychain/src/main/res/menu/key_view.xml4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-de/help_about.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html8
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-de/help_nfc_beam.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-de/help_start.html23
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-de/nfc_beam_share.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es-rCO/help_changelog.html8
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es-rCO/help_nfc_beam.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es-rCO/help_start.html23
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es-rCO/nfc_beam_share.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es/help_about.html43
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es/help_changelog.html108
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es/help_nfc_beam.html12
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es/help_start.html19
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es/nfc_beam_share.html11
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-fr/help_about.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-fr/help_changelog.html6
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-fr/help_nfc_beam.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-fr/help_start.html23
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-fr/nfc_beam_share.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-it-rIT/help_changelog.html8
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-it-rIT/help_nfc_beam.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-it-rIT/help_start.html23
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-it-rIT/nfc_beam_share.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-ja/help_about.html43
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-ja/help_changelog.html108
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-ja/help_nfc_beam.html12
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-ja/help_start.html19
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-ja/nfc_beam_share.html11
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_changelog.html8
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_nfc_beam.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_start.html23
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-nl-rNL/nfc_beam_share.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_changelog.html8
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_nfc_beam.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_start.html23
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-pt-rBR/nfc_beam_share.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-ru/help_about.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-ru/help_changelog.html6
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-ru/help_start.html23
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_changelog.html8
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_nfc_beam.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_start.html23
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-sl-rSI/nfc_beam_share.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-tr/help_about.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-tr/help_changelog.html8
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-tr/help_nfc_beam.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-tr/help_start.html23
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-tr/nfc_beam_share.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-uk/help_about.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-uk/help_changelog.html6
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-uk/help_nfc_beam.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-uk/help_start.html23
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-zh/help_about.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-zh/help_changelog.html8
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-zh/help_nfc_beam.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-zh/help_start.html23
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-zh/nfc_beam_share.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw/help_about.html9
-rw-r--r--OpenPGP-Keychain/src/main/res/raw/help_changelog.html4
-rw-r--r--OpenPGP-Keychain/src/main/res/raw/help_start.html26
-rw-r--r--OpenPGP-Keychain/src/main/res/values-de/strings.xml47
-rw-r--r--OpenPGP-Keychain/src/main/res/values-es-rCO/strings.xml11
-rw-r--r--OpenPGP-Keychain/src/main/res/values-es/strings.xml393
-rw-r--r--OpenPGP-Keychain/src/main/res/values-fr/strings.xml49
-rw-r--r--OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml3
-rw-r--r--OpenPGP-Keychain/src/main/res/values-ja/strings.xml381
-rw-r--r--OpenPGP-Keychain/src/main/res/values-nl-rNL/strings.xml18
-rw-r--r--OpenPGP-Keychain/src/main/res/values-ru/strings.xml44
-rw-r--r--OpenPGP-Keychain/src/main/res/values-tr/strings.xml14
-rw-r--r--OpenPGP-Keychain/src/main/res/values-uk/strings.xml46
-rw-r--r--OpenPGP-Keychain/src/main/res/values-zh/strings.xml138
-rw-r--r--OpenPGP-Keychain/src/main/res/values/static_strings.xml2
-rw-r--r--OpenPGP-Keychain/src/main/res/values/strings.xml63
-rw-r--r--README.md86
-rw-r--r--Resources/old extended service/src/main/aidl/org/sufficientlysecure/keychain/service/remote/IExtendedApiCallback.aidl (renamed from OpenPGP-Keychain/src/main/aidl/org/sufficientlysecure/keychain/service/remote/IExtendedApiCallback.aidl)0
-rw-r--r--Resources/old extended service/src/main/aidl/org/sufficientlysecure/keychain/service/remote/IExtendedApiService.aidl (renamed from OpenPGP-Keychain/src/main/aidl/org/sufficientlysecure/keychain/service/remote/IExtendedApiService.aidl)0
-rw-r--r--Resources/old extended service/src/main/java/ExtendedApiService.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/ExtendedApiService.java)0
-rw-r--r--build.gradle4
-rw-r--r--gradle/wrapper/gradle-wrapper.properties4
-rw-r--r--libraries/Android-AppMsg/.gitignore36
-rw-r--r--libraries/Android-AppMsg/LICENSE.txt202
-rw-r--r--libraries/Android-AppMsg/README.md117
-rw-r--r--libraries/Android-AppMsg/library/AndroidManifest.xml12
-rw-r--r--libraries/Android-AppMsg/library/build.gradle22
-rw-r--r--libraries/Android-AppMsg/library/pom.xml71
-rw-r--r--libraries/Android-AppMsg/library/project.properties15
-rw-r--r--libraries/Android-AppMsg/library/res/layout/app_msg.xml20
-rw-r--r--libraries/Android-AppMsg/library/res/values/colors.xml8
-rw-r--r--libraries/Android-AppMsg/library/src/com/devspark/appmsg/AppMsg.java587
-rw-r--r--libraries/Android-AppMsg/library/src/com/devspark/appmsg/MsgManager.java340
-rw-r--r--libraries/keychain-api-library/.gitignore29
-rw-r--r--libraries/keychain-api-library/AndroidManifest.xml13
-rw-r--r--libraries/keychain-api-library/LICENSE202
-rw-r--r--libraries/keychain-api-library/build.gradle35
-rw-r--r--libraries/keychain-api-library/project.properties15
-rw-r--r--libraries/keychain-api-library/res/drawable-hdpi/ic_action_cancel_launchersize.pngbin0 -> 1520 bytes
-rw-r--r--libraries/keychain-api-library/res/drawable-mdpi/ic_action_cancel_launchersize.pngbin0 -> 1032 bytes
-rw-r--r--libraries/keychain-api-library/res/drawable-xhdpi/ic_action_cancel_launchersize.pngbin0 -> 1570 bytes
-rw-r--r--libraries/keychain-api-library/res/drawable-xxhdpi/ic_action_cancel_launchersize.pngbin0 -> 2345 bytes
-rw-r--r--libraries/keychain-api-library/res/values/strings.xml6
-rw-r--r--libraries/keychain-api-library/src/org/openintents/openpgp/IOpenPgpService.aidl85
-rw-r--r--libraries/keychain-api-library/src/org/openintents/openpgp/OpenPgpError.java (renamed from OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpError.java)11
-rw-r--r--libraries/keychain-api-library/src/org/openintents/openpgp/OpenPgpSignatureResult.java (renamed from OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpSignatureResult.java)10
-rw-r--r--libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpApi.java198
-rw-r--r--libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpConstants.java54
-rw-r--r--libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpListPreference.java (renamed from OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpListPreference.java)102
-rw-r--r--libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpServiceConnection.java (renamed from OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpServiceConnection.java)35
-rw-r--r--libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpUtils.java64
-rw-r--r--libraries/keychain-api-library/src/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java103
-rw-r--r--libraries/keychain-api-library/src/org/sufficientlysecure/keychain/api/OpenKeychainIntents.java37
-rw-r--r--settings.gradle2
259 files changed, 10079 insertions, 5932 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 43a326517..548dd1289 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,17 @@
+2.3.1
+* hotfix for crash when upgrading from old versions
+
+2.3
+* remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)
+* fix setting expiry dates on keys (thanks to Ash Hughes)
+* more internal fixes when editing keys (thanks to Ash Hughes)
+* querying keyservers directly from the import screen
+* fix layout and dialog style on Android 2.2-3.0
+* fix crash on keys with empty user ids
+* fix crash and empty lists when coming back from signing screen
+* Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source
+* fix upload of key from signing screen
+
2.2
* New design with navigation drawer
* New public key list design
@@ -23,7 +37,7 @@
* New AIDL API
1.0.8
-* basic key server support (HKP, please report bugs :))
+* basic keyserver support (HKP, please report bugs :))
* app2sd (untested, let me know if there are problems)
* more choices for pass phrase cache: 1, 2, 4, 8, hours
* translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
diff --git a/DESCRIPTION b/DESCRIPTION
index 9f7beb76a..be5ebc0c7 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -17,7 +17,7 @@ License:
GPLv3+
Permissions:
-• Full network access: Internet access is ONLY used for key server queries
+• Full network access: Internet access is ONLY used for keyserver queries
• Control Near Field Communication: To exchange keys via NFC
• Modify or delete the contents of your USB storage: To export keys to the sd card
• Read the contents of your USB storage: To import keys from the sd card
diff --git a/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/IOpenPgpCallback.aidl b/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/IOpenPgpCallback.aidl
deleted file mode 100644
index ba41de1ba..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/IOpenPgpCallback.aidl
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-import org.openintents.openpgp.OpenPgpData;
-import org.openintents.openpgp.OpenPgpSignatureResult;
-import org.openintents.openpgp.OpenPgpError;
-
-interface IOpenPgpCallback {
-
- /**
- * onSuccess returns on successful OpenPGP operations.
- *
- * @param output
- * contains resulting output (decrypted content (when input was encrypted)
- * or content without signature (when input was signed-only))
- * @param signatureResult
- * signatureResult is only non-null if decryptAndVerify() was called and the content
- * was encrypted or signed-and-encrypted.
- */
- oneway void onSuccess(in OpenPgpData output, in OpenPgpSignatureResult signatureResult);
-
- /**
- * onError returns on errors or when allowUserInteraction was set to false, but user interaction
- * was required execute an OpenPGP operation.
- *
- * @param error
- * See OpenPgpError class for more information.
- */
- oneway void onError(in OpenPgpError error);
-} \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/IOpenPgpKeyIdsCallback.aidl b/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/IOpenPgpKeyIdsCallback.aidl
deleted file mode 100644
index 4ca356fad..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/IOpenPgpKeyIdsCallback.aidl
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-import org.openintents.openpgp.OpenPgpError;
-
-interface IOpenPgpKeyIdsCallback {
-
- /**
- * onSuccess returns on successful getKeyIds operations.
- *
- * @param keyIds
- * returned key ids
- */
- oneway void onSuccess(in long[] keyIds);
-
- /**
- * onError returns on errors or when allowUserInteraction was set to false, but user interaction
- * was required execute an OpenPGP operation.
- *
- * @param error
- * See OpenPgpError class for more information.
- */
- oneway void onError(in OpenPgpError error);
-} \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/IOpenPgpService.aidl b/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/IOpenPgpService.aidl
deleted file mode 100644
index 8f9e8a0fd..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/IOpenPgpService.aidl
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-import org.openintents.openpgp.OpenPgpData;
-import org.openintents.openpgp.IOpenPgpCallback;
-import org.openintents.openpgp.IOpenPgpKeyIdsCallback;
-
-/**
- * All methods are oneway, which means they are asynchronous and non-blocking.
- * Results are returned to the callback, which has to be implemented on client side.
- */
-interface IOpenPgpService {
-
- /**
- * Sign
- *
- * After successful signing, callback's onSuccess will contain the resulting output.
- *
- * @param input
- * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
- * @param output
- * Request output format by defining OpenPgpData object
- *
- * new OpenPgpData(OpenPgpData.TYPE_STRING)
- * Returns as String
- * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
- * new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
- * Returns as byte[]
- * new OpenPgpData(uri)
- * Writes output to given Uri
- * new OpenPgpData(fileDescriptor)
- * Writes output to given ParcelFileDescriptor
- * @param callback
- * Callback where to return results
- */
- oneway void sign(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
-
- /**
- * Encrypt
- *
- * After successful encryption, callback's onSuccess will contain the resulting output.
- *
- * @param input
- * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
- * @param output
- * Request output format by defining OpenPgpData object
- *
- * new OpenPgpData(OpenPgpData.TYPE_STRING)
- * Returns as String
- * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
- * new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
- * Returns as byte[]
- * new OpenPgpData(uri)
- * Writes output to given Uri
- * new OpenPgpData(fileDescriptor)
- * Writes output to given ParcelFileDescriptor
- * @param keyIds
- * Key Ids of recipients. Can be retrieved with getKeyIds()
- * @param callback
- * Callback where to return results
- */
- oneway void encrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
-
- /**
- * Sign then encrypt
- *
- * After successful signing and encryption, callback's onSuccess will contain the resulting output.
- *
- * @param input
- * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
- * @param output
- * Request output format by defining OpenPgpData object
- *
- * new OpenPgpData(OpenPgpData.TYPE_STRING)
- * Returns as String
- * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
- * new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
- * Returns as byte[]
- * new OpenPgpData(uri)
- * Writes output to given Uri
- * new OpenPgpData(fileDescriptor)
- * Writes output to given ParcelFileDescriptor
- * @param keyIds
- * Key Ids of recipients. Can be retrieved with getKeyIds()
- * @param callback
- * Callback where to return results
- */
- oneway void signAndEncrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
-
- /**
- * Decrypts and verifies given input bytes. This methods handles encrypted-only, signed-and-encrypted,
- * and also signed-only input.
- *
- * After successful decryption/verification, callback's onSuccess will contain the resulting output.
- * The signatureResult in onSuccess is only non-null if signed-and-encrypted or signed-only inputBytes were given.
- *
- * @param input
- * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
- * @param output
- * Request output format by defining OpenPgpData object
- *
- * new OpenPgpData(OpenPgpData.TYPE_STRING)
- * Returns as String
- * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
- * new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
- * Returns as byte[]
- * new OpenPgpData(uri)
- * Writes output to given Uri
- * new OpenPgpData(fileDescriptor)
- * Writes output to given ParcelFileDescriptor
- * @param callback
- * Callback where to return results
- */
- oneway void decryptAndVerify(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
-
- /**
- * Get available key ids based on given user ids
- *
- * @param ids
- * User Ids (emails) of recipients OR key ids
- * @param allowUserInteraction
- * Enable user interaction to lookup and import unknown keys
- * @param callback
- * Callback where to return results (different type than callback in other functions!)
- */
- oneway void getKeyIds(in String[] ids, in boolean allowUserInteraction, in IOpenPgpKeyIdsCallback callback);
-
-} \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/OpenPgpData.aidl b/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/OpenPgpData.aidl
deleted file mode 100644
index 3711e4fb4..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/OpenPgpData.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-// Declare OpenPgpData so AIDL can find it and knows that it implements the parcelable protocol.
-parcelable OpenPgpData; \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/OpenPgpError.aidl b/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/OpenPgpError.aidl
deleted file mode 100644
index 7a6bed1e6..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/OpenPgpError.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-// Declare OpenPgpError so AIDL can find it and knows that it implements the parcelable protocol.
-parcelable OpenPgpError; \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/OpenPgpSignatureResult.aidl b/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/OpenPgpSignatureResult.aidl
deleted file mode 100644
index e246792d0..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/aidl/org/openintents/openpgp/OpenPgpSignatureResult.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-// Declare OpenPgpSignatureResult so AIDL can find it and knows that it implements the parcelable protocol.
-parcelable OpenPgpSignatureResult; \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/IOpenPgpCallback.aidl b/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/IOpenPgpCallback.aidl
deleted file mode 100644
index ba41de1ba..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/IOpenPgpCallback.aidl
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-import org.openintents.openpgp.OpenPgpData;
-import org.openintents.openpgp.OpenPgpSignatureResult;
-import org.openintents.openpgp.OpenPgpError;
-
-interface IOpenPgpCallback {
-
- /**
- * onSuccess returns on successful OpenPGP operations.
- *
- * @param output
- * contains resulting output (decrypted content (when input was encrypted)
- * or content without signature (when input was signed-only))
- * @param signatureResult
- * signatureResult is only non-null if decryptAndVerify() was called and the content
- * was encrypted or signed-and-encrypted.
- */
- oneway void onSuccess(in OpenPgpData output, in OpenPgpSignatureResult signatureResult);
-
- /**
- * onError returns on errors or when allowUserInteraction was set to false, but user interaction
- * was required execute an OpenPGP operation.
- *
- * @param error
- * See OpenPgpError class for more information.
- */
- oneway void onError(in OpenPgpError error);
-} \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/IOpenPgpKeyIdsCallback.aidl b/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/IOpenPgpKeyIdsCallback.aidl
deleted file mode 100644
index 4ca356fad..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/IOpenPgpKeyIdsCallback.aidl
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-import org.openintents.openpgp.OpenPgpError;
-
-interface IOpenPgpKeyIdsCallback {
-
- /**
- * onSuccess returns on successful getKeyIds operations.
- *
- * @param keyIds
- * returned key ids
- */
- oneway void onSuccess(in long[] keyIds);
-
- /**
- * onError returns on errors or when allowUserInteraction was set to false, but user interaction
- * was required execute an OpenPGP operation.
- *
- * @param error
- * See OpenPgpError class for more information.
- */
- oneway void onError(in OpenPgpError error);
-} \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/IOpenPgpService.aidl b/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/IOpenPgpService.aidl
deleted file mode 100644
index 8f9e8a0fd..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/IOpenPgpService.aidl
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-import org.openintents.openpgp.OpenPgpData;
-import org.openintents.openpgp.IOpenPgpCallback;
-import org.openintents.openpgp.IOpenPgpKeyIdsCallback;
-
-/**
- * All methods are oneway, which means they are asynchronous and non-blocking.
- * Results are returned to the callback, which has to be implemented on client side.
- */
-interface IOpenPgpService {
-
- /**
- * Sign
- *
- * After successful signing, callback's onSuccess will contain the resulting output.
- *
- * @param input
- * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
- * @param output
- * Request output format by defining OpenPgpData object
- *
- * new OpenPgpData(OpenPgpData.TYPE_STRING)
- * Returns as String
- * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
- * new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
- * Returns as byte[]
- * new OpenPgpData(uri)
- * Writes output to given Uri
- * new OpenPgpData(fileDescriptor)
- * Writes output to given ParcelFileDescriptor
- * @param callback
- * Callback where to return results
- */
- oneway void sign(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
-
- /**
- * Encrypt
- *
- * After successful encryption, callback's onSuccess will contain the resulting output.
- *
- * @param input
- * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
- * @param output
- * Request output format by defining OpenPgpData object
- *
- * new OpenPgpData(OpenPgpData.TYPE_STRING)
- * Returns as String
- * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
- * new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
- * Returns as byte[]
- * new OpenPgpData(uri)
- * Writes output to given Uri
- * new OpenPgpData(fileDescriptor)
- * Writes output to given ParcelFileDescriptor
- * @param keyIds
- * Key Ids of recipients. Can be retrieved with getKeyIds()
- * @param callback
- * Callback where to return results
- */
- oneway void encrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
-
- /**
- * Sign then encrypt
- *
- * After successful signing and encryption, callback's onSuccess will contain the resulting output.
- *
- * @param input
- * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
- * @param output
- * Request output format by defining OpenPgpData object
- *
- * new OpenPgpData(OpenPgpData.TYPE_STRING)
- * Returns as String
- * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
- * new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
- * Returns as byte[]
- * new OpenPgpData(uri)
- * Writes output to given Uri
- * new OpenPgpData(fileDescriptor)
- * Writes output to given ParcelFileDescriptor
- * @param keyIds
- * Key Ids of recipients. Can be retrieved with getKeyIds()
- * @param callback
- * Callback where to return results
- */
- oneway void signAndEncrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
-
- /**
- * Decrypts and verifies given input bytes. This methods handles encrypted-only, signed-and-encrypted,
- * and also signed-only input.
- *
- * After successful decryption/verification, callback's onSuccess will contain the resulting output.
- * The signatureResult in onSuccess is only non-null if signed-and-encrypted or signed-only inputBytes were given.
- *
- * @param input
- * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
- * @param output
- * Request output format by defining OpenPgpData object
- *
- * new OpenPgpData(OpenPgpData.TYPE_STRING)
- * Returns as String
- * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
- * new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
- * Returns as byte[]
- * new OpenPgpData(uri)
- * Writes output to given Uri
- * new OpenPgpData(fileDescriptor)
- * Writes output to given ParcelFileDescriptor
- * @param callback
- * Callback where to return results
- */
- oneway void decryptAndVerify(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
-
- /**
- * Get available key ids based on given user ids
- *
- * @param ids
- * User Ids (emails) of recipients OR key ids
- * @param allowUserInteraction
- * Enable user interaction to lookup and import unknown keys
- * @param callback
- * Callback where to return results (different type than callback in other functions!)
- */
- oneway void getKeyIds(in String[] ids, in boolean allowUserInteraction, in IOpenPgpKeyIdsCallback callback);
-
-} \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpConstants.java b/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpConstants.java
deleted file mode 100644
index b1ca1bfe6..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpConstants.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.openintents.openpgp;
-
-public class OpenPgpConstants {
-
- public static final String TAG = "OpenPgp API";
-
- public static final int REQUIRED_API_VERSION = 1;
- public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService";
-
-}
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpData.aidl b/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpData.aidl
deleted file mode 100644
index 3711e4fb4..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpData.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-// Declare OpenPgpData so AIDL can find it and knows that it implements the parcelable protocol.
-parcelable OpenPgpData; \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpData.java b/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpData.java
deleted file mode 100644
index 6615c2146..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpData.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
-import android.os.Parcelable;
-
-public class OpenPgpData implements Parcelable {
- public static final int TYPE_STRING = 0;
- public static final int TYPE_BYTE_ARRAY = 1;
- public static final int TYPE_FILE_DESCRIPTOR = 2;
- public static final int TYPE_URI = 3;
-
- int type;
-
- String string;
- byte[] bytes = new byte[0];
- ParcelFileDescriptor fileDescriptor;
- Uri uri;
-
- public int getType() {
- return type;
- }
-
- public String getString() {
- return string;
- }
-
- public byte[] getBytes() {
- return bytes;
- }
-
- public ParcelFileDescriptor getFileDescriptor() {
- return fileDescriptor;
- }
-
- public Uri getUri() {
- return uri;
- }
-
- public OpenPgpData() {
-
- }
-
- /**
- * Not a real constructor. This can be used to define requested output type.
- *
- * @param type
- */
- public OpenPgpData(int type) {
- this.type = type;
- }
-
- public OpenPgpData(String string) {
- this.string = string;
- this.type = TYPE_STRING;
- }
-
- public OpenPgpData(byte[] bytes) {
- this.bytes = bytes;
- this.type = TYPE_BYTE_ARRAY;
- }
-
- public OpenPgpData(ParcelFileDescriptor fileDescriptor) {
- this.fileDescriptor = fileDescriptor;
- this.type = TYPE_FILE_DESCRIPTOR;
- }
-
- public OpenPgpData(Uri uri) {
- this.uri = uri;
- this.type = TYPE_URI;
- }
-
- public OpenPgpData(OpenPgpData b) {
- this.string = b.string;
- this.bytes = b.bytes;
- this.fileDescriptor = b.fileDescriptor;
- this.uri = b.uri;
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(type);
- dest.writeString(string);
- dest.writeInt(bytes.length);
- dest.writeByteArray(bytes);
- dest.writeParcelable(fileDescriptor, 0);
- dest.writeParcelable(uri, 0);
- }
-
- public static final Creator<OpenPgpData> CREATOR = new Creator<OpenPgpData>() {
- public OpenPgpData createFromParcel(final Parcel source) {
- OpenPgpData vr = new OpenPgpData();
- vr.type = source.readInt();
- vr.string = source.readString();
- vr.bytes = new byte[source.readInt()];
- source.readByteArray(vr.bytes);
- vr.fileDescriptor = source.readParcelable(ParcelFileDescriptor.class.getClassLoader());
- vr.fileDescriptor = source.readParcelable(Uri.class.getClassLoader());
- return vr;
- }
-
- public OpenPgpData[] newArray(final int size) {
- return new OpenPgpData[size];
- }
- };
-
-}
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpError.aidl b/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpError.aidl
deleted file mode 100644
index 7a6bed1e6..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpError.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-// Declare OpenPgpError so AIDL can find it and knows that it implements the parcelable protocol.
-parcelable OpenPgpError; \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpHelper.java b/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpHelper.java
deleted file mode 100644
index 7305c47ce..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpHelper.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-import java.util.List;
-import java.util.regex.Pattern;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-
-public class OpenPgpHelper {
- private Context context;
-
- public static Pattern PGP_MESSAGE = Pattern.compile(
- ".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.DOTALL);
-
- public static Pattern PGP_SIGNED_MESSAGE = Pattern
- .compile(
- ".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
- Pattern.DOTALL);
-
- public OpenPgpHelper(Context context) {
- super();
- this.context = context;
- }
-
- public boolean isAvailable() {
- Intent intent = new Intent(OpenPgpConstants.SERVICE_INTENT);
- List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(intent, 0);
- if (!resInfo.isEmpty()) {
- return true;
- } else {
- return false;
- }
- }
-
-}
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpSignatureResult.aidl b/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpSignatureResult.aidl
deleted file mode 100644
index e246792d0..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpSignatureResult.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-// Declare OpenPgpSignatureResult so AIDL can find it and knows that it implements the parcelable protocol.
-parcelable OpenPgpSignatureResult; \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/sufficientlysecure/keychain/demo/AidlDemoActivity2.java b/OpenPGP-Keychain-API-Demo/src/main/java/org/sufficientlysecure/keychain/demo/AidlDemoActivity2.java
deleted file mode 100644
index b6e211955..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/sufficientlysecure/keychain/demo/AidlDemoActivity2.java
+++ /dev/null
@@ -1,168 +0,0 @@
-///*
-// * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
-// *
-// * Licensed under the Apache License, Version 2.0 (the "License");
-// * you may not use this file except in compliance with the License.
-// * You may obtain a copy of the License at
-// *
-// * http://www.apache.org/licenses/LICENSE-2.0
-// *
-// * Unless required by applicable law or agreed to in writing, software
-// * distributed under the License is distributed on an "AS IS" BASIS,
-// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// * See the License for the specific language governing permissions and
-// * limitations under the License.
-// */
-//
-//package org.sufficientlysecure.keychain.demo;
-//
-//import java.util.ArrayList;
-//import java.util.List;
-//
-//import org.sufficientlysecure.keychain.demo.R;
-//import org.sufficientlysecure.keychain.integration.KeychainData;
-//import org.sufficientlysecure.keychain.integration.KeychainIntentHelper;
-//import org.sufficientlysecure.keychain.service.IKeychainKeyService;
-//import org.sufficientlysecure.keychain.service.handler.IKeychainGetKeyringsHandler;
-//
-//import android.annotation.SuppressLint;
-//import android.app.Activity;
-//import android.app.AlertDialog;
-//import android.content.ComponentName;
-//import android.content.Context;
-//import android.content.Intent;
-//import android.content.ServiceConnection;
-//import android.os.Bundle;
-//import android.os.IBinder;
-//import android.os.RemoteException;
-//import android.util.Base64;
-//import android.view.View;
-//import android.widget.TextView;
-//
-//public class AidlDemoActivity2 extends Activity {
-// Activity mActivity;
-//
-// TextView mKeyringsTextView;
-//
-// KeychainIntentHelper mKeychainIntentHelper;
-// KeychainData mKeychainData;
-//
-// byte[] keysBytes;
-// ArrayList<String> keysStrings;
-//
-// private IKeychainKeyService service = null;
-// private ServiceConnection svcConn = new ServiceConnection() {
-// public void onServiceConnected(ComponentName className, IBinder binder) {
-// service = IKeychainKeyService.Stub.asInterface(binder);
-// }
-//
-// public void onServiceDisconnected(ComponentName className) {
-// service = null;
-// }
-// };
-//
-// @Override
-// public void onCreate(Bundle icicle) {
-// super.onCreate(icicle);
-// setContentView(R.layout.aidl_demo2);
-//
-// mActivity = this;
-//
-// mKeyringsTextView = (TextView) findViewById(R.id.aidl_demo_keyrings);
-//
-// mKeychainIntentHelper = new KeychainIntentHelper(mActivity);
-// mKeychainData = new KeychainData();
-//
-// bindService(new Intent(IKeychainKeyService.class.getName()), svcConn,
-// Context.BIND_AUTO_CREATE);
-// }
-//
-// public void getKeyringsStringsOnClick(View view) {
-// try {
-// service.getPublicKeyRings(mKeychainData.getPublicKeys(), true, getKeyringsHandler);
-// } catch (RemoteException e) {
-// exceptionImplementation(-1, e.toString());
-// }
-// }
-//
-// public void getKeyringsBytesOnClick(View view) {
-// try {
-// service.getPublicKeyRings(mKeychainData.getPublicKeys(), false, getKeyringsHandler);
-// } catch (RemoteException e) {
-// exceptionImplementation(-1, e.toString());
-// }
-// }
-//
-// @SuppressLint("NewApi")
-// private void updateView() {
-// if (keysBytes != null) {
-// mKeyringsTextView.setText(Base64.encodeToString(keysBytes, Base64.DEFAULT));
-// } else if (keysStrings != null) {
-// mKeyringsTextView.setText("");
-// for (String output : keysStrings) {
-// mKeyringsTextView.append(output);
-// }
-// }
-// }
-//
-// @Override
-// public void onDestroy() {
-// super.onDestroy();
-//
-// unbindService(svcConn);
-// }
-//
-// private void exceptionImplementation(int exceptionId, String error) {
-// AlertDialog.Builder builder = new AlertDialog.Builder(this);
-// builder.setTitle("Exception!").setMessage(error).setPositiveButton("OK", null).show();
-// }
-//
-// private final IKeychainGetKeyringsHandler.Stub getKeyringsHandler = new IKeychainGetKeyringsHandler.Stub() {
-//
-// @Override
-// public void onException(final int exceptionId, final String message) throws RemoteException {
-// runOnUiThread(new Runnable() {
-// public void run() {
-// exceptionImplementation(exceptionId, message);
-// }
-// });
-// }
-//
-// @Override
-// public void onSuccess(final byte[] outputBytes, final List<String> outputStrings)
-// throws RemoteException {
-// runOnUiThread(new Runnable() {
-// public void run() {
-// if (outputBytes != null) {
-// keysBytes = outputBytes;
-// keysStrings = null;
-// } else if (outputStrings != null) {
-// keysBytes = null;
-// keysStrings = (ArrayList<String>) outputStrings;
-// }
-// updateView();
-// }
-// });
-//
-// }
-//
-// };
-//
-// public void selectEncryptionKeysOnClick(View view) {
-// mKeychainIntentHelper.selectPublicKeys("user@example.com");
-// }
-//
-// @Override
-// protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-// // this updates the mKeychainData object to the result of the methods
-// boolean result = mKeychainIntentHelper.onActivityResult(requestCode, resultCode, data,
-// mKeychainData);
-// if (result) {
-// updateView();
-// }
-//
-// // continue with other activity results
-// super.onActivityResult(requestCode, resultCode, data);
-// }
-//
-//}
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/sufficientlysecure/keychain/demo/OpenPgpProviderActivity.java b/OpenPGP-Keychain-API-Demo/src/main/java/org/sufficientlysecure/keychain/demo/OpenPgpProviderActivity.java
deleted file mode 100644
index 4a96de5a1..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/sufficientlysecure/keychain/demo/OpenPgpProviderActivity.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.demo;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.openintents.openpgp.IOpenPgpKeyIdsCallback;
-import org.openintents.openpgp.OpenPgpData;
-import org.openintents.openpgp.OpenPgpError;
-import org.openintents.openpgp.OpenPgpServiceConnection;
-import org.openintents.openpgp.OpenPgpSignatureResult;
-import org.openintents.openpgp.IOpenPgpCallback;
-import org.openintents.openpgp.IOpenPgpService;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.EditText;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class OpenPgpProviderActivity extends Activity {
- Activity mActivity;
-
- EditText mMessage;
- EditText mCiphertext;
- EditText mEncryptUserIds;
-
- private OpenPgpServiceConnection mCryptoServiceConnection;
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setContentView(R.layout.crypto_provider_demo);
-
- mActivity = this;
-
- mMessage = (EditText) findViewById(R.id.crypto_provider_demo_message);
- mCiphertext = (EditText) findViewById(R.id.crypto_provider_demo_ciphertext);
- mEncryptUserIds = (EditText) findViewById(R.id.crypto_provider_demo_encrypt_user_id);
-
- selectCryptoProvider();
- }
-
- /**
- * Callback from remote openpgp service
- */
- final IOpenPgpKeyIdsCallback.Stub getKeysEncryptCallback = new IOpenPgpKeyIdsCallback.Stub() {
-
- @Override
- public void onSuccess(final long[] keyIds) throws RemoteException {
- Log.d(Constants.TAG, "getKeysEncryptCallback keyId " + keyIds[0]);
- mActivity.runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- // encrypt after getting key ids
- String inputStr = mMessage.getText().toString();
- OpenPgpData input = new OpenPgpData(inputStr);
-
- Log.d(Constants.TAG, "getKeysEncryptCallback inputStr " + inputStr);
-
- try {
- mCryptoServiceConnection.getService().encrypt(input,
- new OpenPgpData(OpenPgpData.TYPE_STRING), keyIds, encryptCallback);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoProviderDemo", e);
- }
- }
- });
- }
-
- @Override
- public void onError(OpenPgpError error) throws RemoteException {
- handleError(error);
- }
-
- };
-
- final IOpenPgpKeyIdsCallback.Stub getKeysSignAndEncryptCallback = new IOpenPgpKeyIdsCallback.Stub() {
-
- @Override
- public void onSuccess(final long[] keyIds) throws RemoteException {
- Log.d(Constants.TAG, "getKeysSignAndEncryptCallback keyId " + keyIds[0]);
-
- mActivity.runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- // encrypt after getting key ids
- String inputStr = mMessage.getText().toString();
- OpenPgpData input = new OpenPgpData(inputStr);
-
- try {
- mCryptoServiceConnection.getService().signAndEncrypt(input,
- new OpenPgpData(OpenPgpData.TYPE_STRING), keyIds, encryptCallback);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoProviderDemo", e);
- }
- }
- });
- }
-
- @Override
- public void onError(OpenPgpError error) throws RemoteException {
- handleError(error);
- }
-
- };
-
- final IOpenPgpCallback.Stub encryptCallback = new IOpenPgpCallback.Stub() {
-
- @Override
- public void onSuccess(final OpenPgpData output, OpenPgpSignatureResult signatureResult)
- throws RemoteException {
- Log.d(Constants.TAG, "encryptCallback");
-
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- mCiphertext.setText(output.getString());
- }
- });
- }
-
- @Override
- public void onError(OpenPgpError error) throws RemoteException {
- handleError(error);
- }
-
- };
-
- final IOpenPgpCallback.Stub decryptAndVerifyCallback = new IOpenPgpCallback.Stub() {
-
- @Override
- public void onSuccess(final OpenPgpData output, final OpenPgpSignatureResult signatureResult)
- throws RemoteException {
- Log.d(Constants.TAG, "decryptAndVerifyCallback");
-
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- mMessage.setText(output.getString());
- if (signatureResult != null) {
- Toast.makeText(OpenPgpProviderActivity.this,
- "signature result:\n" + signatureResult.toString(),
- Toast.LENGTH_LONG).show();
- }
- }
- });
-
- }
-
- @Override
- public void onError(OpenPgpError error) throws RemoteException {
- handleError(error);
- }
-
- };
-
- private void handleError(final OpenPgpError error) {
- mActivity.runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- Toast.makeText(mActivity,
- "onError id:" + error.getErrorId() + "\n\n" + error.getMessage(),
- Toast.LENGTH_LONG).show();
- Log.e(Constants.TAG, "onError getErrorId:" + error.getErrorId());
- Log.e(Constants.TAG, "onError getMessage:" + error.getMessage());
- }
- });
- }
-
- public void encryptOnClick(View view) {
- try {
- mCryptoServiceConnection.getService().getKeyIds(
- mEncryptUserIds.getText().toString().split(","), true, getKeysEncryptCallback);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoProviderDemo", e);
- }
- }
-
- public void signOnClick(View view) {
- String inputStr = mMessage.getText().toString();
- OpenPgpData input = new OpenPgpData(inputStr);
-
- try {
- mCryptoServiceConnection.getService().sign(input,
- new OpenPgpData(OpenPgpData.TYPE_STRING), encryptCallback);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoProviderDemo", e);
- }
- }
-
- public void signAndEncryptOnClick(View view) {
- try {
- mCryptoServiceConnection.getService().getKeyIds(
- mEncryptUserIds.getText().toString().split(","), true,
- getKeysSignAndEncryptCallback);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoProviderDemo", e);
- }
- }
-
- public void decryptAndVerifyOnClick(View view) {
- String inputStr = mCiphertext.getText().toString();
- OpenPgpData input = new OpenPgpData(inputStr);
-
- try {
- mCryptoServiceConnection.getService().decryptAndVerify(input,
- new OpenPgpData(OpenPgpData.TYPE_STRING), decryptAndVerifyCallback);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoProviderDemo", e);
- }
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
-
- if (mCryptoServiceConnection != null) {
- mCryptoServiceConnection.unbindFromService();
- }
- }
-
- private static class OpenPgpProviderElement {
- private String packageName;
- private String simpleName;
- private Drawable icon;
-
- public OpenPgpProviderElement(String packageName, String simpleName, Drawable icon) {
- this.packageName = packageName;
- this.simpleName = simpleName;
- this.icon = icon;
- }
-
- @Override
- public String toString() {
- return simpleName;
- }
- }
-
- private void selectCryptoProvider() {
- Intent intent = new Intent(IOpenPgpService.class.getName());
-
- final ArrayList<OpenPgpProviderElement> providerList = new ArrayList<OpenPgpProviderElement>();
-
- List<ResolveInfo> resInfo = getPackageManager().queryIntentServices(intent, 0);
- if (!resInfo.isEmpty()) {
- for (ResolveInfo resolveInfo : resInfo) {
- if (resolveInfo.serviceInfo == null)
- continue;
-
- String packageName = resolveInfo.serviceInfo.packageName;
- String simpleName = String.valueOf(resolveInfo.serviceInfo
- .loadLabel(getPackageManager()));
- Drawable icon = resolveInfo.serviceInfo.loadIcon(getPackageManager());
- providerList.add(new OpenPgpProviderElement(packageName, simpleName, icon));
- }
- }
-
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
- alert.setTitle("Select OpenPGP Provider!");
- alert.setCancelable(false);
-
- if (!providerList.isEmpty()) {
- // add "disable OpenPGP provider"
- providerList.add(0, new OpenPgpProviderElement(null, "Disable OpenPGP Provider",
- getResources().getDrawable(android.R.drawable.ic_menu_close_clear_cancel)));
-
- // Init ArrayAdapter with OpenPGP Providers
- ListAdapter adapter = new ArrayAdapter<OpenPgpProviderElement>(this,
- android.R.layout.select_dialog_item, android.R.id.text1, providerList) {
- public View getView(int position, View convertView, ViewGroup parent) {
- // User super class to create the View
- View v = super.getView(position, convertView, parent);
- TextView tv = (TextView) v.findViewById(android.R.id.text1);
-
- // Put the image on the TextView
- tv.setCompoundDrawablesWithIntrinsicBounds(providerList.get(position).icon,
- null, null, null);
-
- // Add margin between image and text (support various screen densities)
- int dp5 = (int) (5 * getResources().getDisplayMetrics().density + 0.5f);
- tv.setCompoundDrawablePadding(dp5);
-
- return v;
- }
- };
-
- alert.setSingleChoiceItems(adapter, -1, new DialogInterface.OnClickListener() {
-
- public void onClick(DialogInterface dialog, int position) {
- String packageName = providerList.get(position).packageName;
-
- if (packageName == null) {
- dialog.cancel();
- finish();
- }
-
- // bind to service
- mCryptoServiceConnection = new OpenPgpServiceConnection(
- OpenPgpProviderActivity.this, packageName);
- mCryptoServiceConnection.bindToService();
-
- dialog.dismiss();
- }
- });
- } else {
- alert.setMessage("No OpenPGP Provider installed!");
- }
-
- alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
-
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- finish();
- }
- });
-
- AlertDialog ad = alert.create();
- ad.show();
- }
-}
diff --git a/OpenPGP-Keychain-API-Demo/src/main/res/layout/aidl_demo2.xml b/OpenPGP-Keychain-API-Demo/src/main/res/layout/aidl_demo2.xml
deleted file mode 100644
index 73abd9b5c..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/res/layout/aidl_demo2.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" >
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <Button
- android:id="@+id/aidl_demo_select_encryption_key"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="selectEncryptionKeysOnClick"
- android:text="Select encryption key(s)" />
-
- <EditText
- android:id="@+id/aidl_demo_keyrings"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="keyrings output"
- android:textAppearance="@android:style/TextAppearance.Small" />
-
- <Button
- android:id="@+id/aidl_demo_get_keyrings_strings"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="getKeyringsStringsOnClick"
- android:text="getKeyrings as Strings" />
-
- <Button
- android:id="@+id/aidl_demo_get_keyrings_bytes"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="getKeyringsBytesOnClick"
- android:text="getKeyringsBytes" />
- </LinearLayout>
-
-</ScrollView> \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/res/layout/crypto_provider_demo.xml b/OpenPGP-Keychain-API-Demo/src/main/res/layout/crypto_provider_demo.xml
deleted file mode 100644
index 9f2a0e6ee..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/res/layout/crypto_provider_demo.xml
+++ /dev/null
@@ -1,83 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Encrypt UserIds (split with &apos;,&apos;)"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <EditText
- android:id="@+id/crypto_provider_demo_encrypt_user_id"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="dominik@dominikschuermann.de"
- android:textAppearance="@android:style/TextAppearance.Small" />
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Message"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <EditText
- android:id="@+id/crypto_provider_demo_message"
- android:layout_width="match_parent"
- android:layout_height="100dip"
- android:scrollHorizontally="true"
- android:scrollbars="vertical"
- android:text="message"
- android:textAppearance="@android:style/TextAppearance.Small" />
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Ciphertext"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <EditText
- android:id="@+id/crypto_provider_demo_ciphertext"
- android:layout_width="match_parent"
- android:layout_height="100dip"
- android:text="ciphertext"
- android:textAppearance="@android:style/TextAppearance.Small" />
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" >
-
- <Button
- android:id="@+id/crypto_provider_demo_encrypt"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:onClick="encryptOnClick"
- android:text="Encrypt" />
-
- <Button
- android:id="@+id/crypto_provider_demo_sign"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:onClick="signOnClick"
- android:text="Sign" />
-
- <Button
- android:id="@+id/crypto_provider_demo_encrypt_and_sign"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:onClick="signAndEncryptOnClick"
- android:text="Sign and Encrypt" />
- </LinearLayout>
-
- <Button
- android:id="@+id/crypto_provider_demo_decrypt_and_verify"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="decryptAndVerifyOnClick"
- android:text="Decrypt and Verify" />
-
-</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/res/layout/intent_demo.xml b/OpenPGP-Keychain-API-Demo/src/main/res/layout/intent_demo.xml
deleted file mode 100644
index a765343f9..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/res/layout/intent_demo.xml
+++ /dev/null
@@ -1,93 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" >
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
-
- <Button
- android:id="@+id/Button02"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="encryptOnClick"
- android:text="Encrypt" />
-
- <Button
- android:id="@+id/intent_demo_create_new_key"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="createNewKeyOnClick"
- android:text="Create new key" />
-
- <Button
- android:id="@+id/intent_demo_select_secret_key"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="selectSecretKeyOnClick"
- android:text="Select secret key" />
-
- <Button
- android:id="@+id/intent_demo_select_encryption_key"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="selectEncryptionKeysOnClick"
- android:text="Select encryption key(s)" />
-
- <EditText
- android:id="@+id/intent_demo_message"
- android:layout_width="match_parent"
- android:layout_height="150dip"
- android:text="message"
- android:textAppearance="@android:style/TextAppearance.Small" />
-
- <EditText
- android:id="@+id/intent_demo_ciphertext"
- android:layout_width="match_parent"
- android:layout_height="150dip"
- android:text="ciphertext"
- android:textAppearance="@android:style/TextAppearance.Small" />
-
- <Button
- android:id="@+id/intent_demo_encrypt"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="encryptOnClick"
- android:text="Encrypt" />
-
- <Button
- android:id="@+id/intent_demo_encrypt"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="encryptAndReturnOnClick"
- android:text="Encrypt and return result" />
-
- <Button
- android:id="@+id/intent_demo_decrypt"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="decryptOnClick"
- android:text="Decrypt" />
-
- <Button
- android:id="@+id/intent_demo_decrypt"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="decryptAndReturnOnClick"
- android:text="Decrypt and return result" />
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:text="APG Data:" />
-
- <TextView
- android:id="@+id/intent_demo_data"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:minLines="10" />
- </LinearLayout>
-
-</ScrollView> \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/res/xml/base_preference.xml b/OpenPGP-Keychain-API-Demo/src/main/res/xml/base_preference.xml
deleted file mode 100644
index 5febfad44..000000000
--- a/OpenPGP-Keychain-API-Demo/src/main/res/xml/base_preference.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
-
- <PreferenceCategory android:title="Intent" >
- <Preference
- android:key="intent_demo"
- android:title="Intent Demo" />
- </PreferenceCategory>
- <!-- <PreferenceCategory android:title="AIDL" > -->
- <!-- <Preference -->
- <!-- android:key="aidl_demo2" -->
- <!-- android:title="AIDL Demo (ACCESS_KEYS permission)" /> -->
- <!-- </PreferenceCategory> -->
- <PreferenceCategory android:title="OpenPGP Provider" >
- <org.openintents.openpgp.OpenPgpListPreference
- android:key="openpgp_provider_list"
- android:title="Select OpenPGP Provider!" />
- <Preference
- android:key="openpgp_provider_demo"
- android:title="OpenPGP Provider" />
- </PreferenceCategory>
-
-</PreferenceScreen> \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/.gitignore b/OpenPGP-Keychain-API/.gitignore
index aa8bb5760..aa8bb5760 100644
--- a/OpenPGP-Keychain-API-Demo/.gitignore
+++ b/OpenPGP-Keychain-API/.gitignore
diff --git a/OpenPGP-Keychain-API/build.gradle b/OpenPGP-Keychain-API/build.gradle
new file mode 100644
index 000000000..2e41492a3
--- /dev/null
+++ b/OpenPGP-Keychain-API/build.gradle
@@ -0,0 +1,3 @@
+task wrapper(type: Wrapper) {
+ gradleVersion = '1.10'
+} \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/build.gradle b/OpenPGP-Keychain-API/example-app/build.gradle
index 025ff417b..1f8854431 100644
--- a/OpenPGP-Keychain-API-Demo/build.gradle
+++ b/OpenPGP-Keychain-API/example-app/build.gradle
@@ -1,21 +1,19 @@
+// please leave this here, so this library builds on its own
buildscript {
repositories {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:0.8.0'
+ classpath 'com.android.tools.build:gradle:0.8.3'
}
}
apply plugin: 'android'
-repositories {
- mavenCentral()
-}
-
dependencies {
compile 'com.android.support:support-v4:19.0.1'
+ compile project(':libraries:keychain-api-library')
}
android {
@@ -23,7 +21,7 @@ android {
buildToolsVersion "19.0.1"
defaultConfig {
- minSdkVersion 8
+ minSdkVersion 9
targetSdkVersion 19
}
diff --git a/OpenPGP-Keychain-API-Demo/ic_launcher-web.png b/OpenPGP-Keychain-API/example-app/ic_launcher-web.png
index ae4cd0976..ae4cd0976 100644
--- a/OpenPGP-Keychain-API-Demo/ic_launcher-web.png
+++ b/OpenPGP-Keychain-API/example-app/ic_launcher-web.png
Binary files differ
diff --git a/OpenPGP-Keychain-API-Demo/src/main/AndroidManifest.xml b/OpenPGP-Keychain-API/example-app/src/main/AndroidManifest.xml
index 8b8c43776..7803ccd27 100644
--- a/OpenPGP-Keychain-API-Demo/src/main/AndroidManifest.xml
+++ b/OpenPGP-Keychain-API/example-app/src/main/AndroidManifest.xml
@@ -2,19 +2,19 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.sufficientlysecure.keychain.demo"
android:versionCode="2"
- android:versionName="1.1" >
+ android:versionName="1.1">
<uses-sdk
- android:minSdkVersion="8"
+ android:minSdkVersion="9"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
- android:label="OpenPGP Keychain API Demo" >
+ android:label="OpenKeychain API Demo">
<activity
android:name=".BaseActivity"
- android:label="OpenPGP Keychain API Demo" >
+ android:label="OpenKeychain API Demo">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -26,9 +26,8 @@
android:label="OpenPGP Provider"
android:windowSoftInputMode="stateHidden" />
<activity
- android:name=".AidlDemoActivity2"
- android:label="Aidl Demo (ACCESS_KEYS permission)"
- android:windowSoftInputMode="stateHidden" />
+ android:name=".IntentActivity"
+ android:label="Intents" />
</application>
</manifest> \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/sufficientlysecure/keychain/demo/BaseActivity.java b/OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/BaseActivity.java
index 5b286f208..dd642d879 100644
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/sufficientlysecure/keychain/demo/BaseActivity.java
+++ b/OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/BaseActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
+ * 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.
@@ -16,9 +16,6 @@
package org.sufficientlysecure.keychain.demo;
-import org.sufficientlysecure.keychain.demo.R;
-
-import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.preference.Preference;
@@ -27,13 +24,8 @@ import android.preference.PreferenceActivity;
import android.widget.Toast;
public class BaseActivity extends PreferenceActivity {
- private Activity mActivity;
-
private Preference mIntentDemo;
- private Preference mContentProviderDemo;
private Preference mCryptoProvider;
- private Preference mAidlDemo;
- private Preference mAidlDemo2;
/**
* Called when the activity is first created.
@@ -42,23 +34,17 @@ public class BaseActivity extends PreferenceActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mActivity = this;
-
// load preferences from xml
addPreferencesFromResource(R.xml.base_preference);
// find preferences
mIntentDemo = (Preference) findPreference("intent_demo");
- mContentProviderDemo = (Preference) findPreference("content_provider_demo");
mCryptoProvider = (Preference) findPreference("openpgp_provider_demo");
- mAidlDemo = (Preference) findPreference("aidl_demo");
- mAidlDemo2 = (Preference) findPreference("aidl_demo2");
mIntentDemo.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
- // startActivity(new Intent(mActivity, IntentDemoActivity.class));
- Toast.makeText(BaseActivity.this, "Not implemented!", Toast.LENGTH_LONG).show();
+ startActivity(new Intent(BaseActivity.this, IntentActivity.class));
return false;
}
@@ -67,21 +53,11 @@ public class BaseActivity extends PreferenceActivity {
mCryptoProvider.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
- startActivity(new Intent(mActivity, OpenPgpProviderActivity.class));
+ startActivity(new Intent(BaseActivity.this, OpenPgpProviderActivity.class));
return false;
}
});
-
- // mAidlDemo2.setOnPreferenceClickListener(new OnPreferenceClickListener() {
- // @Override
- // public boolean onPreferenceClick(Preference preference) {
- // startActivity(new Intent(mActivity, AidlDemoActivity2.class));
- //
- // return false;
- // }
- // });
-
}
}
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/sufficientlysecure/keychain/demo/Constants.java b/OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/Constants.java
index 03e76254c..acff12f47 100644
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/sufficientlysecure/keychain/demo/Constants.java
+++ b/OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/Constants.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2013-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.
@@ -17,9 +17,5 @@
package org.sufficientlysecure.keychain.demo;
public final class Constants {
-
- public static final boolean DEBUG = BuildConfig.DEBUG;
-
public static final String TAG = "Keychain";
-
}
diff --git a/OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/IntentActivity.java b/OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/IntentActivity.java
new file mode 100644
index 000000000..fdcabaf7c
--- /dev/null
+++ b/OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/IntentActivity.java
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.demo;
+
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceActivity;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.widget.Toast;
+
+import org.sufficientlysecure.keychain.api.OpenKeychainIntents;
+
+import java.io.UnsupportedEncodingException;
+
+public class IntentActivity extends PreferenceActivity {
+ private Preference mEncrypt;
+ private Preference mEncryptUri;
+ private Preference mDecrypt;
+ private Preference mImportKey;
+ private Preference mImportKeyFromKeyserver;
+ private Preference mImportKeyFromQrCode;
+ private Preference mOpenpgp4fpr;
+
+ private static final int REQUEST_CODE_SELECT_PHOTO = 100;
+
+ /**
+ * Called when the activity is first created.
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // load preferences from xml
+ addPreferencesFromResource(R.xml.intent_preference);
+
+ // find preferences
+ mEncrypt = (Preference) findPreference("ENCRYPT");
+ mEncryptUri = (Preference) findPreference("ENCRYPT_URI");
+ mDecrypt = (Preference) findPreference("DECRYPT");
+ mImportKey = (Preference) findPreference("IMPORT_KEY");
+ mImportKeyFromKeyserver = (Preference) findPreference("IMPORT_KEY_FROM_KEYSERVER");
+ mImportKeyFromQrCode = (Preference) findPreference("IMPORT_KEY_FROM_QR_CODE");
+ mOpenpgp4fpr = (Preference) findPreference("openpgp4fpr");
+
+
+ mEncrypt.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ try {
+ Intent intent = new Intent(OpenKeychainIntents.ENCRYPT);
+ intent.putExtra(OpenKeychainIntents.ENCRYPT_EXTRA_TEXT, "Hello world!");
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
+ }
+
+ return false;
+ }
+ });
+
+ mEncryptUri.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
+ photoPickerIntent.setType("image/*");
+ startActivityForResult(photoPickerIntent, REQUEST_CODE_SELECT_PHOTO);
+
+ return false;
+ }
+ });
+
+ mDecrypt.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ try {
+ Intent intent = new Intent(OpenKeychainIntents.DECRYPT);
+ intent.putExtra(OpenKeychainIntents.DECRYPT_EXTRA_TEXT, TEST_SIGNED_MESSAGE);
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
+ }
+
+ return false;
+ }
+ });
+
+ mImportKey.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ try {
+ Intent intent = new Intent(OpenKeychainIntents.IMPORT_KEY);
+ byte[] pubkey = null;
+ try {
+ pubkey = TEST_PUBKEY.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ intent.putExtra(OpenKeychainIntents.IMPORT_KEY_EXTRA_KEY_BYTES, pubkey);
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
+ }
+
+ return false;
+ }
+ });
+
+ mImportKeyFromKeyserver.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ try {
+ Intent intent = new Intent(OpenKeychainIntents.IMPORT_KEY_FROM_KEYSERVER);
+ intent.putExtra(OpenKeychainIntents.IMPORT_KEY_FROM_KEYSERVER_QUERY, "Richard Stallman");
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
+ }
+
+ return false;
+ }
+ });
+
+ mImportKeyFromQrCode.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ try {
+ Intent intent = new Intent(OpenKeychainIntents.IMPORT_KEY_FROM_QR_CODE);
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
+ }
+
+ return false;
+ }
+ });
+
+ mOpenpgp4fpr.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ try {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse("openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282"));
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
+ }
+
+ return false;
+ }
+ });
+
+
+ }
+
+ protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) {
+ super.onActivityResult(requestCode, resultCode, imageReturnedIntent);
+
+ switch (requestCode) {
+ case REQUEST_CODE_SELECT_PHOTO:
+ if (resultCode == RESULT_OK) {
+ Uri selectedImage = imageReturnedIntent.getData();
+
+ String[] filePathColumn = {MediaStore.Images.Media.DATA};
+
+ Cursor cursor = getContentResolver().query(
+ selectedImage, filePathColumn, null, null, null);
+ cursor.moveToFirst();
+
+ int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
+ String filePath = cursor.getString(columnIndex);
+ cursor.close();
+
+ // TODO: after fixing DECRYPT, we could use Uri selectedImage directly
+ Log.d(Constants.TAG, "filePath: " + filePath);
+
+ try {
+ Intent intent = new Intent(OpenKeychainIntents.ENCRYPT);
+ Uri dataUri = Uri.parse("file://" + filePath);
+ Log.d(Constants.TAG, "Uri: " + dataUri);
+ intent.setData(dataUri);
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+ }
+
+ private static final String TEST_SIGNED_MESSAGE = "-----BEGIN PGP SIGNED MESSAGE-----\n" +
+ "Hash: SHA1\n" +
+ "\n" +
+ "Hello world!\n" +
+ "-----BEGIN PGP SIGNATURE-----\n" +
+ "Version: GnuPG v1.4.12 (GNU/Linux)\n" +
+ "Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/\n" +
+ "\n" +
+ "iQEcBAEBAgAGBQJS/7vTAAoJEHGMBwEAASKCkGYH/2jBLzamVyqd61jrjMQM0jUv\n" +
+ "MkDcPUxPrYH3wZOO0HcgdBQEo66GZEC2ATmo8izJUMk35Q5jas99k0ac9pXhPUPE\n" +
+ "5qDXdQS10S07R6J0SeDYFWDSyrSiDTCZpFkVu3JGP/3S0SkMYXPzfYlh8Ciuxu7i\n" +
+ "FR5dmIiz3VQaBgTBSCBFEomNFM5ypynBJqKIzIty8v0NbV72Rtg6Xg76YqWQ/6MC\n" +
+ "/MlT3y3++HhfpEmLf5WLEXljbuZ4SfCybgYXG9gBzhJu3+gmBoSicdYTZDHSxBBR\n" +
+ "BwI+ueLbhgRz+gU+WJFE7xNw35xKtBp1C4PR0iKI8rZCSHLjsRVzor7iwDaR51M=\n" +
+ "=3Ydc\n" +
+ "-----END PGP SIGNATURE-----";
+
+ private static final String TEST_PUBKEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
+ "Version: GnuPG v1.4.12 (GNU/Linux)\n" +
+ "\n" +
+ "mQENBEuG/qABCACZeLHynGVXn7Ou6MKE2GzSTGPWGrA86uHwHPUbbTUR7tYTUWeA\n" +
+ "Ur+l+lR3GRTbcQY4ColGUcDcTVlW/cp9jhHnbbSIS0uJvW+4yu3I5eSIIoI09PLY\n" +
+ "KjT0U5l2z6t6daL7qWfZ1pQkCuCMe43eMLBPvyao1+zEd1zESbMz/bySZRlYMKAC\n" +
+ "aD9pGnFHS+EOU+lQXxfzCpKEQcHmPrrBFh2Gr2JFWWjZArKh7B1lQLekD2KS8aFb\n" +
+ "Lg1WGo5tK1sSk6MnMmqs1zNw1n15p5UDnJ7Qh8ecfMyDLy/ZyUjfFjy4BE0p+4mS\n" +
+ "J5iDU0pTYK3BpdfujY6NE+S2Ca2J6QoNRN8XABEBAAG0MURvbWluaWsgU2Now7xy\n" +
+ "bWFubiA8ZG9taW5pa0Bkb21pbmlrc2NodWVybWFubi5kZT6JAT8EEwECACkCGyMF\n" +
+ "CQlmAYAGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAUCS4b/ZQIZAQAKCRBxjAcBAAEi\n" +
+ "gjHWB/9w+D8DOxOGeUzNxfGn98C1nYVkt8zNcTnODBd8VsaPx1pKOXUu6IfqaTxa\n" +
+ "qS4hsAmgV9l0xLA2CkRndZAangsl3ZwURh8UiX/uqJRA9c9py7O+8GxpARtwtOPQ\n" +
+ "VxXx/O8vkXxFpYsFzpotN5XlGkLWWVySotKbTcSfaBifcS3oFT+d6VAZ/D68iTaH\n" +
+ "YBRfwaganevuqUsrJQiCOX11d6Lnr5cDzvmR2yagsLZPUi3CI02bVZYH99uNAr8b\n" +
+ "O7OkrgbcN7U5VlMuXdzj5fU43QpAzrT11JY9jmYsxbJ3t0Zgb5tnGXq9UBkPgGKC\n" +
+ "T01QW7aKHBN5YeDtOY1d0DQMNLkKiQEcBBMBAgAGBQJL/m2NAAoJENjTIPvvLEnw\n" +
+ "9TsIAIEV6UjXLSfxOggm7/0pG43P6OP1HvItUCg/wPfLexHGKJQAh2SuotUNNq7h\n" +
+ "i67PajHBgS/iZNrT868sDWGN5FT2toFOsY3YMTwv1GpsZGTSB7my+ej0Lq5OxCkD\n" +
+ "1rGsOLhetFzIqRuybmLRgAWxj9tg8ib2bkpI1S+67TRtrDkbKUdVrEvQp4rhItNl\n" +
+ "L17mxdViOkmboNgdTxlfcjyRh96dkAMgzKL/LkwKsgFxYYAOOKGodg1pOGdeTku6\n" +
+ "c0h5UmTY3DwLiY4FeAIzx3L8TYru3wx+if3j1MuVKRt3p51fX5/4dmyfWbrSRPO4\n" +
+ "8fh7RM5JMWtcI9DKLoFIhydppl+IRgQTEQIABgUCTFF5iQAKCRBlHG1tQKlVAhQN\n" +
+ "AKDTCAl9IVdBuuu7NEi3LOKKi/8ekgCeMG5UX1a5YrH7E7n8AqOk9efihuKJARwE\n" +
+ "EwECAAYFAkxReagACgkQVhWX/ckAx3iKEgf+PinRsmf6sGSM7HMZigJPGQuw81jM\n" +
+ "LPC0P3qc9wHpqCpoGcKzZO0wKKKdQc6uKwJUMLKyJeK7BwwJz+r75KWHz8zeUAoY\n" +
+ "Iyqf5ukM26tADxr+oDpOJegHkvucAdKQjKDu04cXBkILRJ3lnytpqE6tiqS1v1Jx\n" +
+ "SvgdtdC6WHHgpwJPRqtZz4KPllQ40SyrNhzgZ4V8qFNrt1jhO+6Z1rgyTEDwUwxM\n" +
+ "VCUz8fIQX9Ic6il4XfwKKt4kA+kiQC8Chs6hSkUERyNZwNAkToUnhwfcULpWnDnz\n" +
+ "q0mKBAhLe++KaigVwzXMFVGoy/YaYR35dxLkSlJEZC2CP962vO4nfkvrFohGBBAR\n" +
+ "AgAGBQJMpHMVAAoJEOQ/9ZAjofikEHYAoJz13rWjPl7S1VCSLO48qPq6DEN3AKC7\n" +
+ "HX/YUbDR3iskXSRDICKUwK8P54hKBBARAgAKBQJNnEjdAwUBeAAKCRDFl97MF3r9\n" +
+ "fEVRAJ9AobzDC3824/Flhk4aomgwFuIJ7QCfbyXutcVsiFk3GJNa4jSThR9/4yuJ\n" +
+ "AhwEEAECAAYFAlEFpa8ACgkQskE8Zt0sP+rkyA/+M2E53FXWzjlRttQQsfY9kH+/\n" +
+ "nQ6x7HAEsa11rysB1seM4JJGXsvyp8e2KHgxAgwcOEbWt56NB5Vlx2qms11iEC5l\n" +
+ "JrqTajWmF7sWcHxFaC8RVm+A0HHK0qWGga6T8E15izsKUM2esDJloWoYqA9Ddfdw\n" +
+ "B0JXH3F8u+2OmWhgU08jIyDt2iM7HMCFssben3VlXribsCP0etOeHFITWyZXOCKZ\n" +
+ "A/zotpLd9LdT1Z9fKFVlgdQbt9PNyxKraUuyG7TAYgU2tZMuDZ0y+FKM/MfsdWHL\n" +
+ "9jBbzPcWYgh/GBkHAEpmuuX5KBVEJAQ+zCv+lBuHGZ0srLH/YZZNE8fSc99kC97i\n" +
+ "IlU2Rj5IkiDySxPeEx3LWDzINV/vPejJ6XoWb6LuV/8PzRIWOdutaEdyjcFwQo0K\n" +
+ "Ht9B3oETz5idGH200aYHJy3Ex3/vvX0xAyUL3naW2ipZH2CLivuD7H4LIdqC2ukh\n" +
+ "BxKB0gufCGqc2YBB54jYU9fvCPGJJ7NYqymYKTBf0DKHe3zVCRkdxQYyLkWCFP3m\n" +
+ "izpxSebbhdfdLbCSssRDA1e4nu8EASDuxDaaz43b+56KZ3Ca+/Q+zQopgaHNHuSy\n" +
+ "7CIo9qO6okjCiLO7FkeeJxmD4GAGi9hSHGiW3hR/h3qv3XHjExeys7AFSVRyOXpo\n" +
+ "DrGwnadlYBkAh8tMbYWJAhwEEAECAAYFAlEFzk4ACgkQ0DssUbNe6zwDuhAAjhwP\n" +
+ "HGUwvS9YsF0+D8Vp8OSYh1Q0/S7iSSVLT1ekilpbOdCIXXETNz1Mphe/7LkRpSmn\n" +
+ "Y6AjHQexvDHJB8VTGO06LJcpaNl+FRB5z7rmmpRSR6zl6mxuNo6UmM8UKxoFnXvk\n" +
+ "Ta23uBo1Q3CylrX5B/6UrhGS+0WiJE/cbfAKWTvOQqD2s7j0AMeJgn/qcInziHik\n" +
+ "W+0PWuHWjUCpy/BPNWDIsLn5ueYoXSmgUeGuWmI9VnyC6PdjWzxE+2QzYEpBANYQ\n" +
+ "Yd09QaBvhQA8/p6FBV4Ofa1ZdMcaGCPA+QSA355a7+uRElzSHa3tN3vThhdwKOjO\n" +
+ "d8l8baYq2RcXVELU3fjDwp6/L00ldSyV0HHlbBLZZ8RGpB76e7LEMqE14DzRkyyp\n" +
+ "r0m1SkDzSYWcTIFuxGnZ1t2/FUe8Vt2WW8UwlKiHSo3VDQzLxvlGpkmV9uV4Z4FE\n" +
+ "+LMFKSZfIa4qG0LUQeaJ8OZ8l1yZgZPkwtuGI2qKh7XFchfB79PIB2QUtMlAzoZf\n" +
+ "CxPF8ifQ3hGtHaKI83LGaQpTHEkzPZYHzOEskSt5oGIPg90wq9c/bbN8nPuwmg9h\n" +
+ "Lzsf5+V5ZZuJcSOQd8Rbx4Juj5NI72jvH0BY7XtVSWL6itJsqbvU8YzCbEcOMAQJ\n" +
+ "jxuvTAQ3BnkOrETgH66TRjFPodTikZOG6hUbLaqJARwEEAECAAYFAlEGSowACgkQ\n" +
+ "Zf77hz/aPZlvmQf8CJwn0S5l7fF1MMgFDs1gVYQLxNPDp+w+ijdiQy9AbHL0eeSA\n" +
+ "5wFAPbMKNke7DiaGlXuJZkGjYE/gelVdiauno60R8sO/V8X0FXQNSb/XLaPVykzF\n" +
+ "ajQBwcvCEGsyNIuYRIQpuKS9eROx+eZS7/Ez4bao6rOEIGWiormSjkUybkxnzXIR\n" +
+ "i2fKUGrFaxSmRFmG3WiozudD5lbY/HD8d8ofZQrhbWGYRsG30VzZk0XY+CkofwYd\n" +
+ "MEoooBc5N6vtskHLkl8Z91laNphC6pk4QQ9EOPfoxE9t9ZyT2wiF6YkysXMOUeJB\n" +
+ "K6BD8Aim7HarCZU74C1permfnE43CKoSEk0HzokCHAQQAQIABgUCUQZgWwAKCRBi\n" +
+ "udz/ZiTBorLyD/9J7Ub2sogWskFA8SPEw5SaCyAgxpNzb7ykJ6Tb1z8zq29be/zS\n" +
+ "BsRWgcQWtOXPc55uhSY0Jwaw07ejT3/fQIDLZCWvXmPBgTh6OLdTJYZWNimBHDp5\n" +
+ "8H35ZBdvEtha6zCGkA20c/F3dnrz56dPFeI8c4hHu0LBOzqZYgoRHFh85fAnHTHc\n" +
+ "TfdvZ/obNeV0NhyKJZQgZepKdAN2Z9cXIHkfdjLeGuA6CEJlVBMk96BvozNqm/4X\n" +
+ "KC+NGxbp5J+yARb80gT56/OGaB08WotRUcFRub6fc7gT7QmsXrx8YWYaBIWlhW/U\n" +
+ "/yliJdZeMawKc7tgLkpf6qM1GerTgzkf44r3rXHl+ImlizdQhFhUGJFbkisiqYzU\n" +
+ "caMmNkPWNg28e6dcUU7nfprt99IbSSdhPAxPd6B6bzUawwV/VnpNXIrH+048lgsh\n" +
+ "FO45JceUfSadpK2p2UXVUP/TfYYY2xBwvoCjAsD4Q9YYheJsWh7i3xi9/0EzfSPB\n" +
+ "1NpLdaujAO8CuTTZ4NlpTLOZFmksQSF4BSpcPC+8TUGwAKXIm2wwIUNm1sBwXNsZ\n" +
+ "aOtAz3vQH3pKTzvXQEoCMCqoqkYS+uTGilwhmlSivfenB7DT3TX8DHnUzT1eNlh/\n" +
+ "PfCmdufvGS4BodAYPgTfmq2B7bpohFOXzjwiIJ4AJfaYhdyQ/PqN9JLShokCHAQQ\n" +
+ "AQIABgUCUQZr1QAKCRDIIhUzoaoywpfcD/wJveyGF8c24F6O5O2LX7yWmJ9LWs9t\n" +
+ "9z7c0gMom/eQMyoFh2VWHnBhNfR635RtSWFasMJSEpPd0iyIz95eaXpsjdn408jC\n" +
+ "8yGZD8N9EMkSbtGrcMExoyH+Tobx2Xs0mBDrG0TZdK07dW3q75asNFtU52isxC60\n" +
+ "WTJKUJud5vfms0cnp+sRCewvwssOyaIYzqg9J4/GZIpY6d9Roi9u+7FIUnzQJN4d\n" +
+ "zsDxBOsKZAVnyDaaaDMWXEV15BVwwFCap58e5HzL/NKK2uJMyMwBpPjZM4CYiWL+\n" +
+ "DVNvedids4iuNezyYKJns+LSo7zl/Rv1IQKPhg6BRbxgKu+DZt7iYI0vEl7PJNIM\n" +
+ "Ex77BZI/DUVZWXlE9g1bB4sP3iMBq1998VvWsTbA6CLRMYQvN5GWzlPyCICx2N9V\n" +
+ "dRD8Y+ySBtXMSLTe/kMXvnpMvYl5Yree/mSRirqA5Z/sZtw+SpTdtXEgoPpuCouc\n" +
+ "KKvxWkyDZX3Ehre/wDsOFN9XZfEp79SYym27rqMr6QPx6UQOPYd05soqM/OHBP89\n" +
+ "OCQqcbKkdcmv+bvGHdrlMjSfQGf3sBHy2TdL0LzzDOiGhoMU4Q4QGkrVYZwzpLDv\n" +
+ "InPbtO9GvAiJeZpgZyyvUHwGrmJDlpO3QPBMJj1AxOc06mJmSwg92ziIkK+jX8s9\n" +
+ "xlxcKmFXxTFUVokBHAQTAQIABgUCUQbiuAAKCRCylLwGx1Uqeo/dCACjY9xvAup2\n" +
+ "MCcs2nvHtKCy3NVmzm9Qsc5hferWJ8xPqUEi+OrpeyknWQWMQUlwCTRX/5I2HLh4\n" +
+ "PI4ycieWGiNh1FLbAcTW+xqkjNfE1iAmD3h/c2wBqlsMIdTnPNKFD1zhRAjixn58\n" +
+ "so1uW5+sTmPs6VVU9Ll6hcr+LzsUoS9t/sHOD53KYG3JKeCKLMzG95Ev8yJvxYZg\n" +
+ "DC2NZZXEeQq8XM59gpBoGrTx0xmWIFio6w3XIYHlhwcvNormrpbaZDxq093qy2hx\n" +
+ "AWVVSb+Rby/Vp4AsJyoQ6p0DzWbxj97o+rFV/Av0pgEuKnhpnpyB5mDvhwPGvWwp\n" +
+ "xGi79eZYSN8qiQEcBBABAgAGBQJRCCrMAAoJEDVPpeI+qihik/EIAJXinMI3lDhV\n" +
+ "KbhE6PYQLwfd8OBrV8fX4/vQbzosjCQBwiZFot1LO4aRAZgLcAwMoxDGQco2h+bf\n" +
+ "UWskvhMGCC7rqvDkmoalGfQ+IwKnnHeZAghM1/Dd/C9ijl+2LQeeNlcaaWsMTjcV\n" +
+ "q3cZtPInLJfJpci8nhDET4dHGl8tai80Oen30Wd19nDDaeL4qeF3E7YaPIwcg7jR\n" +
+ "PF2fYQBIfh7+1Y4tbhAqyLRgFx1aB+nqgVbsLtRXEK4OTPeigN6cEawot4XRC+nR\n" +
+ "Qtp2ZqDTzOF5KH9tG9+S5cQZsTUIFtWevBxrIg8kimIt6sOxt1wkeipb4QPBbkjS\n" +
+ "+6zkLMhO8I+IRgQQEQIABgUCUQq1+AAKCRAEtb81V3CDSkjdAJ9Hq4iFYWxNRpJc\n" +
+ "Hqv2AH1F4yWtjQCgvGQK6MsOuO9QBcqFJmVBHPUPWk2JAhwEEwEIAAYFAlEMbTQA\n" +
+ "CgkQrpk18w7U8BBHfg//be7uVhY/hE04S4NrR6IG/k+9gMQHmH6OAylKWvfd8CuV\n" +
+ "npd3ZmZGosWAxRaizaET8OPATP+Wojbuved0/dFL2cras/+GKWleuWI3qxFAAqSx\n" +
+ "SGDilpladdQZbyHfrAHWFwpwEl51wsc9LkcAd9lywv0wbPzV4oFqEqbPM+wNW5jS\n" +
+ "N1W5doj2A3MUw17ocRtk2XmzhF819w++t16Alweg0QrfEx5mwli/Z17FcYUC21Wc\n" +
+ "04eDL8s/j3U3SYjBvzNIrtx2JiS/MdtewjvAWAeEoFusNcwbYn8J+2qiTrh3twR3\n" +
+ "AU7xU1s2a3GxjjQ+J/HXr52Eujd4nk/V8Au+NYAWlCoR99ByVfzG7tsjaGPysHb7\n" +
+ "KkGVIAlXM0brKwQvRhvGsN0+8mjfM+xl58AVV+w/K2MUTyWHyAMigvXtK0lBGcHb\n" +
+ "YATLBD98dUTkeF49oHAFriw2fLqes4ayqqouiLj7RfNHDQ4X41PkxlaSq8GrXHig\n" +
+ "jUKKod//taTViZU2JYL9ZIAGzDaV483pVZpQlBqeWBaaHTlk/fBrFfIxQ8gxrAX+\n" +
+ "Izano++z5tFPgKPBFisDcLt6g5x53ADh3Ax94a0sR8aoBzeJYlvRLG2OLuzok059\n" +
+ "CcvAG/lCYJTpz3LSsWdV2VqDk9LLqh+uzHmA1SVvYyxHz6IEYLGxNhEOPqC4JDSJ\n" +
+ "ARwEEAECAAYFAlEZK/kACgkQ6kRcQQePqI3KNQf/cAik9KuU5q2LRzagJLpVqIGg\n" +
+ "f7Yu57EQ5yENFbL8BJoVn/CMXsx8btDeouGkUXYVDtn5ThrGOHAs2OYEQSF3HSFp\n" +
+ "xqUci5rVLBoYwq8WcbGihg/YA3T1m9T+hGx7uhvDQOUDxjggcwxuTaHGmbIoDHaw\n" +
+ "BtlS2iznyku43ip0yazqrz79CPTJ7DrGe25q6ApVXhZeZ0Tmj2qa/ZB3nwqW5mov\n" +
+ "0UqIVBoyO4WIP/rMGdK7e1HUu5vpsaeAJKBdU0FMADuDc+vVuQildwIejSxW6etZ\n" +
+ "fHiKX05gZjN5KHfrjxaCr0Tv0xBmJ8QhFe7+4qZlfEEswUGk3gXUK5nkatEn+YhG\n" +
+ "BBMRAgAGBQJReqVtAAoJECuuPPba/7AA2rgAnjbHP4UG4AU4DjSjIK+gAwN9Ekxz\n" +
+ "AJ9/LSzNx/UzZYyw2qXMOdzXODTX67QqRG9taW5payBTY2jDvHJtYW5uIDxkLnNj\n" +
+ "aHVlcm1hbm5AdHUtYnMuZGU+iQE8BBMBAgAmAhsjBQkJZgGABgsJCAcDAgQVAggD\n" +
+ "BBYCAwECHgECF4AFAkuG/2UACgkQcYwHAQABIoLM2gf+Kzd0jobczAyFcvK8Tpu2\n" +
+ "ica+3Cd9ZL9PmzCHKO5S7mAgzKGuUSl7/IUG3vuu+ijfVg6ujsSW9QjKptFZAU3C\n" +
+ "/r0LL2+wUFjGscVCkE3ovFEZ+jOWUDUVQoKFN0h7ue1AkYtilYUNwHcKENxeqLAb\n" +
+ "+nAl6U43eRUVe6IbHBtQcdszZ81C6R6Wm5qGCTRaZVWWhW4iRTxw4XMvZ5jeXO6U\n" +
+ "2h7WOiqlAv0QJr7xARVp0k1qMGSKVMaoqvHj0oai4VeBBDMuYYjfHMo9Yo8beKPY\n" +
+ "pmIAy96y7oE/Lb/WYkPcoZ+tmPVg8ZwvlZTTAbrEUlV1xBEjs8/3ldB/qn3Vf8q1\n" +
+ "6YkBHAQTAQIABgUCS/5tjQAKCRDY0yD77yxJ8IIPB/9BcmtqFesUgLavyEGTCQu7\n" +
+ "9DGDcn4oAQNnBxrIO4Am2jfnEwGt0b+9kIl06PG2zNMxhA8NIFxc6XGnfvqr3BkZ\n" +
+ "gCN9dgNPSXQ4XazESylJk7F3h5yozAel2ZLy4lY04Sy63n/3J48coZaLSPLoUDq0\n" +
+ "2gudqQBTG+sLs69PLTrwYdp4kZpJunmenpgcGSxqpaf0Dvo2Fvq4ftRre4pjaNzQ\n" +
+ "9ZXsWJbC16boJd7Hbo/7oZNNMvZC2XU3PxhiEPhwGP6H/Sjv53MtGNp3/Hcjef1G\n" +
+ "TFQsN79m4FHBB+VnRa7wieZXa3cQWy2RamxuVW15fiaZvAs3pKzvdDwSrTFuVWqv\n" +
+ "iEYEExECAAYFAkxReYkACgkQZRxtbUCpVQJM8QCfWIZL9tcmOuVe1hHq6la4GBWI\n" +
+ "QFEAoMdagHqMu/YZ+jeffnD0XzojV5Q+iQEcBBMBAgAGBQJMUXmoAAoJEFYVl/3J\n" +
+ "AMd4cicIALpzq7i2P29c5C0a+cvJsVTGJWA3tQyi+BpCMtIwneqWH/ojsh0vM/KK\n" +
+ "e6jrUAmQ8kQkLGHbMpDTlvVyhGw5kO6WSlIDKAx8TmzmMa/wuCBR8g8zi27fx06C\n" +
+ "RQ7NcDJy4AmU2cKzK7rKpPkLTBHf3zNbUoISJW+icf2tfMBjsJ5tS6o54+f/zhnf\n" +
+ "QM+S9IdRgfH2by59J11H+Oykiy0I77jMNXO04LbMfp/ZqJE1Cwa1piygNodBeWfm\n" +
+ "mlB4WzCiplKJDqVCK6pQHjAnv7f5O3O1MH7w5FTjE/AYSeZ1AZtHbjv8QeXLbRuf\n" +
+ "S3amF3w3yjZIpLSmp8DD3umJe2lpWaOIRgQQEQIABgUCTKRzFQAKCRDkP/WQI6H4\n" +
+ "pIrtAJ9Ri2cWkWnJbvgYjCWF0mNCV2Zx4QCeJBeudrxAYqwTmU5clrFGb8kiuSiI\n" +
+ "SgQQEQIACgUCTZxI4gMFAXgACgkQxZfezBd6/XwgQACbBOBJSdnAeuJHufGi7ETZ\n" +
+ "cwRYGxcAn1B3NirdNJ8dJ6bVV1qEKoJZgpzUiQIcBBABAgAGBQJRBc5OAAoJENA7\n" +
+ "LFGzXus8gCgP/jfftQRpM/PyGwW4O7w9lDf3EyWshqnoO4MGNEy1wX4TW48vpOZs\n" +
+ "KMR1/e1r7hTIC0HXQIfUWGSWd2h+FJIVO36sGXqwgJnopOe1S/3W7MsWa8zfkZEz\n" +
+ "fNXWmK8PUNIGc5hOFxfbAQk+4ZpRtu6nqnlZ4bqP9tDyZ3673jkbth2W9PqNifE7\n" +
+ "wzKOYUIW/cWkyIh3HZLLKpLXu6o5p8P3nIP2IuznybPqNFMfhaFghYT7bWWpixLE\n" +
+ "K2svy7tGKvhJAxfnGvtEYDzjhyh6L2Cmm0X+c/4HcLLlCdErE4tU0SjQSaf+HDce\n" +
+ "+WmwpHL2q+EX8Yd2C2rM2vm5wMVZPiH5GL9Q1O+B+xkmmD7I2TEA3B7ZGw4pZpBz\n" +
+ "X0U6QhRIM+ojMfFYkE03+S8kkhQdFjtPDEIJXAIkCZ/bROa1ByBuVdLm0TQAN1Hg\n" +
+ "m8A39kylJ9kHXPiuQAYggbh0ynx8PYv3w+IxDg2lSnjz/pUrOBmGJ3Hw1MZU/9mj\n" +
+ "riwmxOGg2LqAQ1uJN51pDFV9TGE6WoGi7cob3F1zLrzMZC75C+WpzWlur/gA7vIJ\n" +
+ "Y9NSjND+FOIo8F/oQJO/PyfC1bEI5X/ofQ9yNTKBa9xHIxx+lgiCrDVlbD/pQQBT\n" +
+ "9TyvhT2qjSdM9ipN19c8Mpc3pJrrs+RY96r4u4tZC/AO3+2nW7kWpmqiiQEcBBAB\n" +
+ "AgAGBQJRBkqSAAoJEGX++4c/2j2ZTAQIAKXOo0o4XUzPrMKRBBj3iGTGFZ1ABZ+1\n" +
+ "Zs99t+I9Ksy2LmPsQ96CwK2AzqfbcOlZ9+eMCzYhfX9alvJ7Ms5CTkKj9xo9wDcg\n" +
+ "/fzqG+xVlt3oXeLMc1juW8nLLKhLBn4vELmjh4JuvkjEaMaGwZCbeAQKFyXtZQOq\n" +
+ "YxcKnq8Fe7xW+rHdB9F1m4uCKRW2L9IglUDlOiflUTvCt/3blea936mzsDPhoQJO\n" +
+ "O+zGCF0NXbvJ4lzQmgyWpvd81mbN/DQ055UQjG1DNNS6q7O/EqNRy5Jnv1/qSCAF\n" +
+ "+afVQgrDvrvcAuQRUfw3i66HXNGEm++43C6K+0fkPteh8ESx+H1WSgeJAhwEEAEC\n" +
+ "AAYFAlEGYFsACgkQYrnc/2YkwaIpgA/9HKKyfO5oJpV3bUXf0IZGTgrVISiVY90t\n" +
+ "IbX0qkyGVFwEovp8eRJ0B6cluQiNypjKY+5xh0wYbexk2El53dNDkTfisfVM5ib7\n" +
+ "a7aMAMQ6R99zFVtag/eXmAWzKcL8x3RdVyRFSttwrGwDlrv8VpQByYYSnUPWvzZs\n" +
+ "YJQL+XgHqVLzK16/oZ5rzBvzbIH0oFm7HoGqKsRGDEL2hkNRhjuDlxrzijSqQfqY\n" +
+ "qhMqQAjf6fenpVFFPXr8TY4RXRRcBFq1aP3Xp2Vq1ekwgzHTokryWEyZTdsVXoMU\n" +
+ "Tmjk/sZ4R61N5YW3EEyGuG2E9wgZD8FmUElJTAduZPH16JcfO4KUXsNSXap8yKJ6\n" +
+ "yZ47TvbNcwQq7IhxbwimhaR4pbBpcOfQEpdHA24csOBPJ5Ly02LpAs0ZuhmOvDLW\n" +
+ "Yxxr9++Sm+5UBcAMav+N69f3lUnIc/MhDI0uYLe762z7cs5opIx8Fr64GZn30SY9\n" +
+ "OMpce9UwsmhurO9T/0CKpKeZEynKUHcCWgsdsbDULhuCLr9WypzCy9wJm79bYwqE\n" +
+ "sAJGqK5i1Qxdp0O7VJkPaR1FTdTWazW6phWCnlskpWKtRmu09v9vosqnzd6vSKqo\n" +
+ "q/uL1i1lGvAyKdLSEBc8yrTUTrH82uFRZejcUgR2+f2BslZvPMtQlyQW35D9373A\n" +
+ "MQxZYPg7vNmJAhwEEAECAAYFAlEGa9UACgkQyCIVM6GqMsI8jRAAxjj0Z/62i+Jd\n" +
+ "Yy9iYuUXZyfLh5vexn89pesMgETaopNlv0OAT4rthpujmRCVLV/hN3XG94H/G5yW\n" +
+ "trwzokBz3a7JDdPVSWsLWibANx1zzukG2FkKEl1gWJyoTQ69xet7jZK3p9xl/xEH\n" +
+ "zS7t3rhniTqxViIpRIiHb5tSR/ESDIlR9tvoQwuoriI8TZd0tOkLS1myN+US39jf\n" +
+ "Dbo1sla85bTEAQusTtpHTe7OztzON45saJvfRRIdHlZify3lv7+6Y7jOpFHTe6g5\n" +
+ "1Qou5B9+mwZRb/2Pe4moWsQCKScZanJQDliyggY5s7a2gufEN2hTLzDniTc8FI0E\n" +
+ "YZK+14DiI5uoPhhGJo3kKGcviye07l1VNvxsKxPwW0Xf/hYvTwgn1xIKN1rs1dTY\n" +
+ "3wJpbGLZDdPfRDkqj4ZKAQTujjkqL1RQjdaBoFYmF3At6jV2dWCCK4Cppjv+rm6i\n" +
+ "hNgvKtYpPrTX3m0FJ31x9G8UkGlqhxT0lQ3f5MVLC8K7rqOEUHCIdy4jBaNDEWV7\n" +
+ "YJ/mU69yIb/xBGtVqrSDCMYh9sOy2zxaLQulUiSZRLRs1zr7npVvNf638DqErBAq\n" +
+ "rTjzTNVzkEKcgp1Xpn97xl+pDtS9qm+4P0bp7RPSwIzM9kym69Gnwy6xk6v/Gizf\n" +
+ "xZaRMdUyqXFuptsN5AAN5rn7ukZ3BAOJAhwEEAECAAYFAlEFpa8ACgkQskE8Zt0s\n" +
+ "P+paKQ//QCkex3hC4v2xPOCnMtUtOZ/s+8ptjUaxBmcud985Kl1vuzpfqhRE5SpB\n" +
+ "M3kgEWbqDmVhjvIDf2BwMxm1uLn3Ahl4fy3qZ0mOPlxTh1QRNINgPzf3Ch560jpy\n" +
+ "rug21kUmr9QQRX4yFKe+4g1+NSuC/A7P2AzJKSgkvQM2orR9noNMLNMYO61mr8bN\n" +
+ "cJgna/6G9PEwPunWkiU+ircp7gbDqZR0WDPIoj8WAHGHite7JA/tLD4t9gpNRSYw\n" +
+ "hXqUWXObbB0a6sFSzgJt4QwEqOP6M/eggymohBlVjexA1Zh95mfJkNGnjhCkLXG4\n" +
+ "qPMTq9Sk4cExv2Y5jSCEK/qDyz+IGRPGMIAdC8GFsLrQbWWcHPYWSAxGj5242gDg\n" +
+ "DyYUl0KxMignGOY51eEL35a3Yha/B3L64+6fwStKbWx2X4L5+m26BUAJ9nNhdCmB\n" +
+ "TMXB3uHhoHmstrI512md/M1voO76aq/20akGNcORTlKcfm2W805pSQfg1kfCQswP\n" +
+ "Ja1j9/L1ELmUy+VaDHj2y8MRNIEo00Ax++ElHIM3/+eehyesmdCSLh11IPwxnWhw\n" +
+ "GiJ+QPnqUqJe2e9LApff+Y+m4yPDUcZRnPfWDNRnfL4dEADR2P2ALF3YUS+OIDjh\n" +
+ "/U9njqx2WdWGpI57H9D84EiayOrVE7r3FWJB3qtbYRU9ZrHxDfiJARwEEwECAAYF\n" +
+ "AlEG4rgACgkQspS8BsdVKnrSDQgAiBoqUrh9dVmjo6EqEgJ+C+VsLdVP4t8DVWNV\n" +
+ "Ufpc2lndtrJRpvdyqFN9Kc/7gBEyFuNCM5P5JRKfVoKSY0i9sTq3yWQjsv2iMsQ/\n" +
+ "aDVaSzdmVvl4u7YtTJRGEgnIALL/X1Br9QmLcp/6Fju5p7f1mm5Sbwiqvi6G2cxP\n" +
+ "GHm0ptHsfr96I4JjCAKfNbiZ8I7d9tPnejT6sSuuoB307E/Dr4J+hS2HWuevNm4D\n" +
+ "KVrHvc/9+YTUIgkLSAqyAiOKUEsBIpDejyHfBCVM6x8S/BTBpLzJsIdXF4ip3ww9\n" +
+ "GRPq1m9y0yuC/hvnnbNAP4cFUfLV9KWtOMvlhoFGHplW5GZYV4kBHAQQAQIABgUC\n" +
+ "UQgq6AAKCRA1T6XiPqooYvSnB/9aJgpZ/LNn+QsXGQ0gz/D3aOT6P+coN/h2kfCU\n" +
+ "p0TQ3djdOodrWJ4SQz3a4AmMmRkdcQa0XPKQqZJSir76IHoKnQep07oPSWD+JQyE\n" +
+ "6Ix2BPM4Er5RqscQ3udbiwDatR57Hb5UIJChapiZqseu6Lx8uU/Swi9HjlFpKs3h\n" +
+ "sDP6ocpD2LW2yLadtE5ivLTcLO3qPEzsecflq2XIi3zuaZRlPzkhnj4bnVWo3ET3\n" +
+ "JfScv4OLTTMCWhF2zWSHjrwxBqMTdE/QrwaSMUvPdyaGjg8G9eDNRW3BylcDlWH7\n" +
+ "SzDeFZGb2SjmwR9ie/mbiUjD05lIEQCk9NGQC4GTE+8c/qFoiEYEEBECAAYFAlEK\n" +
+ "tfgACgkQBLW/NVdwg0qDGACeKq99OOyDJS1cCvAGJZeFRN6mmSkAoIDydwBZu23Z\n" +
+ "ghKLi9JFSnQj80A6iQIcBBMBCAAGBQJRDG00AAoJEK6ZNfMO1PAQbeYP/1eyE285\n" +
+ "TN3RO0OdaV7PyWkG9tpo+qiVMdEc77YP6DPkb46hDKcD5oTW5X9ySJIRP+SWFNUw\n" +
+ "3kTeuVYnZz1VtnxZWW4ODeSk9czbvxN7+aRCDtS67mjVG+KFhC+o+iiwi7Ex51gY\n" +
+ "BaFBTbowoIUBIAHcFz2nyBtY+8k2DRzcdiIAx+0CuRUWpWd3hqd5tK32SRAea366\n" +
+ "kCcGBbBBMmpMRMvlkzXVvI4CC3BRoqhFQDgj7liJhSqQ831SAhR5FqxbioXTVVA+\n" +
+ "h19Tzp+45YSjqyfE+VZe8PSg8P6hbLqUpqABZ+92e0HhR314U9XjPTpEEapaNMpm\n" +
+ "34b8u+Ix5w2IL91fCJd7P5GAboYu+BoQagDP5NV4fXQOj5gTulhn6nIHX64+/nRK\n" +
+ "F5IB+fcb0HZYFCQ2t7nMt2wM9QHmoPaGB9KhLrsre15raQURk0R2R9AEgh5kjdrY\n" +
+ "sWkkhng4kAkO7zIOMZiti5TkMWiCXh0Uq0jGIHS5Bqg1MhLoEC9pcCNBcOVjIPFt\n" +
+ "4jDBsxHAwp+x7Mmeo5ljFMoODAkcMq5JNhL1BI4kiSux5g32lU42aF6r1x79UPzE\n" +
+ "9MvycTBaCQLGiRTHaUZyOeUrcwIK8+4TgvYHTrL9f0de/og16Qair+K7T+HDBQpM\n" +
+ "p0evZHphbrnryKCUEKynIySP3IOTLAFevmLdiEYEExECAAYFAlF6pW0ACgkQK648\n" +
+ "9tr/sACqOACcCAjaMFIUCWY4VPnZ6CjiMohki6oAnRz9LeE27s05FM3qF3r7yqTB\n" +
+ "bLVetDREb21pbmlrIFNjaMO8cm1hbm4gPGQuc2NodWVybWFubkB0dS1icmF1bnNj\n" +
+ "aHdlaWcuZGU+iQE+BBMBAgAoBQJMO4BcAhsjBQkJZgGABgsJCAcDAgYVCAIJCgsE\n" +
+ "FgIDAQIeAQIXgAAKCRBxjAcBAAEigvbfB/9jjRtyvBDda1PbB5HMkS+5YZuy1mTj\n" +
+ "WmMYMtza1p8L3uRhZLb09Ve2sQ0tSNJSnUcL4MEJapXSnwsz3l7Zh7aOo6GjUAO9\n" +
+ "2LZzV/DWoCIei/caJhEiNV44HzdJUlN2+FBl5tMt9DFordfZIEm0jPWR8kTzF/l0\n" +
+ "sGMxVUBo7JrdodTX/2nybPLnSpSIhTrSfA8sn2VJV1FrN50nXOOnGJCYOx0HoyFP\n" +
+ "zX+QVoGO2S2lFl1dLcnrYKfNcMnkPZyxN9K+7/+4D6jKMCfn2hKBH2+in9D9yNWl\n" +
+ "Dbb9fxYP3AW1ObyrvyKFe1pCEBDpifH5+n9W2gqbNS/w7Xoh1/Phn9vsiEYEExEC\n" +
+ "AAYFAkxReYkACgkQZRxtbUCpVQKCkQCeKQ/i3XXlHunMU3blZu+vHoLO0XcAn3L+\n" +
+ "erc3GGnUT+fUix8RmeY1oPiOiQEcBBMBAgAGBQJMUXmoAAoJEFYVl/3JAMd4ZisH\n" +
+ "/0XuGH+G7cROn9u0cgjXSPScDdCTDVjaRwj1KYgZ3y63naqbvCe18gZ5sSsmsrBg\n" +
+ "WSnI9ynpQmU4HFfqOnZFXoV8qXkkoSv6E43QUtsrKBJf77VYRRtmpNsQEs6MQ7l0\n" +
+ "OPhWhnrEKWyeoa1PhMxN9vBXuqT/DvK9vQCCwAJ0i0mlLslnsw78tY6Dw3km0w7S\n" +
+ "1AS7ZQ0R5Hv/VtxAwQEsQ0ON3sptVzy9Mv1mpyqT8VPcpVSoMs76MLvHv1FpdUJr\n" +
+ "zxBwuapZjZcgH2L+QEzcgtUGIZKNfsw4w4T+S/fSzKQbhnROaLZG64cOAUuBAsxl\n" +
+ "S1xpg9tupgk86g8Gu+GTKNuIRgQQEQIABgUCTKRzFQAKCRDkP/WQI6H4pP4gAJ9a\n" +
+ "EpJPzGtsV1Hrp+L3J96kbX5cswCdH+IKmnveVUZBhWnDy2xCoW5X0BeISgQQEQIA\n" +
+ "CgUCTZxI4gMFAXgACgkQxZfezBd6/Xz5YgCfRouhQNbaBelpk+pgwk8XbVi+C3sA\n" +
+ "niQ3EIOLdXEDEsozpDcrKsd08rRAiQEcBBABAgAGBQJRBYnKAAoJENjTIPvvLEnw\n" +
+ "CAcH/Rmciw+bRgCPbroPGkzQHTD8y9RWTEclBDv6mnJLNlzacKzfFhafvMnP/CuH\n" +
+ "9gEVKf/nM1vCS9G98t5CksGrLsEXJoVRGeOG41bREafUc+n2dxEoHAW3yUuvfnVZ\n" +
+ "zLEgNBk066v4wuNh6mt/vEUz+8k2kJ/1BRe+V3x6kFKKfN5ezszXs8UWMwROrLHA\n" +
+ "ElYOZKeDEL6oLpykHXFokjLHMgOxnvwOuT3tOMuHo2kW2LyV5DyGlJbYx+pHbdaC\n" +
+ "9dzXe+uPQ2YzKCuc1TgyMAjCDcG9OOiZEqTdFAY8Lr5eUdNG8Rozv9+rteSk2QaQ\n" +
+ "FqCbKmpvV6u7cnO5dydego2t2O+JAhwEEAECAAYFAlEFzk4ACgkQ0DssUbNe6zwO\n" +
+ "Tg//Qi20qePBfw+fsq77Pddt6s5kAulMzIK2vbUQYY+63MCnIbiiTC5464K1xwMz\n" +
+ "56erQJSqltW5r7MxgLJdP2IISkG8PfRCBQqJWlsriHL/EuJ16AsLUCncWggHik1L\n" +
+ "oaHegyplc35Ai3Nm70nxCVtmC/62k8EHlFuw7rJbhqg5s1hAKjl7HRryAHhzag/o\n" +
+ "LwzIQxKiGg4jIRhhPS3Ye1NnJR1yv0JywovwgIbGYfvKqmNInym7au4o/DSKfigd\n" +
+ "hA8t1LwmcGaXrTEyxTm2wj6hXu1BITzZhmhayrCZv3ZnEE3r99bdq/Qr9f1qrVPD\n" +
+ "7W3OMve7MW2H2gpd48uVre29SV1RCl4qKnVGO7v6weppVudbnpYh/I+jfrpDC0QT\n" +
+ "h8qPf8/4aec/j9tD8tXYMtBK/+J1xEYTO4o+j5Gg2u4Nv22xT0TUD53m9SPo2PXr\n" +
+ "hIZLlE5t24Mj8lyK8f0nAspq9RZRoSaxdGzLzyrIVpXaaqo+3ldCA3JWPp+cAMay\n" +
+ "dj7TTEJ+v3DlEmqsI1UQMTcsDXA+PaEqVryRxQ7rSu4HXKeszEJfAxPQslsSIQcy\n" +
+ "deVqAhG06QYYgIgHGD8DNVvOOtp6IXj2vt2Ss77APVNMtIUualtb1R+tT+p/H3ti\n" +
+ "bFn295UYYnCJOjG/3QnWGBBzrgwNqSbrdIUNEAf3w7ogUk2JARwEEAECAAYFAlEG\n" +
+ "SpIACgkQZf77hz/aPZm5/wf/Z7uOa3Vg90aTBRa6UV22p7VK7kWYJW19MHNBNYQO\n" +
+ "vDEPMPVkJz0GXyckOnYnPz+9fZvIeO3RzvYVc9YOYAYvmBlu4934R5ZGiCqjvy+M\n" +
+ "KR9q6X0hXHZ/cioW2Li6zRIqdfdfomXHiK7IrK+yCLyJIIua8P5S0YzY6A0/Xfaj\n" +
+ "xgO1QCA8O1wNaP7vcCxIN2a5fptlmOEsNe0okfX/2I/lKMF+//pJHGa8kYC9rnFo\n" +
+ "Y5I4IcDuI/jXaJCattmUijAtvSaDMox5/MozEVv5lTbdet4cZyUQ6ZjgdrwjTs2e\n" +
+ "nnvU6C4PDZWng/kbBxkp+ne+iaiKrT0iCUgBDIOWu+8VZokCHAQQAQIABgUCUQZg\n" +
+ "WwAKCRBiudz/ZiTBoo65D/4vK0rAodk71PQvbWM2Z+p5+fWHmPrtg1v8jN3NTmWX\n" +
+ "RG7+ujO5sX0gA3K4aY4X0zNRUROVMhJi8A9m9fU0ZlaZ3dXxbOGmEuhG8PMAcnwY\n" +
+ "pTEYmHGOOcEOJ7dUE7zu9NIBKI+Hi1mzKLvQqLXbw9cRoAscHLK8M00hpmANSxb/\n" +
+ "MWJS1+l2gqkWE8u6s1Jxih00a+ex6ealhKsgaxMpSd98FQzu8s3achTQYFy7zEGL\n" +
+ "T5iEnXqspoEmrIrQoUL/yHJg6Sol5dofP/dWhMm7FewjrYZWykgo4yeGMPfIbALH\n" +
+ "KlQu2p5i7NdTfwVcei20rtlk5R+ZqU/k520qcU2mwfgKu1Oma9cxPEbJ6Cn6tVHl\n" +
+ "eelotjH6aCj8MratzZw+BO7u15st2j7BMFs5qPOqm98qCVJ/ujZbXgMvxuk/KloR\n" +
+ "GRsPsr6r146GsbkcrtdWTvvSwiYcA2rRbdJkqqUkXc3Pr1pdKNkc51rnRnuaUp1P\n" +
+ "EEyuBxSfiZdClpVf/yXiAZfPVf+db5mWhu32rvRq4GLQ5uXM/T/eX91YPWCcmOKn\n" +
+ "wM+4RK0wmpcn7Iak8f+stJKnHF9QcInqHvb2JiHS7K/UOdjpzeQ3gr0xjoSyT5tq\n" +
+ "Rhp13/PSr6tcgIWcghVTolmTtBj9BlAdf32+zfC/sE5fiuzQf+ckYHmyVIBjLAaH\n" +
+ "lYkCHAQQAQIABgUCUQZr1QAKCRDIIhUzoaoywhGzD/9PW8BdkzJXyR6fCXi4z682\n" +
+ "0/DvZkfYxHkOsaDBthjDwBnMaRZfNyP8QDQ9APequPSI43Kd3/RI+lof0NE2yXE2\n" +
+ "j7W33K1RnSXTunrZ+knKL2vsU2t0mpoBX3D7QGF9IwMl31JuOPV/pPJ9gK6mVyD5\n" +
+ "eq8fJgHkyI351OOnLFK7THDHF6lY2MeBSs8EsH8u0Qe4drb8AShOIEQxbG3NoCSp\n" +
+ "SEPeAuPO8KoYSsUCDrJqHhK/UtLkORjVQpwv1T2hZSXe4kEoUn9rccpc+dY8mype\n" +
+ "FZlq233hOfPRsYWX4z22JLK6XjuC9LmRN14ZjSQsYTbmHUKKn/yd5+JFeh9jaxQe\n" +
+ "vKg7ZYeHOOl+9xNiMOCyeADvz15tqFSmeNtPMpzw/gUrMuootmrYVw6wsgG3rWQx\n" +
+ "ljKMtR2Xq6/VEvE6RgVzE6Qp6ylFpQ332VuMCErrbUGwaimXbRQkX/C54U5pWdxg\n" +
+ "O4OxNWanKawYNJXQ//gnNosr9EOQQudQ/Adkq5BnnC57XRzpGz7G3gwndBzI1nkp\n" +
+ "lXJEpbh6+4WBxBulFbrv9VD2ot17uO9kQVWM7RLq4GI++x3pg1CQVdxo5yYMRcca\n" +
+ "7gEGeBR/OzYKJNKyFxOcSbtMT54WGeptWU5IPSaR3corZyBcu0LJCzldXLgfF5jY\n" +
+ "sP9hNqhK3hxgKelsI0ECd4kCHAQQAQIABgUCUQWlrwAKCRCyQTxm3Sw/6oi/D/9M\n" +
+ "70bk62Gvqhe9X3bUvrrff1yieTa2UhTDqT3EG1cMRdLa1aGJsjbEBy2hr9u7vCWP\n" +
+ "2NVYkPSIo2Q2+t9LfeT19Q+nzG11ynAZ+MM+pY63aHN8a/YrSEGLYbRM41Q8337/\n" +
+ "SzppV737R9HYibj9pLo2m8q03DnjoacEfBO6RExoXoVuNn3J7rkaA52XNgWrj/MP\n" +
+ "fcMVCJqUsBA69ZliFWNmizUeeM3yWvj6HSeDBxwz7l5pmj3Gq/50qw0vzYe6t05M\n" +
+ "BGow8xqI2rstvr7wF/D2WZyABIIDEwlpE1kfBCB6Yifdq8go10dBJGH7KCETo7er\n" +
+ "/a5NVV8ur4sgSJsOBrHYW0aHMJWvCp2mSooX4VOWhd91PJ1vUD6+3H8IB1NGWB5v\n" +
+ "CVrqVTpYViZDhYcAbIEqF98vOwkjJga4w0BFUqNC5IwbWQ4VH5pDHuSviUyFIWii\n" +
+ "ejAJLmZu5ycg3fHXJ51HDJlgyttP16W5NPJnPpOe7bKipcUcKER7YDOlPF/z2y7E\n" +
+ "Af9lp3uPLx7iIN46iAAlbwSkMny52DNSxCaOsdvmuB5nIkfn762+1cURFvgACYh0\n" +
+ "NeQawtn2tQGTYQjw6P21uJGXi8kmy12iFHGhi7vptVVZxNDT1GvcozyZ3bdOWN2T\n" +
+ "/S/x3o+RO8kbdMgHjkOOHKNHYvfQpKAhAbVD5lCNCokBHAQTAQIABgUCUQbiuAAK\n" +
+ "CRCylLwGx1UqeiraB/9yKTH4xcj0e13D8zRCyTcpQzoJwihllFVbtOYV07dcKi3d\n" +
+ "SKoMPpW3W2yr3ADHFDTpHhNj55ZOqq985k9hrR2KccbFmvSAkqDJluBeK49AK7uF\n" +
+ "4UW1kAHg2XqZB8ieiyITNsvNpZaB9a0dIGnuAoNJdE/b+Jwx8h9di5IPjVc9P0Sz\n" +
+ "z6u1z+H4xlUc7rB0VW3xlLJEUvmflg2fqGZvJ/jE6F5/Sn3oPZ2Bevoz+F7gqsOW\n" +
+ "LLjQbrulG/vLg5zXFqYNPU/2x34Z6bwEmmvWSYwY+bXlfYH71rEVzSzK3oA2KyyU\n" +
+ "nCD4v6XbqdEj9Iaiqvz5wggs+pzRh7s4py9TjuhFiQEcBBABAgAGBQJRCCroAAoJ\n" +
+ "EDVPpeI+qihix50H/3bfZkaaYo3OnmyQbj6KGcuptkBSdr77CfRgho3R0mOrFT63\n" +
+ "1vUv8l3pUwCNxCWH1wm5v3QvYpCKs/G+J8fJvzJjInw+/CcEUtPJuO/A6WCsJYZ+\n" +
+ "42O1Eu5IE6BpQhQwvq/v+ggJNdWZLNDipVBTVDtB6J8RBHk3ncUx6upTWVcQlvSv\n" +
+ "kCwJ7hRMglM9V8jYBqhlR/oxDxbDaj9TXCkpQc6VuM8VLNKaA1Ih7tEvCtoW1+0d\n" +
+ "ZQIqEn3DkWun1CtezBP/xR9BeA4tGselDnAZACUD9FxSza70FCoD2m/bUHFvsvgY\n" +
+ "4cGNSjg+ZRgS3BikUgVKJAL/A3qhyF25ATSLFT6IRgQQEQIABgUCUQq1+AAKCRAE\n" +
+ "tb81V3CDSkyLAJ9LEg8I/lyaUp0W6XUCfZ+yeFJk7QCggB2oBTyBin4VRDYg5aFW\n" +
+ "2vDbKHSJAhwEEwEIAAYFAlEMbTQACgkQrpk18w7U8BDCuxAAiDD0h9v8UksJVjiz\n" +
+ "RpAA6qiZyddjghzcO4GgAqxv3fdqBNZ5uYCtbYEeEHLWHmd/O2f98i4PRgHe6xlo\n" +
+ "gC+7TQAG/O4YVNtnntCFmx0G4z6/1nZc+IbfYHSmk0tszo6zIO0NOzek5N8t8GzD\n" +
+ "QknSixKh8z0hWmseUz0RJKagmxkbnDOLvfVAOIbJW3iKVauIeyxqE/5gNIWn+/vT\n" +
+ "p87MxSpMMrgWHjMHuyaxdN93t+ea7XZ+iWQCd9HQ7RhylATUPsoeiwjUm0O16jqX\n" +
+ "OGLFJM9PFKS4DIRMze4JRrdFlIxOQP4xjbu8VS4hGJK8Gi46QMhB8TLR3qOzpZyU\n" +
+ "S2f+Kjt4RoYa8iwbWbfB8jCSGf+lgQPsNDVEdaRJQPqClKqkfldlt32E9GULx9ln\n" +
+ "Ncyfb0CXt06Gt9dFXIP/tU0cgZm8KsmSEl11TofZ/UL/KLgIJjiw80ZqUSrFKARz\n" +
+ "6UfxQwkbIWMu5BXU5t/8P/SQawpSbXugnejQ9Q7wNZ593SgH8VdXrGS5zNagGaIj\n" +
+ "GJsj4LzCuYc2a12w1zuWeVIGCbJyoWzd6PYfIleHZo2ISRnAR6S24yTKPkMwiutT\n" +
+ "VthVNeE33Yek6YQZ5Xdmgfy/q98IdV12U+sA1LQOABoJF+goBNHh1AlfCAuLbgmo\n" +
+ "BYSjSGXQ7XjaiNUeexAV8f7TEhiIRgQTEQIABgUCUXqlbQAKCRArrjz22v+wABZn\n" +
+ "AKCs+Z19eY/NmrSzPQsZ7SlHBNremACeJehgL8VdZkPMiW3xUbEki2ji62u0MURv\n" +
+ "bWluaWsgU2Now7xybWFubiA8KzQ5MTcxNjU4MTQ1MkBjcnlwdG9jYWxsLm9yZz6J\n" +
+ "AR8EMAECAAkFAlD393UCHQAACgkQcYwHAQABIoJawQf/RpeNorZbtnIZmNz8y2Ko\n" +
+ "3xNKEGlf4XoFY7irtJo4ImO5Muftr+Y20rhIQYTf7tBNaFabj2nb223d7Apg84lR\n" +
+ "MGSUA9+5V+C0yjALA1SttqRW2evd4NX9/N5WNrf4z+S3C2QfD0mL41eUiIgLgJhc\n" +
+ "Hmll9wiZyJzr2t9GNkOk0iYJzkqDBhdxj2Zl3OcD3v6P6IUM+3RWzk5tFmt/YHvN\n" +
+ "aXPWgND/8OVAdd470p/aK10qZ9v51ZxWN1OT/HVZrNh5rLdfroeNjFKtS/pl1wMT\n" +
+ "ImtN03lhhyWR0a++Eowh6zEJKeDfg7C+2djqsB9C8nMDZbmQdNLFJNQRVSiK4i8E\n" +
+ "4YkBPgQTAQIAKAUCTp8R3QIbIwUJCWYBgAYLCQgHAwIGFQgCCQoLBBYCAwECHgEC\n" +
+ "F4AACgkQcYwHAQABIoI6KAf/THY5iMm+CH5dJOTAwuHUyuKduvSVFxq+1WX3rX21\n" +
+ "x670fhHx9WarvE+CsgreUzfBVZxq1cq2oB72KyFsa45utKt761x4QEOM01CRQO21\n" +
+ "hIgl+wed9CRgzO17OzZ/E/G47/P8pHxm8kXwictTWqZ4rlgfzOg7YcY5An05rFH2\n" +
+ "J+fxUVfdjZ2u75XDE6CAHV1hMvrRwatnJQ33S1/yRCdhT3qad7U7wrbtiu7Y4KNi\n" +
+ "gM4ur+kGqRSNWN6/4v7OHRgj0Pp6jbs2pXqccR9rAhlKhnatd6RKb1+LlYEyblSC\n" +
+ "76PIZm26h8qhY8UKrj9a2ydmWDY2uquxbRLvjrT8suZZebQvRG9taW5payBTY2jD\n" +
+ "vHJtYW5uIDxzY2h1ZXJtYW5uQGlici5jcy50dS1icy5kZT6JAT4EEwECACgFAlLq\n" +
+ "hOACGyMFCQlmAYAGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEHGMBwEAASKC\n" +
+ "nKcH/ik9LmnpEclsCDfzYqTEbVOSNZA30YcvwdlsHlzjRm+KjJC3D350147o5D60\n" +
+ "xq0UBUxKeXJPPMofeRgzTiAjTdv2ilJEZe8bMM5b7gcmp92q4tAY3OxcGNprIswc\n" +
+ "eb5hG4kY905WAy076iaQOD9z0Y+bXWQo0OHc07lc4+8ZLG9u+rGDjfF0x4UWgkAQ\n" +
+ "d8Thth4lTzdZR/kLLBCdlOyb9sAKqfbxbfATDQEceex7dZF/uJRCvFojHMtDbhxe\n" +
+ "xdfEjWbsJRQR0KKTHYS02zqhVu34elwuRSWf1OOR7ynh5nD5CCAAmVbi+x7y281i\n" +
+ "YYTchi/s71CSs81OtFtaBfVNSeq5AQ0ES4b+oAEIANr825Ns9mewUTHNfZ3/xK7R\n" +
+ "mp+nVLgOoyJDZF+Qum08RnFiECCiDTPlHIUuZt6jUu8vb/TKH5bdviFkC2MQPhm0\n" +
+ "/5sbbbqbV6wMnXfMd/RTPkIeeheEumY/5n4oYYGuVTZ+0MBouPY/wXfxp6HkqtuI\n" +
+ "qUZm8Bmy9AEScxiBURBu4MOr9/c9niLFlnpFLhEsSm17nS6/tdEJGdMRb3WNFn5+\n" +
+ "bE8w9e8RqPlye9SFZHsjmv9jCZaW5fZkcdDTcDClPVvIBtUl6y/kkh0RfIwdU+T5\n" +
+ "GRI8XekgI8WkvEqxTaQqn03C79zU3nhRuSgy8E492uaTmwpmAXC/m4Z6luTNPrEA\n" +
+ "EQEAAYkBJQQYAQIADwUCS4b+oAIbDAUJCWYBgAAKCRBxjAcBAAEignQvB/915fHh\n" +
+ "7di/yoyJfmufnj4fJ9Lt6OYyXvKetXpC+dLx7zH61KCeKosgWIN5HyY2Si1ZfGdO\n" +
+ "JQ1L0d9Y+TsRVslU34uY7DuYLs4yGNwFdI4r6Y+PHIAE0Cd3xxf8xFr8oiinPMvm\n" +
+ "SVDO2MbF0W/TnYwvyoN7Of0uAUdFY0sRupamPgNEz7dTZ+UoKgRFzfPh4zUb5Hav\n" +
+ "loqJCE/BEJ4wkxYTaJfFdJq+3WAZdd0f1OZLLDcCCvbZHNYBvpPauoVq3LD8MHXz\n" +
+ "hCRY9Rp2ZxX92PrFiSNpKheP30iZM8VInDfPGaApQU1y8R2uLL0I/7XWiFtpmR6e\n" +
+ "k3wUxv46o0y15asU\n" +
+ "=Bbew\n" +
+ "-----END PGP PUBLIC KEY BLOCK-----\n";
+
+}
diff --git a/OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/OpenPgpProviderActivity.java b/OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/OpenPgpProviderActivity.java
new file mode 100644
index 000000000..21eca36d6
--- /dev/null
+++ b/OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/OpenPgpProviderActivity.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.demo;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import org.openintents.openpgp.OpenPgpError;
+import org.openintents.openpgp.OpenPgpSignatureResult;
+import org.openintents.openpgp.util.OpenPgpApi;
+import org.openintents.openpgp.util.OpenPgpConstants;
+import org.openintents.openpgp.util.OpenPgpServiceConnection;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+
+public class OpenPgpProviderActivity extends Activity {
+ private EditText mMessage;
+ private EditText mCiphertext;
+ private EditText mEncryptUserIds;
+ private Button mSign;
+ private Button mEncrypt;
+ private Button mSignAndEncrypt;
+ private Button mDecryptAndVerify;
+
+ private OpenPgpServiceConnection mServiceConnection;
+
+ public static final int REQUEST_CODE_SIGN = 9910;
+ public static final int REQUEST_CODE_ENCRYPT = 9911;
+ public static final int REQUEST_CODE_SIGN_AND_ENCRYPT = 9912;
+ public static final int REQUEST_CODE_DECRYPT_AND_VERIFY = 9913;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.openpgp_provider);
+
+ mMessage = (EditText) findViewById(R.id.crypto_provider_demo_message);
+ mCiphertext = (EditText) findViewById(R.id.crypto_provider_demo_ciphertext);
+ mEncryptUserIds = (EditText) findViewById(R.id.crypto_provider_demo_encrypt_user_id);
+ mSign = (Button) findViewById(R.id.crypto_provider_demo_sign);
+ mEncrypt = (Button) findViewById(R.id.crypto_provider_demo_encrypt);
+ mSignAndEncrypt = (Button) findViewById(R.id.crypto_provider_demo_sign_and_encrypt);
+ mDecryptAndVerify = (Button) findViewById(R.id.crypto_provider_demo_decrypt_and_verify);
+
+ mSign.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ sign(new Bundle());
+ }
+ });
+ mEncrypt.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ encrypt(new Bundle());
+ }
+ });
+ mSignAndEncrypt.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ signAndEncrypt(new Bundle());
+ }
+ });
+ mDecryptAndVerify.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ decryptAndVerify(new Bundle());
+ }
+ });
+
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
+ String providerPackageName = settings.getString("openpgp_provider_list", "");
+ if (TextUtils.isEmpty(providerPackageName)) {
+ Toast.makeText(this, "No OpenPGP Provider selected!", Toast.LENGTH_LONG).show();
+ finish();
+ } else {
+ // bind to service
+ mServiceConnection = new OpenPgpServiceConnection(
+ OpenPgpProviderActivity.this, providerPackageName);
+ mServiceConnection.bindToService();
+ }
+ }
+
+ private void handleError(final OpenPgpError error) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ Toast.makeText(OpenPgpProviderActivity.this,
+ "onError id:" + error.getErrorId() + "\n\n" + error.getMessage(),
+ Toast.LENGTH_LONG).show();
+ Log.e(Constants.TAG, "onError getErrorId:" + error.getErrorId());
+ Log.e(Constants.TAG, "onError getMessage:" + error.getMessage());
+ }
+ });
+ }
+
+ private void handleSignature(final OpenPgpSignatureResult sigResult) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ Toast.makeText(OpenPgpProviderActivity.this,
+ sigResult.toString(),
+ Toast.LENGTH_LONG).show();
+ }
+ });
+ }
+
+ /**
+ * Takes input from message or ciphertext EditText and turns it into a ByteArrayInputStream
+ *
+ * @param ciphertext
+ * @return
+ */
+ private InputStream getInputstream(boolean ciphertext) {
+ InputStream is = null;
+ try {
+ String inputStr = null;
+ if (ciphertext) {
+ inputStr = mCiphertext.getText().toString();
+ } else {
+ inputStr = mMessage.getText().toString();
+ }
+ is = new ByteArrayInputStream(inputStr.getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ Log.e(Constants.TAG, "UnsupportedEncodingException", e);
+ }
+
+ return is;
+ }
+
+ private class MyCallback implements OpenPgpApi.IOpenPgpCallback {
+ boolean returnToCiphertextField;
+ ByteArrayOutputStream os;
+ int requestCode;
+
+ private MyCallback(boolean returnToCiphertextField, ByteArrayOutputStream os, int requestCode) {
+ this.returnToCiphertextField = returnToCiphertextField;
+ this.os = os;
+ this.requestCode = requestCode;
+ }
+
+ @Override
+ public void onReturn(Bundle result) {
+ switch (result.getInt(OpenPgpConstants.RESULT_CODE)) {
+ case OpenPgpConstants.RESULT_CODE_SUCCESS: {
+ try {
+ Log.d(OpenPgpConstants.TAG, "result: " + os.toByteArray().length
+ + " str=" + os.toString("UTF-8"));
+
+ if (returnToCiphertextField) {
+ mCiphertext.setText(os.toString("UTF-8"));
+ } else {
+ mMessage.setText(os.toString("UTF-8"));
+ }
+ } catch (UnsupportedEncodingException e) {
+ Log.e(Constants.TAG, "UnsupportedEncodingException", e);
+ }
+
+ if (result.getBoolean(OpenPgpConstants.RESULT_SIGNATURE, false)) {
+ OpenPgpSignatureResult sigResult
+ = result.getParcelable(OpenPgpConstants.RESULT_SIGNATURE);
+ handleSignature(sigResult);
+ }
+ break;
+ }
+ case OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED: {
+ PendingIntent pi = result.getParcelable(OpenPgpConstants.RESULT_INTENT);
+ try {
+ OpenPgpProviderActivity.this.startIntentSenderForResult(pi.getIntentSender(),
+ requestCode, null, 0, 0, 0);
+ } catch (IntentSender.SendIntentException e) {
+ Log.e(Constants.TAG, "SendIntentException", e);
+ }
+ break;
+ }
+ case OpenPgpConstants.RESULT_CODE_ERROR: {
+ OpenPgpError error = result.getParcelable(OpenPgpConstants.RESULT_ERRORS);
+ handleError(error);
+ break;
+ }
+ }
+ }
+ }
+
+ public void sign(Bundle params) {
+ params.putBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
+
+ InputStream is = getInputstream(false);
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
+ api.sign(params, is, os, new MyCallback(true, os, REQUEST_CODE_SIGN));
+ }
+
+ public void encrypt(Bundle params) {
+ params.putStringArray(OpenPgpConstants.PARAMS_USER_IDS, mEncryptUserIds.getText().toString().split(","));
+ params.putBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
+
+ InputStream is = getInputstream(false);
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
+ api.encrypt(params, is, os, new MyCallback(true, os, REQUEST_CODE_ENCRYPT));
+ }
+
+ public void signAndEncrypt(Bundle params) {
+ params.putStringArray(OpenPgpConstants.PARAMS_USER_IDS, mEncryptUserIds.getText().toString().split(","));
+ params.putBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
+
+ InputStream is = getInputstream(false);
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
+ api.signAndEncrypt(params, is, os, new MyCallback(true, os, REQUEST_CODE_SIGN_AND_ENCRYPT));
+ }
+
+ public void decryptAndVerify(Bundle params) {
+ params.putBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
+
+ InputStream is = getInputstream(true);
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
+ api.decryptAndVerify(params, is, os, new MyCallback(false, os, REQUEST_CODE_DECRYPT_AND_VERIFY));
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ Log.d(Constants.TAG, "onActivityResult resultCode: " + resultCode);
+
+ // try again after user interaction
+ if (resultCode == RESULT_OK) {
+ /*
+ * The params originally given to the pgp method are are again
+ * returned here to be used when calling again after user interaction.
+ *
+ * They also contain results from the user interaction which happened,
+ * for example selected key ids.
+ */
+ Bundle params = data.getBundleExtra(OpenPgpConstants.PI_RESULT_PARAMS);
+
+ switch (requestCode) {
+ case REQUEST_CODE_SIGN: {
+ sign(params);
+ break;
+ }
+ case REQUEST_CODE_ENCRYPT: {
+ encrypt(params);
+ break;
+ }
+ case REQUEST_CODE_SIGN_AND_ENCRYPT: {
+ signAndEncrypt(params);
+ break;
+ }
+ case REQUEST_CODE_DECRYPT_AND_VERIFY: {
+ decryptAndVerify(params);
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ if (mServiceConnection != null) {
+ mServiceConnection.unbindFromService();
+ }
+ }
+
+}
diff --git a/OpenPGP-Keychain-API-Demo/src/main/res/drawable-hdpi/ic_launcher.png b/OpenPGP-Keychain-API/example-app/src/main/res/drawable-hdpi/ic_launcher.png
index cf114d7db..cf114d7db 100644
--- a/OpenPGP-Keychain-API-Demo/src/main/res/drawable-hdpi/ic_launcher.png
+++ b/OpenPGP-Keychain-API/example-app/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/OpenPGP-Keychain-API-Demo/src/main/res/drawable-mdpi/ic_launcher.png b/OpenPGP-Keychain-API/example-app/src/main/res/drawable-mdpi/ic_launcher.png
index d55318843..d55318843 100644
--- a/OpenPGP-Keychain-API-Demo/src/main/res/drawable-mdpi/ic_launcher.png
+++ b/OpenPGP-Keychain-API/example-app/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/OpenPGP-Keychain-API-Demo/src/main/res/drawable-xhdpi/ic_launcher.png b/OpenPGP-Keychain-API/example-app/src/main/res/drawable-xhdpi/ic_launcher.png
index 13ed3d450..13ed3d450 100644
--- a/OpenPGP-Keychain-API-Demo/src/main/res/drawable-xhdpi/ic_launcher.png
+++ b/OpenPGP-Keychain-API/example-app/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/OpenPGP-Keychain-API-Demo/src/main/res/drawable-xxhdpi/ic_launcher.png b/OpenPGP-Keychain-API/example-app/src/main/res/drawable-xxhdpi/ic_launcher.png
index 831c993d4..831c993d4 100644
--- a/OpenPGP-Keychain-API-Demo/src/main/res/drawable-xxhdpi/ic_launcher.png
+++ b/OpenPGP-Keychain-API/example-app/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/OpenPGP-Keychain-API/example-app/src/main/res/layout/openpgp_provider.xml b/OpenPGP-Keychain-API/example-app/src/main/res/layout/openpgp_provider.xml
new file mode 100644
index 000000000..6c2ce1a7c
--- /dev/null
+++ b/OpenPGP-Keychain-API/example-app/src/main/res/layout/openpgp_provider.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/parent_scroll"
+ android:fillViewport="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:padding="8dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Encrypt UserIds (split with &apos;,&apos;)"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+
+ <EditText
+ android:id="@+id/crypto_provider_demo_encrypt_user_id"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="dominik@dominikschuermann.de"
+ android:textAppearance="@android:style/TextAppearance.Small" />
+
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Message"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+
+ <ScrollView
+ android:id="@+id/child_scroll1"
+ android:fillViewport="true"
+ android:layout_width="match_parent"
+ android:layout_height="120dp">
+
+ <EditText
+ android:id="@+id/crypto_provider_demo_message"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scrollHorizontally="true"
+ android:scrollbars="vertical"
+ android:text="message"
+ android:textAppearance="@android:style/TextAppearance.Small" />
+ </ScrollView>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Ciphertext"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <ScrollView
+ android:id="@+id/child_scroll2"
+ android:fillViewport="true"
+ android:layout_width="match_parent"
+ android:layout_height="120dp">
+
+ <EditText
+ android:id="@+id/crypto_provider_demo_ciphertext"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="ciphertext"
+ android:textAppearance="@android:style/TextAppearance.Small" />
+ </ScrollView>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button
+ android:id="@+id/crypto_provider_demo_sign"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Sign"
+ android:layout_gravity="center_vertical" />
+
+ <Button
+ android:id="@+id/crypto_provider_demo_encrypt"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Encrypt"
+ android:layout_gravity="center_vertical" />
+
+ <Button
+ android:id="@+id/crypto_provider_demo_sign_and_encrypt"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Sign and Encrypt"
+ android:layout_gravity="center_vertical" />
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/crypto_provider_demo_decrypt_and_verify"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Decrypt and Verify" />
+
+ </LinearLayout>
+</ScrollView> \ No newline at end of file
diff --git a/OpenPGP-Keychain-API/example-app/src/main/res/xml/base_preference.xml b/OpenPGP-Keychain-API/example-app/src/main/res/xml/base_preference.xml
new file mode 100644
index 000000000..b38e07a36
--- /dev/null
+++ b/OpenPGP-Keychain-API/example-app/src/main/res/xml/base_preference.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <PreferenceCategory android:title="OpenKeychain Intents">
+ <Preference
+ android:key="intent_demo"
+ android:title="Intent Demo" />
+ </PreferenceCategory>
+ <PreferenceCategory android:title="OpenPGP Provider API">
+ <org.openintents.openpgp.util.OpenPgpListPreference
+ android:key="openpgp_provider_list"
+ android:title="Select OpenPGP Provider!" />
+ <Preference
+ android:key="openpgp_provider_demo"
+ android:title="OpenPGP Provider Demo" />
+ </PreferenceCategory>
+
+</PreferenceScreen> \ No newline at end of file
diff --git a/OpenPGP-Keychain-API/example-app/src/main/res/xml/intent_preference.xml b/OpenPGP-Keychain-API/example-app/src/main/res/xml/intent_preference.xml
new file mode 100644
index 000000000..801e4a78a
--- /dev/null
+++ b/OpenPGP-Keychain-API/example-app/src/main/res/xml/intent_preference.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <PreferenceCategory android:title="Intents (org.sufficientlysecure.keychain.action.)">
+ <Preference
+ android:key="ENCRYPT"
+ android:title="ENCRYPT" />
+ <Preference
+ android:key="ENCRYPT_URI"
+ android:title="ENCRYPT with Uri" />
+ <Preference
+ android:key="DECRYPT"
+ android:title="DECRYPT" />
+ <Preference
+ android:key="IMPORT_KEY"
+ android:title="IMPORT_KEY" />
+ <Preference
+ android:key="IMPORT_KEY_FROM_KEYSERVER"
+ android:title="IMPORT_KEY_FROM_KEYSERVER" />
+ <Preference
+ android:key="IMPORT_KEY_FROM_QR_CODE"
+ android:title="IMPORT_KEY_FROM_QR_CODE" />
+ </PreferenceCategory>
+ <PreferenceCategory android:title="Special Intents">
+ <Preference
+ android:key="openpgp4fpr"
+ android:title="VIEW openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282" />
+ </PreferenceCategory>
+
+</PreferenceScreen> \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/gradle/wrapper/gradle-wrapper.jar b/OpenPGP-Keychain-API/gradle/gradle/wrapper/gradle-wrapper.jar
index d5c591c9c..583859812 100644
--- a/OpenPGP-Keychain-API-Demo/gradle/wrapper/gradle-wrapper.jar
+++ b/OpenPGP-Keychain-API/gradle/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/OpenPGP-Keychain-API-Demo/gradle/wrapper/gradle-wrapper.properties b/OpenPGP-Keychain-API/gradle/gradle/wrapper/gradle-wrapper.properties
index e3efa88f1..42c0fbd9d 100644
--- a/OpenPGP-Keychain-API-Demo/gradle/wrapper/gradle-wrapper.properties
+++ b/OpenPGP-Keychain-API/gradle/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Jan 27 14:45:08 CET 2014
+#Sun Feb 09 19:19:25 CET 2014
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.9-bin.zip
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip
diff --git a/OpenPGP-Keychain-API-Demo/gradlew b/OpenPGP-Keychain-API/gradle/gradlew
index 91a7e269e..91a7e269e 100755
--- a/OpenPGP-Keychain-API-Demo/gradlew
+++ b/OpenPGP-Keychain-API/gradle/gradlew
diff --git a/OpenPGP-Keychain-API-Demo/gradlew.bat b/OpenPGP-Keychain-API/gradle/gradlew.bat
index aec99730b..aec99730b 100644
--- a/OpenPGP-Keychain-API-Demo/gradlew.bat
+++ b/OpenPGP-Keychain-API/gradle/gradlew.bat
diff --git a/OpenPGP-Keychain-API/gradle/wrapper/gradle-wrapper.jar b/OpenPGP-Keychain-API/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..583859812
--- /dev/null
+++ b/OpenPGP-Keychain-API/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/OpenPGP-Keychain-API/gradle/wrapper/gradle-wrapper.properties b/OpenPGP-Keychain-API/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..932184188
--- /dev/null
+++ b/OpenPGP-Keychain-API/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Feb 14 01:26:40 CET 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
diff --git a/OpenPGP-Keychain-API/gradlew b/OpenPGP-Keychain-API/gradlew
new file mode 100755
index 000000000..91a7e269e
--- /dev/null
+++ b/OpenPGP-Keychain-API/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/OpenPGP-Keychain-API/gradlew.bat b/OpenPGP-Keychain-API/gradlew.bat
new file mode 100644
index 000000000..aec99730b
--- /dev/null
+++ b/OpenPGP-Keychain-API/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/.gitignore b/OpenPGP-Keychain-API/libraries/keychain-api-library/.gitignore
new file mode 100644
index 000000000..aa8bb5760
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/.gitignore
@@ -0,0 +1,29 @@
+#Android specific
+bin
+gen
+obj
+lint.xml
+local.properties
+release.properties
+ant.properties
+*.class
+*.apk
+
+#Gradle
+.gradle
+build
+gradle.properties
+
+#Maven
+target
+pom.xml.*
+
+#Eclipse
+.project
+.classpath
+.settings
+.metadata
+
+#IntelliJ IDEA
+.idea
+*.iml
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/AndroidManifest.xml b/OpenPGP-Keychain-API/libraries/keychain-api-library/AndroidManifest.xml
new file mode 100644
index 000000000..768922c22
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.sufficientlysecure.keychain.api"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="9"
+ android:targetSdkVersion="19" />
+
+ <application/>
+
+</manifest> \ No newline at end of file
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/LICENSE b/OpenPGP-Keychain-API/libraries/keychain-api-library/LICENSE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/build.gradle b/OpenPGP-Keychain-API/libraries/keychain-api-library/build.gradle
new file mode 100644
index 000000000..1d5911783
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/build.gradle
@@ -0,0 +1,35 @@
+// please leave this here, so this library builds on its own
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:0.8.3'
+ }
+}
+
+apply plugin: 'android-library'
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion '19.0.1'
+
+ // NOTE: We are using the old folder structure to also support Eclipse
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ resources.srcDirs = ['src']
+ aidl.srcDirs = ['src']
+ renderscript.srcDirs = ['src']
+ res.srcDirs = ['res']
+ assets.srcDirs = ['assets']
+ }
+ }
+
+ // Do not abort build if lint finds errors
+ lintOptions {
+ abortOnError false
+ }
+}
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/project.properties b/OpenPGP-Keychain-API/libraries/keychain-api-library/project.properties
new file mode 100644
index 000000000..91d2b0246
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
+android.library=true
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-hdpi/ic_action_cancel_launchersize.png b/OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-hdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 000000000..71b9118dc
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-hdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-mdpi/ic_action_cancel_launchersize.png b/OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-mdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 000000000..270abf45f
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-mdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-xhdpi/ic_action_cancel_launchersize.png b/OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-xhdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 000000000..1e3571fa5
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-xhdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-xxhdpi/ic_action_cancel_launchersize.png b/OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-xxhdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 000000000..52044601e
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/res/drawable-xxhdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/res/values/strings.xml b/OpenPGP-Keychain-API/libraries/keychain-api-library/res/values/strings.xml
new file mode 100644
index 000000000..a198d0b5e
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="openpgp_list_preference_none">None</string>
+
+</resources> \ No newline at end of file
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/IOpenPgpService.aidl b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/IOpenPgpService.aidl
new file mode 100644
index 000000000..578a7d4b5
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/IOpenPgpService.aidl
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp;
+
+interface IOpenPgpService {
+
+ /**
+ * General extras
+ * --------------
+ *
+ * Bundle params:
+ * int api_version (required)
+ * boolean ascii_armor (request ascii armor for ouput)
+ *
+ * returned Bundle:
+ * int result_code (0, 1, or 2 (see OpenPgpConstants))
+ * OpenPgpError error (if result_code == 0)
+ * Intent intent (if result_code == 2)
+ *
+ */
+
+ /**
+ * Sign only
+ *
+ * optional params:
+ * String passphrase (for key passphrase)
+ */
+ Bundle sign(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
+
+ /**
+ * Encrypt
+ *
+ * Bundle params:
+ * long[] key_ids
+ * or
+ * String[] user_ids (= emails of recipients) (if more than one key has this user_id, a PendingIntent is returned)
+ *
+ * optional params:
+ * String passphrase (for key passphrase)
+ */
+ Bundle encrypt(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
+
+ /**
+ * Sign and encrypt
+ *
+ * Bundle params:
+ * same as in encrypt()
+ */
+ Bundle signAndEncrypt(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
+
+ /**
+ * Decrypts and verifies given input bytes. This methods handles encrypted-only, signed-and-encrypted,
+ * and also signed-only input.
+ *
+ * returned Bundle:
+ * OpenPgpSignatureResult signature_result
+ */
+ Bundle decryptAndVerify(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
+
+ /**
+ * Retrieves key ids based on given user ids (=emails)
+ *
+ * Bundle params:
+ * String[] user_ids
+ *
+ * returned Bundle:
+ * long[] key_ids
+ */
+ Bundle getKeyIds(in Bundle params);
+
+} \ No newline at end of file
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpError.java b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/OpenPgpError.java
index f108d3169..4dd2cc641 100644
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpError.java
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/OpenPgpError.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,10 +20,13 @@ import android.os.Parcel;
import android.os.Parcelable;
public class OpenPgpError implements Parcelable {
+ public static final int CLIENT_SIDE_ERROR = -1;
+
public static final int GENERIC_ERROR = 0;
- public static final int NO_OR_WRONG_PASSPHRASE = 1;
- public static final int NO_USER_IDS = 2;
- public static final int USER_INTERACTION_REQUIRED = 3;
+ public static final int INCOMPATIBLE_API_VERSIONS = 1;
+
+ public static final int NO_OR_WRONG_PASSPHRASE = 2;
+ public static final int NO_USER_IDS = 3;
int errorId;
String message;
diff --git a/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpSignatureResult.java b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/OpenPgpSignatureResult.java
index 829f8f8cf..16c79ca27 100644
--- a/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpSignatureResult.java
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/OpenPgpSignatureResult.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,14 +22,14 @@ import android.os.Parcelable;
public class OpenPgpSignatureResult implements Parcelable {
// generic error on signature verification
public static final int SIGNATURE_ERROR = 0;
- // successfully verified signature, with trusted public key
- public static final int SIGNATURE_SUCCESS_TRUSTED = 1;
+ // successfully verified signature, with certified public key
+ public static final int SIGNATURE_SUCCESS_CERTIFIED = 1;
// no public key was found for this signature verification
// you can retrieve the key with
// getKeys(new String[] {String.valueOf(signatureResult.getKeyId)}, true, callback)
public static final int SIGNATURE_UNKNOWN_PUB_KEY = 2;
- // successfully verified signature, but with untrusted public key
- public static final int SIGNATURE_SUCCESS_UNTRUSTED = 3;
+ // successfully verified signature, but with certified public key
+ public static final int SIGNATURE_SUCCESS_UNCERTIFIED = 3;
int status;
boolean signatureOnly;
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpApi.java b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpApi.java
new file mode 100644
index 000000000..f121c345d
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpApi.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp.util;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import org.openintents.openpgp.IOpenPgpService;
+import org.openintents.openpgp.OpenPgpError;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class OpenPgpApi {
+
+ IOpenPgpService mService;
+ Context mContext;
+
+ private static final int OPERATION_SIGN = 0;
+ private static final int OPERATION_ENCRYPT = 1;
+ private static final int OPERATION_SIGN_ENCRYPT = 2;
+ private static final int OPERATION_DECRYPT_VERIFY = 3;
+ private static final int OPERATION_GET_KEY_IDS = 4;
+
+ public OpenPgpApi(Context context, IOpenPgpService service) {
+ this.mContext = context;
+ this.mService = service;
+ }
+
+ public Bundle sign(InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_SIGN, new Bundle(), is, os);
+ }
+
+ public Bundle sign(Bundle params, InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_SIGN, params, is, os);
+ }
+
+ public void sign(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
+ executeApiAsync(OPERATION_SIGN, params, is, os, callback);
+ }
+
+ public Bundle encrypt(InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_ENCRYPT, new Bundle(), is, os);
+ }
+
+ public Bundle encrypt(Bundle params, InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_ENCRYPT, params, is, os);
+ }
+
+ public void encrypt(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
+ executeApiAsync(OPERATION_ENCRYPT, params, is, os, callback);
+ }
+
+ public Bundle signAndEncrypt(InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_SIGN_ENCRYPT, new Bundle(), is, os);
+ }
+
+ public Bundle signAndEncrypt(Bundle params, InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_SIGN_ENCRYPT, params, is, os);
+ }
+
+ public void signAndEncrypt(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
+ executeApiAsync(OPERATION_SIGN_ENCRYPT, params, is, os, callback);
+ }
+
+ public Bundle decryptAndVerify(InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_DECRYPT_VERIFY, new Bundle(), is, os);
+ }
+
+ public Bundle decryptAndVerify(Bundle params, InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_DECRYPT_VERIFY, params, is, os);
+ }
+
+ public void decryptAndVerify(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
+ executeApiAsync(OPERATION_DECRYPT_VERIFY, params, is, os, callback);
+ }
+
+ public Bundle getKeyIds(Bundle params) {
+ return executeApi(OPERATION_GET_KEY_IDS, params, null, null);
+ }
+
+ public interface IOpenPgpCallback {
+ void onReturn(final Bundle result);
+ }
+
+ private class OpenPgpAsyncTask extends AsyncTask<Void, Integer, Bundle> {
+ int operationId;
+ Bundle params;
+ InputStream is;
+ OutputStream os;
+ IOpenPgpCallback callback;
+
+ private OpenPgpAsyncTask(int operationId, Bundle params, InputStream is, OutputStream os, IOpenPgpCallback callback) {
+ this.operationId = operationId;
+ this.params = params;
+ this.is = is;
+ this.os = os;
+ this.callback = callback;
+ }
+
+ @Override
+ protected Bundle doInBackground(Void... unused) {
+ return executeApi(operationId, params, is, os);
+ }
+
+ protected void onPostExecute(Bundle result) {
+ callback.onReturn(result);
+ }
+
+ }
+
+ private void executeApiAsync(int operationId, Bundle params, InputStream is, OutputStream os, IOpenPgpCallback callback) {
+ new OpenPgpAsyncTask(operationId, params, is, os, callback).execute((Void[]) null);
+ }
+
+ private Bundle executeApi(int operationId, Bundle params, InputStream is, OutputStream os) {
+ try {
+ params.putInt(OpenPgpConstants.PARAMS_API_VERSION, OpenPgpConstants.API_VERSION);
+
+ Bundle result = null;
+
+ if (operationId == OPERATION_GET_KEY_IDS) {
+ result = mService.getKeyIds(params);
+ return result;
+ } else {
+ // send the input and output pfds
+ ParcelFileDescriptor input = ParcelFileDescriptorUtil.pipeFrom(is,
+ new ParcelFileDescriptorUtil.IThreadListener() {
+
+ @Override
+ public void onThreadFinished(Thread thread) {
+ Log.d(OpenPgpConstants.TAG, "Copy to service finished");
+ }
+ });
+ ParcelFileDescriptor output = ParcelFileDescriptorUtil.pipeTo(os,
+ new ParcelFileDescriptorUtil.IThreadListener() {
+
+ @Override
+ public void onThreadFinished(Thread thread) {
+ Log.d(OpenPgpConstants.TAG, "Service finished writing!");
+ }
+ });
+
+
+ // blocks until result is ready
+ switch (operationId) {
+ case OPERATION_SIGN:
+ result = mService.sign(params, input, output);
+ break;
+ case OPERATION_ENCRYPT:
+ result = mService.encrypt(params, input, output);
+ break;
+ case OPERATION_SIGN_ENCRYPT:
+ result = mService.signAndEncrypt(params, input, output);
+ break;
+ case OPERATION_DECRYPT_VERIFY:
+ result = mService.decryptAndVerify(params, input, output);
+ break;
+ }
+ // close() is required to halt the TransferThread
+ output.close();
+
+ // set class loader to current context to allow unparcelling
+ // of OpenPgpError and OpenPgpSignatureResult
+ // http://stackoverflow.com/a/3806769
+ result.setClassLoader(mContext.getClassLoader());
+
+ return result;
+ }
+ } catch (Exception e) {
+ Log.e(OpenPgpConstants.TAG, "Exception", e);
+ Bundle result = new Bundle();
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
+ result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
+ new OpenPgpError(OpenPgpError.CLIENT_SIDE_ERROR, e.getMessage()));
+ return result;
+ }
+ }
+
+
+}
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpConstants.java b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpConstants.java
new file mode 100644
index 000000000..263b42aaa
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpConstants.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp.util;
+
+public class OpenPgpConstants {
+
+ public static final String TAG = "OpenPgp API";
+
+ public static final int API_VERSION = 1;
+ public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService";
+
+
+ /* Bundle params */
+ public static final String PARAMS_API_VERSION = "api_version";
+ // request ASCII Armor for output
+ // OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
+ public static final String PARAMS_REQUEST_ASCII_ARMOR = "ascii_armor";
+ // (for encrypt method)
+ public static final String PARAMS_USER_IDS = "user_ids";
+ public static final String PARAMS_KEY_IDS = "key_ids";
+ // optional parameter:
+ public static final String PARAMS_PASSPHRASE = "passphrase";
+
+ /* Service Bundle returns */
+ public static final String RESULT_CODE = "result_code";
+ public static final String RESULT_SIGNATURE = "signature";
+ public static final String RESULT_ERRORS = "error";
+ public static final String RESULT_INTENT = "intent";
+
+ // get actual error object from RESULT_ERRORS
+ public static final int RESULT_CODE_ERROR = 0;
+ // success!
+ public static final int RESULT_CODE_SUCCESS = 1;
+ // executeServiceMethod intent and do it again with params from intent
+ public static final int RESULT_CODE_USER_INTERACTION_REQUIRED = 2;
+
+ /* PendingIntent returns */
+ public static final String PI_RESULT_PARAMS = "params";
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpListPreference.java b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpListPreference.java
index 4ddd97485..034186a3a 100644
--- a/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpListPreference.java
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpListPreference.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,18 +14,14 @@
* limitations under the License.
*/
-package org.openintents.openpgp;
-
-import java.util.ArrayList;
-import java.util.List;
+package org.openintents.openpgp.util;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
+import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.preference.DialogPreference;
import android.util.AttributeSet;
@@ -35,33 +31,21 @@ import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.TextView;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.sufficientlysecure.keychain.api.R;
+
+/**
+ * Does not extend ListPreference, but is very similar to it!
+ * http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/preference/ListPreference.java/?v=source
+ */
public class OpenPgpListPreference extends DialogPreference {
- ArrayList<OpenPgpProviderEntry> mProviderList = new ArrayList<OpenPgpProviderEntry>();
+ private ArrayList<OpenPgpProviderEntry> mProviderList = new ArrayList<OpenPgpProviderEntry>();
private String mSelectedPackage;
public OpenPgpListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
-
- List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(
- new Intent(OpenPgpConstants.SERVICE_INTENT), PackageManager.GET_META_DATA);
- if (!resInfo.isEmpty()) {
- for (ResolveInfo resolveInfo : resInfo) {
- if (resolveInfo.serviceInfo == null)
- continue;
-
- String packageName = resolveInfo.serviceInfo.packageName;
- String simpleName = String.valueOf(resolveInfo.serviceInfo.loadLabel(context
- .getPackageManager()));
- Drawable icon = resolveInfo.serviceInfo.loadIcon(context.getPackageManager());
-
- // get api version
- ServiceInfo si = resolveInfo.serviceInfo;
- int apiVersion = si.metaData.getInt("api_version");
-
- mProviderList.add(new OpenPgpProviderEntry(packageName, simpleName, icon,
- apiVersion));
- }
- }
}
public OpenPgpListPreference(Context context) {
@@ -69,20 +53,42 @@ public class OpenPgpListPreference extends DialogPreference {
}
/**
- * Can be used to add "no selection"
- *
+ * Public method to add new entries for legacy applications
+ *
* @param packageName
* @param simpleName
* @param icon
*/
- public void addProvider(int position, String packageName, String simpleName, Drawable icon,
- int apiVersion) {
- mProviderList.add(position, new OpenPgpProviderEntry(packageName, simpleName, icon,
- apiVersion));
+ public void addProvider(int position, String packageName, String simpleName, Drawable icon) {
+ mProviderList.add(position, new OpenPgpProviderEntry(packageName, simpleName, icon));
}
@Override
protected void onPrepareDialogBuilder(Builder builder) {
+
+ // get providers
+ mProviderList.clear();
+ Intent intent = new Intent(OpenPgpConstants.SERVICE_INTENT);
+ List<ResolveInfo> resInfo = getContext().getPackageManager().queryIntentServices(intent, 0);
+ if (!resInfo.isEmpty()) {
+ for (ResolveInfo resolveInfo : resInfo) {
+ if (resolveInfo.serviceInfo == null)
+ continue;
+
+ String packageName = resolveInfo.serviceInfo.packageName;
+ String simpleName = String.valueOf(resolveInfo.serviceInfo.loadLabel(getContext()
+ .getPackageManager()));
+ Drawable icon = resolveInfo.serviceInfo.loadIcon(getContext().getPackageManager());
+
+ mProviderList.add(new OpenPgpProviderEntry(packageName, simpleName, icon));
+ }
+ }
+
+ // add "none"-entry
+ mProviderList.add(0, new OpenPgpProviderEntry("",
+ getContext().getString(R.string.openpgp_list_preference_none),
+ getContext().getResources().getDrawable(R.drawable.ic_action_cancel_launchersize)));
+
// Init ArrayAdapter with OpenPGP Providers
ListAdapter adapter = new ArrayAdapter<OpenPgpProviderEntry>(getContext(),
android.R.layout.select_dialog_singlechoice, android.R.id.text1, mProviderList) {
@@ -99,15 +105,6 @@ public class OpenPgpListPreference extends DialogPreference {
int dp10 = (int) (10 * getContext().getResources().getDisplayMetrics().density + 0.5f);
tv.setCompoundDrawablePadding(dp10);
- // disable if it has the wrong api_version
- if (mProviderList.get(position).apiVersion == OpenPgpConstants.REQUIRED_API_VERSION) {
- tv.setEnabled(true);
- } else {
- tv.setEnabled(false);
- tv.setText(tv.getText() + " (API v" + mProviderList.get(position).apiVersion
- + ", needs v" + OpenPgpConstants.REQUIRED_API_VERSION + ")");
- }
-
return v;
}
};
@@ -169,6 +166,16 @@ public class OpenPgpListPreference extends DialogPreference {
return getEntryByValue(mSelectedPackage);
}
+ @Override
+ protected Object onGetDefaultValue(TypedArray a, int index) {
+ return a.getString(index);
+ }
+
+ @Override
+ protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+ setValue(restoreValue ? getPersistedString(mSelectedPackage) : (String) defaultValue);
+ }
+
public String getEntryByValue(String packageName) {
for (OpenPgpProviderEntry app : mProviderList) {
if (app.packageName.equals(packageName)) {
@@ -183,14 +190,11 @@ public class OpenPgpListPreference extends DialogPreference {
private String packageName;
private String simpleName;
private Drawable icon;
- private int apiVersion;
- public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon,
- int apiVersion) {
+ public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon) {
this.packageName = packageName;
this.simpleName = simpleName;
this.icon = icon;
- this.apiVersion = apiVersion;
}
@Override
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpServiceConnection.java b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpServiceConnection.java
index f7ba06aaf..c80656c52 100644
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpServiceConnection.java
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpServiceConnection.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.openintents.openpgp;
+package org.openintents.openpgp.util;
import org.openintents.openpgp.IOpenPgpService;
@@ -23,18 +23,17 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
-import android.util.Log;
public class OpenPgpServiceConnection {
private Context mApplicationContext;
- private IOpenPgpService mService;
private boolean mBound;
- private String mCryptoProviderPackageName;
+ private IOpenPgpService mService;
+ private String mProviderPackageName;
- public OpenPgpServiceConnection(Context context, String cryptoProviderPackageName) {
+ public OpenPgpServiceConnection(Context context, String providerPackageName) {
this.mApplicationContext = context.getApplicationContext();
- this.mCryptoProviderPackageName = cryptoProviderPackageName;
+ this.mProviderPackageName = providerPackageName;
}
public IOpenPgpService getService() {
@@ -45,49 +44,45 @@ public class OpenPgpServiceConnection {
return mBound;
}
- private ServiceConnection mCryptoServiceConnection = new ServiceConnection() {
+ private ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IOpenPgpService.Stub.asInterface(service);
- Log.d(OpenPgpConstants.TAG, "connected to service");
mBound = true;
}
public void onServiceDisconnected(ComponentName name) {
mService = null;
- Log.d(OpenPgpConstants.TAG, "disconnected from service");
mBound = false;
}
};
/**
- * If not already bound, bind!
- *
+ * If not already bound, bind to service!
+ *
* @return
*/
public boolean bindToService() {
- if (mService == null && !mBound) { // if not already connected
+ // if not already bound...
+ if (mService == null && !mBound) {
try {
- Log.d(OpenPgpConstants.TAG, "not bound yet");
-
Intent serviceIntent = new Intent();
serviceIntent.setAction(IOpenPgpService.class.getName());
- serviceIntent.setPackage(mCryptoProviderPackageName);
- mApplicationContext.bindService(serviceIntent, mCryptoServiceConnection,
+ // NOTE: setPackage is very important to restrict the intent to this provider only!
+ serviceIntent.setPackage(mProviderPackageName);
+ mApplicationContext.bindService(serviceIntent, mServiceConnection,
Context.BIND_AUTO_CREATE);
return true;
} catch (Exception e) {
- Log.d(OpenPgpConstants.TAG, "Exception on binding", e);
return false;
}
} else {
- Log.d(OpenPgpConstants.TAG, "already bound");
return true;
}
}
public void unbindFromService() {
- mApplicationContext.unbindService(mCryptoServiceConnection);
+ mApplicationContext.unbindService(mServiceConnection);
}
}
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpUtils.java b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpUtils.java
new file mode 100644
index 000000000..ffecaceba
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpUtils.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp.util;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+
+public class OpenPgpUtils {
+
+ 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 int PARSE_RESULT_NO_PGP = -1;
+ public static final int PARSE_RESULT_MESSAGE = 0;
+ public static final int PARSE_RESULT_SIGNED_MESSAGE = 1;
+
+ public static int parseMessage(String message) {
+ Matcher matcherSigned = PGP_SIGNED_MESSAGE.matcher(message);
+ Matcher matcherMessage = PGP_MESSAGE.matcher(message);
+
+ if (matcherMessage.matches()) {
+ return PARSE_RESULT_MESSAGE;
+ } else if (matcherSigned.matches()) {
+ return PARSE_RESULT_SIGNED_MESSAGE;
+ } else {
+ return PARSE_RESULT_NO_PGP;
+ }
+ }
+
+ public static boolean isAvailable(Context context) {
+ Intent intent = new Intent(OpenPgpConstants.SERVICE_INTENT);
+ List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(intent, 0);
+ if (!resInfo.isEmpty()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+}
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java
new file mode 100644
index 000000000..3569caf5b
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * 2013 Flow (http://stackoverflow.com/questions/18212152/transfer-inputstream-to-another-service-across-process-boundaries-with-parcelf)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.openintents.openpgp.util;
+
+import android.os.ParcelFileDescriptor;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class ParcelFileDescriptorUtil {
+
+ public interface IThreadListener {
+ void onThreadFinished(final Thread thread);
+ }
+
+ public static ParcelFileDescriptor pipeFrom(InputStream inputStream, IThreadListener listener)
+ throws IOException {
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ ParcelFileDescriptor readSide = pipe[0];
+ ParcelFileDescriptor writeSide = pipe[1];
+
+ // start the transfer thread
+ new TransferThread(inputStream, new ParcelFileDescriptor.AutoCloseOutputStream(writeSide),
+ listener)
+ .start();
+
+ return readSide;
+ }
+
+ public static ParcelFileDescriptor pipeTo(OutputStream outputStream, IThreadListener listener)
+ throws IOException {
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ ParcelFileDescriptor readSide = pipe[0];
+ ParcelFileDescriptor writeSide = pipe[1];
+
+ // start the transfer thread
+ new TransferThread(new ParcelFileDescriptor.AutoCloseInputStream(readSide), outputStream,
+ listener)
+ .start();
+
+ return writeSide;
+ }
+
+ static class TransferThread extends Thread {
+ final InputStream mIn;
+ final OutputStream mOut;
+ final IThreadListener mListener;
+
+ TransferThread(InputStream in, OutputStream out, IThreadListener listener) {
+ super("ParcelFileDescriptor Transfer Thread");
+ mIn = in;
+ mOut = out;
+ mListener = listener;
+ setDaemon(true);
+ }
+
+ @Override
+ public void run() {
+ byte[] buf = new byte[1024];
+ int len;
+
+ try {
+ while ((len = mIn.read(buf)) > 0) {
+ mOut.write(buf, 0, len);
+ }
+ mOut.flush(); // just to be safe
+ } catch (IOException e) {
+ //Log.e(OpenPgpConstants.TAG, "TransferThread" + getId() + ": writing failed", e);
+ } finally {
+ try {
+ mIn.close();
+ } catch (IOException e) {
+ //Log.e(OpenPgpConstants.TAG, "TransferThread" + getId(), e);
+ }
+ try {
+ mOut.close();
+ } catch (IOException e) {
+ //Log.e(OpenPgpConstants.TAG, "TransferThread" + getId(), e);
+ }
+ }
+ if (mListener != null) {
+ //Log.d(OpenPgpConstants.TAG, "TransferThread " + getId() + " finished!");
+ mListener.onThreadFinished(this);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/sufficientlysecure/keychain/api/OpenKeychainIntents.java b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/sufficientlysecure/keychain/api/OpenKeychainIntents.java
new file mode 100644
index 000000000..15aceb534
--- /dev/null
+++ b/OpenPGP-Keychain-API/libraries/keychain-api-library/src/org/sufficientlysecure/keychain/api/OpenKeychainIntents.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.api;
+
+public class OpenKeychainIntents {
+
+ public static final String ENCRYPT = "org.sufficientlysecure.keychain.action.ENCRYPT";
+ public static final String ENCRYPT_EXTRA_TEXT = "text"; // String
+ public static final String ENCRYPT_ASCII_ARMOR = "ascii_armor"; // boolean
+
+ public static final String DECRYPT = "org.sufficientlysecure.keychain.action.DECRYPT";
+ public static final String DECRYPT_EXTRA_TEXT = "text"; // String
+
+ public static final String IMPORT_KEY = "org.sufficientlysecure.keychain.action.IMPORT_KEY";
+ public static final String IMPORT_KEY_EXTRA_KEY_BYTES = "key_bytes"; // byte[]
+
+ public static final String IMPORT_KEY_FROM_KEYSERVER = "org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_KEYSERVER";
+ public static final String IMPORT_KEY_FROM_KEYSERVER_QUERY = "query"; // String
+ public static final String IMPORT_KEY_FROM_KEYSERVER_FINGERPRINT = "fingerprint"; // String
+
+ public static final String IMPORT_KEY_FROM_QR_CODE = "org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE";
+
+}
diff --git a/OpenPGP-Keychain-API/settings.gradle b/OpenPGP-Keychain-API/settings.gradle
new file mode 100644
index 000000000..4df1d7b4f
--- /dev/null
+++ b/OpenPGP-Keychain-API/settings.gradle
@@ -0,0 +1,2 @@
+include ':example-app'
+include ':libraries:keychain-api-library' \ No newline at end of file
diff --git a/OpenPGP-Keychain/build.gradle b/OpenPGP-Keychain/build.gradle
index 44f5ca280..1454b80e7 100644
--- a/OpenPGP-Keychain/build.gradle
+++ b/OpenPGP-Keychain/build.gradle
@@ -3,6 +3,7 @@ apply plugin: 'android'
dependencies {
compile 'com.android.support:support-v4:19.0.1'
compile 'com.android.support:appcompat-v7:19.0.1'
+ compile project(':libraries:keychain-api-library')
compile project(':libraries:HtmlTextView')
compile project(':libraries:StickyListHeaders:library')
compile project(':libraries:AndroidBootstrap')
@@ -12,6 +13,7 @@ dependencies {
compile project(':libraries:spongycastle:pg')
compile project(':libraries:spongycastle:pkix')
compile project(':libraries:spongycastle:prov')
+ compile project(':libraries:Android-AppMsg:library')
}
android {
@@ -19,7 +21,7 @@ android {
buildToolsVersion "19.0.1"
defaultConfig {
- minSdkVersion 8
+ minSdkVersion 9
targetSdkVersion 19
}
diff --git a/OpenPGP-Keychain/src/main/AndroidManifest.xml b/OpenPGP-Keychain/src/main/AndroidManifest.xml
index 3ef04e4f6..480acdbd8 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="22001"
- android:versionName="2.2">
+ android:versionCode="23100"
+ android:versionName="2.3.1">
<!--
General remarks
@@ -30,7 +30,7 @@
-->
<uses-sdk
- android:minSdkVersion="8"
+ android:minSdkVersion="9"
android:targetSdkVersion="19" />
<uses-feature
@@ -153,12 +153,19 @@
android:windowSoftInputMode="stateHidden">
<!-- Keychain's own Actions -->
+ <!-- ENCRYPT with text as extra -->
<intent-filter>
<action android:name="org.sufficientlysecure.keychain.action.ENCRYPT" />
<category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <!-- ENCRYPT with data Uri -->
+ <intent-filter>
+ <action android:name="org.sufficientlysecure.keychain.action.ENCRYPT" />
- <data android:mimeType="*/*" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <!-- TODO: accept other schemes! -->
+ <data android:scheme="file" />
</intent-filter>
<!-- Android's Send Action -->
<intent-filter android:label="@string/intent_send_encrypt">
@@ -175,13 +182,40 @@
android:label="@string/title_decrypt"
android:windowSoftInputMode="stateHidden">
+ <!--&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" />-->
+
+ <!--<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" />-->
+ <!--</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" />-->
+
+ <!--<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" />-->
+ <!--</intent-filter>-->
<!-- Keychain's own Actions -->
+ <!-- DECRYPT with text as extra -->
<intent-filter>
<action android:name="org.sufficientlysecure.keychain.action.DECRYPT" />
<category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <!-- DECRYPT with data Uri -->
+ <intent-filter>
+ <action android:name="org.sufficientlysecure.keychain.action.DECRYPT" />
- <data android:mimeType="*/*" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <!-- TODO: accept other schemes! -->
+ <data android:scheme="file" />
</intent-filter>
<!-- Android's Send Action -->
<intent-filter android:label="@string/intent_send_decrypt">
@@ -249,17 +283,19 @@
android:label="@string/title_key_server_preference"
android:windowSoftInputMode="stateHidden" />
<activity
- android:name=".ui.SignKeyActivity"
+ android:name=".ui.CertifyKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
- android:label="@string/title_sign_key" />
+ android:label="@string/title_certify_key" />
<activity
android:name=".ui.ImportKeysActivity"
+ android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_import_keys"
android:launchMode="singleTop"
android:windowSoftInputMode="stateHidden">
- <!-- Handle URIs with fingerprints when scanning directly from Barcode Scanner -->
- <intent-filter>
+ <!-- VIEW with fingerprint scheme:
+ Handle URIs with fingerprints when scanning directly from Barcode Scanner -->
+ <intent-filter android:label="@string/intent_import_key">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
@@ -272,31 +308,25 @@
<data android:scheme="OpenPGP4Fpr" />
<data android:scheme="OpenPGP4fpr" />
</intent-filter>
- <!-- Handle NFC tags detected from outside our application -->
- <intent-filter>
- <action android:name="android.nfc.action.NDEF_DISCOVERED" />
-
- <category android:name="android.intent.category.DEFAULT" />
- <!-- mime type as defined in http://tools.ietf.org/html/rfc3156, section 7 -->
- <data android:mimeType="application/pgp-keys" />
- </intent-filter>
- <!-- Keychain's own Actions -->
+ <!-- VIEW with mimeType: Allows to import keys (attached to emails) from email apps -->
<intent-filter android:label="@string/intent_import_key">
- <action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY" />
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="*/*" />
+ <!-- mime type as defined in http://tools.ietf.org/html/rfc3156 -->
+ <data android:mimeType="application/pgp-keys" />
</intent-filter>
- <!-- IMPORT again without mimeType to also allow data only without filename -->
- <intent-filter android:label="@string/intent_import_key">
- <action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY" />
- <action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE" />
- <action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_KEY_SERVER" />
+ <!-- NFC: Handle NFC tags detected from outside our application -->
+ <intent-filter>
+ <action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
+ <!-- mime type as defined in http://tools.ietf.org/html/rfc3156 -->
+ <data android:mimeType="application/pgp-keys" />
</intent-filter>
- <!-- Linking "Import key" to file types -->
+ <!-- VIEW with file endings: *.gpg (e.g. to import from OI File Manager) -->
<intent-filter android:label="@string/intent_import_key">
<action android:name="android.intent.action.VIEW" />
@@ -317,6 +347,7 @@
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
</intent-filter>
+ <!-- VIEW with file endings: *.asc -->
<intent-filter android:label="@string/intent_import_key">
<action android:name="android.intent.action.VIEW" />
@@ -338,6 +369,31 @@
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
</intent-filter>
+ <!-- Keychain's own Actions -->
+ <!-- IMPORT_KEY with files TODO: does this work? -->
+ <intent-filter android:label="@string/intent_import_key">
+ <action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+
+ <data android:mimeType="*/*" />
+ </intent-filter>
+ <!-- IMPORT_KEY with mimeType 'application/pgp-keys' -->
+ <intent-filter>
+ <action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+ <!-- mime type as defined in http://tools.ietf.org/html/rfc3156, section 7 -->
+ <data android:mimeType="application/pgp-keys" />
+ </intent-filter>
+ <!-- IMPORT_KEY without mimeType to allow import with extras Bundle -->
+ <intent-filter android:label="@string/intent_import_key">
+ <action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY" />
+ <action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE" />
+ <action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_KEYSERVER" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
</activity>
<activity
android:name=".ui.HelpActivity"
@@ -361,10 +417,9 @@
<activity
android:name="org.sufficientlysecure.keychain.service.remote.RemoteServiceActivity"
android:exported="false"
- android:label="@string/app_name"
- android:launchMode="singleTop"
- android:process=":remote_api"
- android:taskAffinity=":remote_api" />
+ android:label="@string/app_name" />
+ <!--android:launchMode="singleTop"-->
+ <!--android:process=":remote_api"-->
<activity
android:name="org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
@@ -391,19 +446,19 @@
</service>
<!-- Extended Remote API -->
- <service
- android:name="org.sufficientlysecure.keychain.service.remote.ExtendedApiService"
- android:enabled="true"
- android:exported="true"
- android:process=":remote_api">
- <intent-filter>
- <action android:name="org.sufficientlysecure.keychain.service.remote.IExtendedApiService" />
- </intent-filter>
-
- <meta-data
- android:name="api_version"
- android:value="1" />
- </service>
+ <!--<service-->
+ <!--android:name="org.sufficientlysecure.keychain.service.remote.ExtendedApiService"-->
+ <!--android:enabled="true"-->
+ <!--android:exported="true"-->
+ <!--android:process=":remote_api">-->
+ <!--<intent-filter>-->
+ <!--<action android:name="org.sufficientlysecure.keychain.service.remote.IExtendedApiService" />-->
+ <!--</intent-filter>-->
+
+ <!--<meta-data-->
+ <!--android:name="api_version"-->
+ <!--android:value="1" />-->
+ <!--</service>-->
<!-- TODO: authority! Make this API with content provider uris -->
<!-- <provider -->
diff --git a/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/IOpenPgpCallback.aidl b/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/IOpenPgpCallback.aidl
deleted file mode 100644
index ba41de1ba..000000000
--- a/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/IOpenPgpCallback.aidl
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-import org.openintents.openpgp.OpenPgpData;
-import org.openintents.openpgp.OpenPgpSignatureResult;
-import org.openintents.openpgp.OpenPgpError;
-
-interface IOpenPgpCallback {
-
- /**
- * onSuccess returns on successful OpenPGP operations.
- *
- * @param output
- * contains resulting output (decrypted content (when input was encrypted)
- * or content without signature (when input was signed-only))
- * @param signatureResult
- * signatureResult is only non-null if decryptAndVerify() was called and the content
- * was encrypted or signed-and-encrypted.
- */
- oneway void onSuccess(in OpenPgpData output, in OpenPgpSignatureResult signatureResult);
-
- /**
- * onError returns on errors or when allowUserInteraction was set to false, but user interaction
- * was required execute an OpenPGP operation.
- *
- * @param error
- * See OpenPgpError class for more information.
- */
- oneway void onError(in OpenPgpError error);
-} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/IOpenPgpKeyIdsCallback.aidl b/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/IOpenPgpKeyIdsCallback.aidl
deleted file mode 100644
index 4ca356fad..000000000
--- a/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/IOpenPgpKeyIdsCallback.aidl
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-import org.openintents.openpgp.OpenPgpError;
-
-interface IOpenPgpKeyIdsCallback {
-
- /**
- * onSuccess returns on successful getKeyIds operations.
- *
- * @param keyIds
- * returned key ids
- */
- oneway void onSuccess(in long[] keyIds);
-
- /**
- * onError returns on errors or when allowUserInteraction was set to false, but user interaction
- * was required execute an OpenPGP operation.
- *
- * @param error
- * See OpenPgpError class for more information.
- */
- oneway void onError(in OpenPgpError error);
-} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/IOpenPgpService.aidl b/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/IOpenPgpService.aidl
deleted file mode 100644
index 8f9e8a0fd..000000000
--- a/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/IOpenPgpService.aidl
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-import org.openintents.openpgp.OpenPgpData;
-import org.openintents.openpgp.IOpenPgpCallback;
-import org.openintents.openpgp.IOpenPgpKeyIdsCallback;
-
-/**
- * All methods are oneway, which means they are asynchronous and non-blocking.
- * Results are returned to the callback, which has to be implemented on client side.
- */
-interface IOpenPgpService {
-
- /**
- * Sign
- *
- * After successful signing, callback's onSuccess will contain the resulting output.
- *
- * @param input
- * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
- * @param output
- * Request output format by defining OpenPgpData object
- *
- * new OpenPgpData(OpenPgpData.TYPE_STRING)
- * Returns as String
- * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
- * new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
- * Returns as byte[]
- * new OpenPgpData(uri)
- * Writes output to given Uri
- * new OpenPgpData(fileDescriptor)
- * Writes output to given ParcelFileDescriptor
- * @param callback
- * Callback where to return results
- */
- oneway void sign(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
-
- /**
- * Encrypt
- *
- * After successful encryption, callback's onSuccess will contain the resulting output.
- *
- * @param input
- * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
- * @param output
- * Request output format by defining OpenPgpData object
- *
- * new OpenPgpData(OpenPgpData.TYPE_STRING)
- * Returns as String
- * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
- * new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
- * Returns as byte[]
- * new OpenPgpData(uri)
- * Writes output to given Uri
- * new OpenPgpData(fileDescriptor)
- * Writes output to given ParcelFileDescriptor
- * @param keyIds
- * Key Ids of recipients. Can be retrieved with getKeyIds()
- * @param callback
- * Callback where to return results
- */
- oneway void encrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
-
- /**
- * Sign then encrypt
- *
- * After successful signing and encryption, callback's onSuccess will contain the resulting output.
- *
- * @param input
- * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
- * @param output
- * Request output format by defining OpenPgpData object
- *
- * new OpenPgpData(OpenPgpData.TYPE_STRING)
- * Returns as String
- * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
- * new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
- * Returns as byte[]
- * new OpenPgpData(uri)
- * Writes output to given Uri
- * new OpenPgpData(fileDescriptor)
- * Writes output to given ParcelFileDescriptor
- * @param keyIds
- * Key Ids of recipients. Can be retrieved with getKeyIds()
- * @param callback
- * Callback where to return results
- */
- oneway void signAndEncrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
-
- /**
- * Decrypts and verifies given input bytes. This methods handles encrypted-only, signed-and-encrypted,
- * and also signed-only input.
- *
- * After successful decryption/verification, callback's onSuccess will contain the resulting output.
- * The signatureResult in onSuccess is only non-null if signed-and-encrypted or signed-only inputBytes were given.
- *
- * @param input
- * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
- * @param output
- * Request output format by defining OpenPgpData object
- *
- * new OpenPgpData(OpenPgpData.TYPE_STRING)
- * Returns as String
- * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
- * new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
- * Returns as byte[]
- * new OpenPgpData(uri)
- * Writes output to given Uri
- * new OpenPgpData(fileDescriptor)
- * Writes output to given ParcelFileDescriptor
- * @param callback
- * Callback where to return results
- */
- oneway void decryptAndVerify(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
-
- /**
- * Get available key ids based on given user ids
- *
- * @param ids
- * User Ids (emails) of recipients OR key ids
- * @param allowUserInteraction
- * Enable user interaction to lookup and import unknown keys
- * @param callback
- * Callback where to return results (different type than callback in other functions!)
- */
- oneway void getKeyIds(in String[] ids, in boolean allowUserInteraction, in IOpenPgpKeyIdsCallback callback);
-
-} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/OpenPgpData.aidl b/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/OpenPgpData.aidl
deleted file mode 100644
index 3711e4fb4..000000000
--- a/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/OpenPgpData.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-// Declare OpenPgpData so AIDL can find it and knows that it implements the parcelable protocol.
-parcelable OpenPgpData; \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/OpenPgpError.aidl b/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/OpenPgpError.aidl
deleted file mode 100644
index 7a6bed1e6..000000000
--- a/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/OpenPgpError.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-// Declare OpenPgpError so AIDL can find it and knows that it implements the parcelable protocol.
-parcelable OpenPgpError; \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/OpenPgpSignatureResult.aidl b/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/OpenPgpSignatureResult.aidl
deleted file mode 100644
index e246792d0..000000000
--- a/OpenPGP-Keychain/src/main/aidl/org/openintents/openpgp/OpenPgpSignatureResult.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-// Declare OpenPgpSignatureResult so AIDL can find it and knows that it implements the parcelable protocol.
-parcelable OpenPgpSignatureResult; \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpConstants.java b/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpConstants.java
deleted file mode 100644
index b1ca1bfe6..000000000
--- a/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpConstants.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.openintents.openpgp;
-
-public class OpenPgpConstants {
-
- public static final String TAG = "OpenPgp API";
-
- public static final int REQUIRED_API_VERSION = 1;
- public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService";
-
-}
diff --git a/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpData.java b/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpData.java
deleted file mode 100644
index 6615c2146..000000000
--- a/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpData.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
-import android.os.Parcelable;
-
-public class OpenPgpData implements Parcelable {
- public static final int TYPE_STRING = 0;
- public static final int TYPE_BYTE_ARRAY = 1;
- public static final int TYPE_FILE_DESCRIPTOR = 2;
- public static final int TYPE_URI = 3;
-
- int type;
-
- String string;
- byte[] bytes = new byte[0];
- ParcelFileDescriptor fileDescriptor;
- Uri uri;
-
- public int getType() {
- return type;
- }
-
- public String getString() {
- return string;
- }
-
- public byte[] getBytes() {
- return bytes;
- }
-
- public ParcelFileDescriptor getFileDescriptor() {
- return fileDescriptor;
- }
-
- public Uri getUri() {
- return uri;
- }
-
- public OpenPgpData() {
-
- }
-
- /**
- * Not a real constructor. This can be used to define requested output type.
- *
- * @param type
- */
- public OpenPgpData(int type) {
- this.type = type;
- }
-
- public OpenPgpData(String string) {
- this.string = string;
- this.type = TYPE_STRING;
- }
-
- public OpenPgpData(byte[] bytes) {
- this.bytes = bytes;
- this.type = TYPE_BYTE_ARRAY;
- }
-
- public OpenPgpData(ParcelFileDescriptor fileDescriptor) {
- this.fileDescriptor = fileDescriptor;
- this.type = TYPE_FILE_DESCRIPTOR;
- }
-
- public OpenPgpData(Uri uri) {
- this.uri = uri;
- this.type = TYPE_URI;
- }
-
- public OpenPgpData(OpenPgpData b) {
- this.string = b.string;
- this.bytes = b.bytes;
- this.fileDescriptor = b.fileDescriptor;
- this.uri = b.uri;
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(type);
- dest.writeString(string);
- dest.writeInt(bytes.length);
- dest.writeByteArray(bytes);
- dest.writeParcelable(fileDescriptor, 0);
- dest.writeParcelable(uri, 0);
- }
-
- public static final Creator<OpenPgpData> CREATOR = new Creator<OpenPgpData>() {
- public OpenPgpData createFromParcel(final Parcel source) {
- OpenPgpData vr = new OpenPgpData();
- vr.type = source.readInt();
- vr.string = source.readString();
- vr.bytes = new byte[source.readInt()];
- source.readByteArray(vr.bytes);
- vr.fileDescriptor = source.readParcelable(ParcelFileDescriptor.class.getClassLoader());
- vr.fileDescriptor = source.readParcelable(Uri.class.getClassLoader());
- return vr;
- }
-
- public OpenPgpData[] newArray(final int size) {
- return new OpenPgpData[size];
- }
- };
-
-}
diff --git a/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpHelper.java b/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpHelper.java
deleted file mode 100644
index 7305c47ce..000000000
--- a/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpHelper.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.openintents.openpgp;
-
-import java.util.List;
-import java.util.regex.Pattern;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-
-public class OpenPgpHelper {
- private Context context;
-
- public static Pattern PGP_MESSAGE = Pattern.compile(
- ".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.DOTALL);
-
- public static Pattern PGP_SIGNED_MESSAGE = Pattern
- .compile(
- ".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
- Pattern.DOTALL);
-
- public OpenPgpHelper(Context context) {
- super();
- this.context = context;
- }
-
- public boolean isAvailable() {
- Intent intent = new Intent(OpenPgpConstants.SERVICE_INTENT);
- List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(intent, 0);
- if (!resInfo.isEmpty()) {
- return true;
- } else {
- return false;
- }
- }
-
-}
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 22f30cb1f..a1571e491 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java
@@ -78,7 +78,7 @@ public final class Id {
public static final int filename = 0x00007003;
// public static final int output_filename = 0x00007004;
public static final int key_server_preference = 0x00007005;
- public static final int look_up_key_id = 0x00007006;
+// public static final int look_up_key_id = 0x00007006;
public static final int export_to_server = 0x00007007;
public static final int import_from_qr_code = 0x00007008;
public static final int sign_key = 0x00007009;
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
new file mode 100644
index 000000000..fb97f3a5c
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -0,0 +1,782 @@
+/*
+ * 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");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import org.spongycastle.bcpg.ArmoredInputStream;
+import org.spongycastle.bcpg.SignatureSubpacketTags;
+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.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.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.ProviderHelper;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.util.InputData;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
+
+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.Iterator;
+
+/**
+ * This class uses a Builder pattern!
+ */
+public class PgpDecryptVerify {
+ private Context context;
+ private InputData data;
+ private OutputStream outStream;
+
+ private ProgressDialogUpdater progress;
+ boolean assumeSymmetric;
+ String passphrase;
+
+ private PgpDecryptVerify(Builder builder) {
+ // private Constructor can only be called from Builder
+ this.context = builder.context;
+ this.data = builder.data;
+ this.outStream = builder.outStream;
+
+ this.progress = builder.progress;
+ this.assumeSymmetric = builder.assumeSymmetric;
+ this.passphrase = builder.passphrase;
+ }
+
+ public static class Builder {
+ // mandatory parameter
+ private Context context;
+ private InputData data;
+ private OutputStream outStream;
+
+ // optional
+ private ProgressDialogUpdater progress = null;
+ private boolean assumeSymmetric = false;
+ private String passphrase = "";
+
+ public Builder(Context context, InputData data, OutputStream outStream) {
+ this.context = context;
+ this.data = data;
+ this.outStream = outStream;
+ }
+
+ public Builder progress(ProgressDialogUpdater progress) {
+ this.progress = progress;
+ return this;
+ }
+
+ public Builder assumeSymmetric(boolean assumeSymmetric) {
+ this.assumeSymmetric = assumeSymmetric;
+ return this;
+ }
+
+ public Builder passphrase(String passphrase) {
+ this.passphrase = passphrase;
+ return this;
+ }
+
+ public PgpDecryptVerify build() {
+ return new PgpDecryptVerify(this);
+ }
+ }
+
+ public void updateProgress(int message, int current, int total) {
+ if (progress != null) {
+ progress.setProgress(message, current, total);
+ }
+ }
+
+ public void updateProgress(int current, int total) {
+ if (progress != null) {
+ progress.setProgress(current, total);
+ }
+ }
+
+ 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
+ *
+ * @return
+ * @throws IOException
+ * @throws PgpGeneralException
+ * @throws PGPException
+ * @throws SignatureException
+ */
+ public Bundle execute()
+ throws IOException, PgpGeneralException, PGPException, SignatureException {
+
+ // automatically works with ascii armor input and binary
+ InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
+ if (in instanceof ArmoredInputStream) {
+ ArmoredInputStream aIn = (ArmoredInputStream) in;
+ // it is ascii armored
+ Log.d(Constants.TAG, "ASCII Armor Header Line: " + aIn.getArmorHeaderLine());
+
+ if (aIn.isClearText()) {
+ // a cleartext signature, verify it with the other method
+ return verifyCleartextSignature(aIn);
+ }
+ // else: ascii armored encryption! go on...
+ }
+
+ return decryptVerify(in);
+ }
+
+ /**
+ * Decrypt and/or verifies binary or ascii armored pgp
+ *
+ * @param in
+ * @return
+ * @throws IOException
+ * @throws PgpGeneralException
+ * @throws PGPException
+ * @throws SignatureException
+ */
+ private Bundle decryptVerify(InputStream in)
+ throws IOException, PgpGeneralException, PGPException, SignatureException {
+ Bundle returnData = new Bundle();
+
+ PGPObjectFactory pgpF = new PGPObjectFactory(in);
+ PGPEncryptedDataList enc;
+ Object o = pgpF.nextObject();
+
+ int currentProgress = 0;
+ updateProgress(R.string.progress_reading_data, currentProgress, 100);
+
+ if (o instanceof PGPEncryptedDataList) {
+ enc = (PGPEncryptedDataList) o;
+ } else {
+ enc = (PGPEncryptedDataList) pgpF.nextObject();
+ }
+
+ if (enc == null) {
+ throw new PgpGeneralException(context.getString(R.string.error_invalid_data));
+ }
+
+ InputStream clear;
+ PGPEncryptedData encryptedData;
+
+ currentProgress += 5;
+
+ // TODO: currently we always only look at the first known key or symmetric encryption,
+ // there might be more...
+ if (assumeSymmetric) {
+ PGPPBEEncryptedData pbe = null;
+ Iterator<?> it = enc.getEncryptedDataObjects();
+ // find secret key
+ while (it.hasNext()) {
+ Object obj = it.next();
+ if (obj instanceof PGPPBEEncryptedData) {
+ pbe = (PGPPBEEncryptedData) obj;
+ break;
+ }
+ }
+
+ if (pbe == null) {
+ throw new PgpGeneralException(
+ context.getString(R.string.error_no_symmetric_encryption_packet));
+ }
+
+ updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
+
+ PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build();
+ PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder(
+ digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ passphrase.toCharArray());
+
+ clear = pbe.getDataStream(decryptorFactory);
+
+ encryptedData = pbe;
+ 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(context, encData.getKeyID());
+ if (secretKey != null) {
+ pbe = encData;
+ break;
+ }
+ }
+ }
+
+ if (secretKey == null) {
+ throw new PgpGeneralException(context.getString(R.string.error_no_secret_key_found));
+ }
+
+ currentProgress += 5;
+ updateProgress(R.string.progress_extracting_key, currentProgress, 100);
+ PGPPrivateKey privateKey = null;
+ try {
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ passphrase.toCharArray());
+ privateKey = secretKey.extractPrivateKey(keyDecryptor);
+ } catch (PGPException e) {
+ throw new PGPException(context.getString(R.string.error_wrong_passphrase));
+ }
+ if (privateKey == null) {
+ throw new PgpGeneralException(
+ context.getString(R.string.error_could_not_extract_private_key));
+ }
+ currentProgress += 5;
+ updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
+
+ PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey);
+
+ clear = pbe.getDataStream(decryptorFactory);
+
+ encryptedData = pbe;
+ currentProgress += 5;
+ }
+
+ PGPObjectFactory plainFact = new PGPObjectFactory(clear);
+ Object dataChunk = plainFact.nextObject();
+ PGPOnePassSignature signature = null;
+ PGPPublicKey signatureKey = null;
+ int signatureIndex = -1;
+
+ if (dataChunk instanceof PGPCompressedData) {
+ updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
+
+ PGPObjectFactory fact = new PGPObjectFactory(
+ ((PGPCompressedData) dataChunk).getDataStream());
+ dataChunk = fact.nextObject();
+ plainFact = fact;
+ currentProgress += 10;
+ }
+
+ long signatureKeyId = 0;
+ if (dataChunk instanceof PGPOnePassSignatureList) {
+ updateProgress(R.string.progress_processing_signature, currentProgress, 100);
+
+ returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true);
+ PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
+ for (int i = 0; i < sigList.size(); ++i) {
+ signature = sigList.get(i);
+ signatureKey = ProviderHelper
+ .getPGPPublicKeyByKeyId(context, signature.getKeyID());
+ if (signatureKeyId == 0) {
+ signatureKeyId = signature.getKeyID();
+ }
+ if (signatureKey == null) {
+ signature = null;
+ } else {
+ signatureIndex = i;
+ signatureKeyId = signature.getKeyID();
+ String userId = null;
+ PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(
+ context, signatureKeyId);
+ if (signKeyRing != null) {
+ userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
+ }
+ returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId);
+ break;
+ }
+ }
+
+ returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId);
+
+ if (signature != null) {
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ signature.init(contentVerifierBuilderProvider, signatureKey);
+ } else {
+ returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true);
+ }
+
+ dataChunk = plainFact.nextObject();
+ currentProgress += 10;
+ }
+
+ if (dataChunk instanceof PGPSignatureList) {
+ dataChunk = plainFact.nextObject();
+ }
+
+ if (dataChunk instanceof PGPLiteralData) {
+ updateProgress(R.string.progress_decrypting, currentProgress, 100);
+
+ PGPLiteralData literalData = (PGPLiteralData) dataChunk;
+
+ byte[] buffer = new byte[1 << 16];
+ InputStream dataIn = literalData.getInputStream();
+
+ int startProgress = currentProgress;
+ int endProgress = 100;
+ if (signature != null) {
+ endProgress = 90;
+ } else if (encryptedData.isIntegrityProtected()) {
+ endProgress = 95;
+ }
+
+ int n;
+ // TODO: progress calculation is broken here! Try to rework it based on commented code!
+// int progress = 0;
+ long startPos = data.getStreamPosition();
+ while ((n = dataIn.read(buffer)) > 0) {
+ outStream.write(buffer, 0, n);
+// progress += n;
+ if (signature != null) {
+ try {
+ signature.update(buffer, 0, n);
+ } catch (SignatureException e) {
+ returnData
+ .putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false);
+ signature = null;
+ }
+ }
+ // TODO: dead code?!
+ // unknown size, but try to at least have a moving, slowing down progress bar
+// currentProgress = startProgress + (endProgress - startProgress) * progress
+// / (progress + 100000);
+ if (data.getSize() - startPos == 0) {
+ currentProgress = endProgress;
+ } else {
+ currentProgress = (int) (startProgress + (endProgress - startProgress)
+ * (data.getStreamPosition() - startPos) / (data.getSize() - startPos));
+ }
+ updateProgress(currentProgress, 100);
+ }
+
+ if (signature != null) {
+ updateProgress(R.string.progress_verifying_signature, 90, 100);
+
+ PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
+ PGPSignature messageSignature = signatureList.get(signatureIndex);
+
+ // these are not cleartext signatures!
+ returnData.putBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, false);
+
+ //Now check binding signatures
+ boolean keyBinding_isok = verifyKeyBinding(context, messageSignature, signatureKey);
+ boolean sig_isok = signature.verify(messageSignature);
+
+ returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, keyBinding_isok & sig_isok);
+ }
+ }
+
+ // TODO: test if this integrity really check works!
+ if (encryptedData.isIntegrityProtected()) {
+ updateProgress(R.string.progress_verifying_integrity, 95, 100);
+
+ if (encryptedData.verify()) {
+ // passed
+ Log.d(Constants.TAG, "Integrity verification: success!");
+ } else {
+ // failed
+ Log.d(Constants.TAG, "Integrity verification: failed!");
+ throw new PgpGeneralException(context.getString(R.string.error_integrity_check_failed));
+ }
+ } else {
+ // no integrity check
+ Log.e(Constants.TAG, "Encrypted data was not integrity protected!");
+ }
+
+ updateProgress(R.string.progress_done, 100, 100);
+ return returnData;
+ }
+
+ /**
+ * This method verifies cleartext signatures
+ * as defined in http://tools.ietf.org/html/rfc4880#section-7
+ * <p/>
+ * The method is heavily based on
+ * pg/src/main/java/org/spongycastle/openpgp/examples/ClearSignedFileProcessor.java
+ *
+ * @return
+ * @throws IOException
+ * @throws PgpGeneralException
+ * @throws PGPException
+ * @throws SignatureException
+ */
+ private Bundle verifyCleartextSignature(ArmoredInputStream aIn)
+ throws IOException, PgpGeneralException, PGPException, SignatureException {
+ Bundle returnData = new Bundle();
+ // cleartext signatures are never encrypted ;)
+ returnData.putBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, true);
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ updateProgress(R.string.progress_done, 0, 100);
+
+ ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
+ int lookAhead = readInputLine(lineOut, aIn);
+ byte[] lineSep = getLineSeparator();
+
+ byte[] line = lineOut.toByteArray();
+ out.write(line, 0, getLengthWithoutSeparator(line));
+ out.write(lineSep);
+
+ while (lookAhead != -1 && aIn.isClearText()) {
+ lookAhead = readInputLine(lineOut, lookAhead, aIn);
+ line = lineOut.toByteArray();
+ out.write(line, 0, getLengthWithoutSeparator(line));
+ out.write(lineSep);
+ }
+
+ out.close();
+
+ byte[] clearText = out.toByteArray();
+ outStream.write(clearText);
+
+ returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true);
+
+ updateProgress(R.string.progress_processing_signature, 60, 100);
+ PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
+
+ PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject();
+ if (sigList == null) {
+ throw new PgpGeneralException(context.getString(R.string.error_corrupt_data));
+ }
+ PGPSignature signature = null;
+ long signatureKeyId = 0;
+ PGPPublicKey signatureKey = null;
+ for (int i = 0; i < sigList.size(); ++i) {
+ signature = sigList.get(i);
+ signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID());
+ if (signatureKeyId == 0) {
+ signatureKeyId = signature.getKeyID();
+ }
+
+ if (signatureKey == null) {
+ signature = null;
+ } else {
+ signatureKeyId = signature.getKeyID();
+ String userId = null;
+ PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context,
+ signatureKeyId);
+ if (signKeyRing != null) {
+ userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
+ }
+ returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId);
+ break;
+ }
+ }
+
+ returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId);
+
+ if (signature == null) {
+ returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true);
+ updateProgress(R.string.progress_done, 100, 100);
+ return returnData;
+ }
+
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ signature.init(contentVerifierBuilderProvider, signatureKey);
+
+ InputStream sigIn = new BufferedInputStream(new ByteArrayInputStream(clearText));
+
+ lookAhead = readInputLine(lineOut, sigIn);
+
+ processLine(signature, lineOut.toByteArray());
+
+ if (lookAhead != -1) {
+ do {
+ lookAhead = readInputLine(lineOut, lookAhead, sigIn);
+
+ signature.update((byte) '\r');
+ signature.update((byte) '\n');
+
+ processLine(signature, lineOut.toByteArray());
+ } while (lookAhead != -1);
+ }
+
+ boolean sig_isok = signature.verify();
+
+ //Now check binding signatures
+ boolean keyBinding_isok = verifyKeyBinding(context, signature, signatureKey);
+
+ returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, sig_isok & keyBinding_isok);
+
+ updateProgress(R.string.progress_done, 100, 100);
+ return returnData;
+ }
+
+ private static boolean verifyKeyBinding(Context context, PGPSignature signature, PGPPublicKey signatureKey) {
+ long signatureKeyId = signature.getKeyID();
+ boolean keyBinding_isok = false;
+ String userId = null;
+ PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context,
+ signatureKeyId);
+ PGPPublicKey mKey = null;
+ if (signKeyRing != null) {
+ mKey = PgpKeyHelper.getMasterKey(signKeyRing);
+ }
+ if (signature.getKeyID() != mKey.getKeyID()) {
+ keyBinding_isok = verifyKeyBinding(mKey, signatureKey);
+ } else { //if the key used to make the signature was the master key, no need to check binding sigs
+ keyBinding_isok = true;
+ }
+ return keyBinding_isok;
+ }
+
+ private static boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
+ boolean subkeyBinding_isok = false;
+ boolean tmp_subkeyBinding_isok = false;
+ boolean primkeyBinding_isok = false;
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ Iterator<PGPSignature> itr = signingPublicKey.getSignatures();
+
+ subkeyBinding_isok = false;
+ tmp_subkeyBinding_isok = false;
+ primkeyBinding_isok = false;
+ while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
+ //gpg has an invalid subkey binding error on key import I think, but doesn't shout
+ //about keys without subkey signing. Can't get it to import a slightly broken one
+ //either, so we will err on bad subkey binding here.
+ PGPSignature sig = itr.next();
+ if (sig.getKeyID() == masterPublicKey.getKeyID() && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
+ //check and if ok, check primary key binding.
+ try {
+ sig.init(contentVerifierBuilderProvider, masterPublicKey);
+ tmp_subkeyBinding_isok = sig.verifyCertification(masterPublicKey, signingPublicKey);
+ } catch (PGPException e) {
+ continue;
+ } catch (SignatureException e) {
+ continue;
+ }
+
+ if (tmp_subkeyBinding_isok)
+ subkeyBinding_isok = true;
+ if (tmp_subkeyBinding_isok) {
+ primkeyBinding_isok = verifyPrimaryBinding(sig.getUnhashedSubPackets(), masterPublicKey, signingPublicKey);
+ if (primkeyBinding_isok)
+ break;
+ primkeyBinding_isok = verifyPrimaryBinding(sig.getHashedSubPackets(), masterPublicKey, signingPublicKey);
+ if (primkeyBinding_isok)
+ break;
+ }
+ }
+ }
+ return (subkeyBinding_isok & primkeyBinding_isok);
+ }
+
+ private static boolean verifyPrimaryBinding(PGPSignatureSubpacketVector Pkts, PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
+ boolean primkeyBinding_isok = false;
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureList eSigList;
+
+ if (Pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
+ try {
+ eSigList = Pkts.getEmbeddedSignatures();
+ } catch (IOException e) {
+ return false;
+ } catch (PGPException e) {
+ return false;
+ }
+ for (int j = 0; j < eSigList.size(); ++j) {
+ PGPSignature emSig = eSigList.get(j);
+ if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
+ try {
+ emSig.init(contentVerifierBuilderProvider, signingPublicKey);
+ primkeyBinding_isok = emSig.verifyCertification(masterPublicKey, signingPublicKey);
+ if (primkeyBinding_isok)
+ break;
+ } catch (PGPException e) {
+ continue;
+ } catch (SignatureException e) {
+ continue;
+ }
+ }
+ }
+ }
+ return primkeyBinding_isok;
+ }
+
+ /**
+ * Mostly taken from ClearSignedFileProcessor in Bouncy Castle
+ *
+ * @param sig
+ * @param line
+ * @throws SignatureException
+ * @throws IOException
+ */
+ private static void processLine(PGPSignature sig, byte[] line)
+ throws SignatureException, IOException {
+ int length = getLengthWithoutWhiteSpace(line);
+ if (length > 0) {
+ sig.update(line, 0, length);
+ }
+ }
+
+ private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
+ throws IOException {
+ bOut.reset();
+
+ int lookAhead = -1;
+ int ch;
+
+ while ((ch = fIn.read()) >= 0) {
+ bOut.write(ch);
+ if (ch == '\r' || ch == '\n') {
+ lookAhead = readPassedEOL(bOut, ch, fIn);
+ break;
+ }
+ }
+
+ return lookAhead;
+ }
+
+ private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn)
+ throws IOException {
+ bOut.reset();
+
+ int ch = lookAhead;
+
+ do {
+ bOut.write(ch);
+ if (ch == '\r' || ch == '\n') {
+ lookAhead = readPassedEOL(bOut, ch, fIn);
+ break;
+ }
+ } while ((ch = fIn.read()) >= 0);
+
+ if (ch < 0) {
+ lookAhead = -1;
+ }
+
+ return lookAhead;
+ }
+
+ private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn)
+ throws IOException {
+ int lookAhead = fIn.read();
+
+ if (lastCh == '\r' && lookAhead == '\n') {
+ bOut.write(lookAhead);
+ lookAhead = fIn.read();
+ }
+
+ return lookAhead;
+ }
+
+ private static int getLengthWithoutSeparator(byte[] line) {
+ int end = line.length - 1;
+
+ while (end >= 0 && isLineEnding(line[end])) {
+ end--;
+ }
+
+ return end + 1;
+ }
+
+ private static boolean isLineEnding(byte b) {
+ return b == '\r' || b == '\n';
+ }
+
+ private static int getLengthWithoutWhiteSpace(byte[] line) {
+ int end = line.length - 1;
+
+ while (end >= 0 && isWhiteSpace(line[end])) {
+ end--;
+ }
+
+ return end + 1;
+ }
+
+ private static boolean isWhiteSpace(byte b) {
+ return b == '\r' || b == '\n' || b == '\t' || b == ' ';
+ }
+
+ private static byte[] getLineSeparator() {
+ String nl = System.getProperty("line.separator");
+ byte[] nlBytes = new byte[nl.length()];
+
+ for (int i = 0; i != nlBytes.length; i++) {
+ nlBytes[i] = (byte) nl.charAt(i);
+ }
+
+ return nlBytes;
+ }
+}
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 78d42cbf9..c1c6f3088 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
@@ -42,6 +42,8 @@ import android.content.Context;
public class PgpKeyHelper {
+ private static final Pattern USER_ID_PATTERN = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$");
+
public static Date getCreationDate(PGPPublicKey key) {
return key.getCreationTime();
}
@@ -591,8 +593,7 @@ public class PgpKeyHelper {
* "Max Mustermann (this is a comment)"
* "Max Mustermann [this is nothing]"
*/
- Pattern withComment = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$");
- Matcher matcher = withComment.matcher(userId);
+ Matcher matcher = USER_ID_PATTERN.matcher(userId);
if (matcher.matches()) {
result[0] = matcher.group(1);
result[1] = matcher.group(3);
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 6055aebfc..80831d35f 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
@@ -504,7 +504,7 @@ public class PgpKeyOperation {
updateProgress(R.string.progress_done, 100, 100);
}
- public PGPPublicKeyRing signKey(long masterKeyId, long pubKeyId, String passphrase)
+ public PGPPublicKeyRing certifyKey(long masterKeyId, long pubKeyId, String passphrase)
throws PgpGeneralException, PGPException, SignatureException {
if (passphrase == null) {
throw new PgpGeneralException("Unable to obtain passphrase");
@@ -512,14 +512,14 @@ public class PgpKeyOperation {
PGPPublicKeyRing pubring = ProviderHelper
.getPGPPublicKeyRingByKeyId(mContext, pubKeyId);
- PGPSecretKey signingKey = PgpKeyHelper.getCertificationKey(mContext, masterKeyId);
- if (signingKey == null) {
+ PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(mContext, masterKeyId);
+ if (certificationKey == null) {
throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
}
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
- PGPPrivateKey signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
+ PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
if (signaturePrivateKey == null) {
throw new PgpGeneralException(
mContext.getString(R.string.error_could_not_extract_private_key));
@@ -527,7 +527,7 @@ public class PgpKeyOperation {
// TODO: SHA256 fixed?
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
- signingKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
+ certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpOperation.java
deleted file mode 100644
index 1402be435..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpOperation.java
+++ /dev/null
@@ -1,1154 +0,0 @@
-/*
- * 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;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-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;
-import java.util.Date;
-import java.util.Iterator;
-
-import org.spongycastle.bcpg.ArmoredInputStream;
-import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.bcpg.BCPGInputStream;
-import org.spongycastle.bcpg.BCPGOutputStream;
-
-import org.spongycastle.bcpg.SignaturePacket;
-
-import org.spongycastle.bcpg.SignatureSubpacket;
-import org.spongycastle.bcpg.SignatureSubpacketTags;
-import org.spongycastle.openpgp.PGPCompressedData;
-import org.spongycastle.openpgp.PGPCompressedDataGenerator;
-import org.spongycastle.openpgp.PGPEncryptedData;
-import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
-import org.spongycastle.openpgp.PGPEncryptedDataList;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPLiteralData;
-import org.spongycastle.openpgp.PGPLiteralDataGenerator;
-import org.spongycastle.openpgp.PGPObjectFactory;
-import org.spongycastle.openpgp.PGPOnePassSignature;
-import org.spongycastle.openpgp.PGPOnePassSignatureList;
-import org.spongycastle.openpgp.PGPPBEEncryptedData;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.PGPSignatureGenerator;
-import org.spongycastle.openpgp.PGPSignatureList;
-import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
-import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
-import org.spongycastle.openpgp.PGPUtil;
-import org.spongycastle.openpgp.PGPV3SignatureGenerator;
-import org.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.JcaPGPContentSignerBuilder;
-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.JcePBEKeyEncryptionMethodGenerator;
-import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
-import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.util.InputData;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
-
-import android.content.Context;
-import android.os.Bundle;
-
-public class PgpOperation {
- private Context mContext;
- private ProgressDialogUpdater mProgress;
- private InputData mData;
- private OutputStream mOutStream;
-
- public PgpOperation(Context context, ProgressDialogUpdater progress, InputData data,
- OutputStream outStream) {
- super();
- this.mContext = context;
- this.mProgress = progress;
- this.mData = data;
- this.mOutStream = outStream;
- }
-
- public void updateProgress(int message, int current, int total) {
- if (mProgress != null) {
- mProgress.setProgress(message, current, total);
- }
- }
-
- public void updateProgress(int current, int total) {
- if (mProgress != null) {
- mProgress.setProgress(current, total);
- }
- }
-
- public void signAndEncrypt(boolean useAsciiArmor, int compression, long[] encryptionKeyIds,
- String encryptionPassphrase, int symmetricEncryptionAlgorithm, long signatureKeyId,
- int signatureHashAlgorithm, boolean signatureForceV3, String signaturePassphrase)
- throws IOException, PgpGeneralException, PGPException, NoSuchProviderException,
- NoSuchAlgorithmException, SignatureException {
-
- if (encryptionKeyIds == null) {
- encryptionKeyIds = new long[0];
- }
-
- ArmoredOutputStream armorOut = null;
- OutputStream out = null;
- OutputStream encryptOut = null;
- if (useAsciiArmor) {
- armorOut = new ArmoredOutputStream(mOutStream);
- armorOut.setHeader("Version", PgpHelper.getFullVersion(mContext));
- out = armorOut;
- } else {
- out = mOutStream;
- }
- PGPSecretKey signingKey = null;
- PGPSecretKeyRing signingKeyRing = null;
- PGPPrivateKey signaturePrivateKey = null;
-
- if (encryptionKeyIds.length == 0 && encryptionPassphrase == null) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_no_encryption_keys_or_passphrase));
- }
-
- if (signatureKeyId != Id.key.none) {
- signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, signatureKeyId);
- signingKey = PgpKeyHelper.getSigningKey(mContext, signatureKeyId);
- if (signingKey == null) {
- throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
- }
-
- if (signaturePassphrase == null) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_no_signature_passphrase));
- }
-
- updateProgress(R.string.progress_extracting_signature_key, 0, 100);
-
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray());
- signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
- if (signaturePrivateKey == null) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_could_not_extract_private_key));
- }
- }
- updateProgress(R.string.progress_preparing_streams, 5, 100);
-
- // encrypt and compress input file content
- JcePGPDataEncryptorBuilder encryptorBuilder = new JcePGPDataEncryptorBuilder(
- symmetricEncryptionAlgorithm).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME)
- .setWithIntegrityPacket(true);
-
- PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(encryptorBuilder);
-
- if (encryptionKeyIds.length == 0) {
- // Symmetric encryption
- Log.d(Constants.TAG, "encryptionKeyIds length is 0 -> symmetric encryption");
-
- JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator = new JcePBEKeyEncryptionMethodGenerator(
- encryptionPassphrase.toCharArray());
- cPk.addMethod(symmetricEncryptionGenerator);
- } else {
- // Asymmetric encryption
- for (long id : encryptionKeyIds) {
- PGPPublicKey key = PgpKeyHelper.getEncryptPublicKey(mContext, id);
- if (key != null) {
-
- JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator = new JcePublicKeyKeyEncryptionMethodGenerator(
- key);
- cPk.addMethod(pubKeyEncryptionGenerator);
- }
- }
- }
- encryptOut = cPk.open(out, new byte[1 << 16]);
-
- PGPSignatureGenerator signatureGenerator = null;
- PGPV3SignatureGenerator signatureV3Generator = null;
-
- if (signatureKeyId != Id.key.none) {
- updateProgress(R.string.progress_preparing_signature, 10, 100);
-
- // content signer based on signing key algorithm and choosen hash algorithm
- JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
- signingKey.getPublicKey().getAlgorithm(), signatureHashAlgorithm)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
-
- if (signatureForceV3) {
- signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
- signatureV3Generator.init(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey);
- } else {
- signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
- signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey);
-
- String userId = PgpKeyHelper.getMainUserId(PgpKeyHelper
- .getMasterKey(signingKeyRing));
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- spGen.setSignerUserID(false, userId);
- signatureGenerator.setHashedSubpackets(spGen.generate());
- }
- }
-
- PGPCompressedDataGenerator compressGen = null;
- BCPGOutputStream bcpgOut = null;
- if (compression == Id.choice.compression.none) {
- bcpgOut = new BCPGOutputStream(encryptOut);
- } else {
- compressGen = new PGPCompressedDataGenerator(compression);
- bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut));
- }
- if (signatureKeyId != Id.key.none) {
- if (signatureForceV3) {
- signatureV3Generator.generateOnePassVersion(false).encode(bcpgOut);
- } else {
- signatureGenerator.generateOnePassVersion(false).encode(bcpgOut);
- }
- }
-
- PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
- // file name not needed, so empty string
- OutputStream pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "", new Date(),
- new byte[1 << 16]);
- updateProgress(R.string.progress_encrypting, 20, 100);
-
- long done = 0;
- int n = 0;
- byte[] buffer = new byte[1 << 16];
- InputStream in = mData.getInputStream();
- while ((n = in.read(buffer)) > 0) {
- pOut.write(buffer, 0, n);
- if (signatureKeyId != Id.key.none) {
- if (signatureForceV3) {
- signatureV3Generator.update(buffer, 0, n);
- } else {
- signatureGenerator.update(buffer, 0, n);
- }
- }
- done += n;
- if (mData.getSize() != 0) {
- updateProgress((int) (20 + (95 - 20) * done / mData.getSize()), 100);
- }
- }
-
- literalGen.close();
-
- if (signatureKeyId != Id.key.none) {
- updateProgress(R.string.progress_generating_signature, 95, 100);
- if (signatureForceV3) {
- signatureV3Generator.generate().encode(pOut);
- } else {
- signatureGenerator.generate().encode(pOut);
- }
- }
- if (compressGen != null) {
- compressGen.close();
- }
- encryptOut.close();
- if (useAsciiArmor) {
- armorOut.close();
- }
-
- updateProgress(R.string.progress_done, 100, 100);
- }
-
- public void signText(long signatureKeyId, String signaturePassphrase,
- int signatureHashAlgorithm, boolean forceV3Signature) throws PgpGeneralException,
- PGPException, IOException, NoSuchAlgorithmException, SignatureException {
-
- ArmoredOutputStream armorOut = new ArmoredOutputStream(mOutStream);
- armorOut.setHeader("Version", PgpHelper.getFullVersion(mContext));
-
- PGPSecretKey signingKey = null;
- PGPSecretKeyRing signingKeyRing = null;
- PGPPrivateKey signaturePrivateKey = null;
-
- if (signatureKeyId == 0) {
- armorOut.close();
- throw new PgpGeneralException(mContext.getString(R.string.error_no_signature_key));
- }
-
- signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, signatureKeyId);
- signingKey = PgpKeyHelper.getSigningKey(mContext, signatureKeyId);
- if (signingKey == null) {
- armorOut.close();
- throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
- }
-
- if (signaturePassphrase == null) {
- armorOut.close();
- throw new PgpGeneralException(mContext.getString(R.string.error_no_signature_passphrase));
- }
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray());
- signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
- if (signaturePrivateKey == null) {
- armorOut.close();
- throw new PgpGeneralException(
- mContext.getString(R.string.error_could_not_extract_private_key));
- }
- updateProgress(R.string.progress_preparing_streams, 0, 100);
-
- updateProgress(R.string.progress_preparing_signature, 30, 100);
-
- PGPSignatureGenerator signatureGenerator = null;
- PGPV3SignatureGenerator signatureV3Generator = null;
-
- // content signer based on signing key algorithm and choosen hash algorithm
- JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey
- .getPublicKey().getAlgorithm(), signatureHashAlgorithm)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
-
- if (forceV3Signature) {
- signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
- signatureV3Generator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey);
- } else {
- signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
- signatureGenerator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey);
-
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- String userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signingKeyRing));
- spGen.setSignerUserID(false, userId);
- signatureGenerator.setHashedSubpackets(spGen.generate());
- }
-
- updateProgress(R.string.progress_signing, 40, 100);
-
- armorOut.beginClearText(signatureHashAlgorithm);
-
- InputStream inStream = mData.getInputStream();
- final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
-
- final byte[] newline = "\r\n".getBytes("UTF-8");
-
- if (forceV3Signature) {
- processLine(reader.readLine(), armorOut, signatureV3Generator);
- } else {
- processLine(reader.readLine(), armorOut, signatureGenerator);
- }
-
- while (true) {
- final String line = reader.readLine();
-
- if (line == null) {
- armorOut.write(newline);
- break;
- }
-
- armorOut.write(newline);
- if (forceV3Signature) {
- signatureV3Generator.update(newline);
- processLine(line, armorOut, signatureV3Generator);
- } else {
- signatureGenerator.update(newline);
- processLine(line, armorOut, signatureGenerator);
- }
- }
-
- armorOut.endClearText();
-
- BCPGOutputStream bOut = new BCPGOutputStream(armorOut);
- if (forceV3Signature) {
- signatureV3Generator.generate().encode(bOut);
- } else {
- signatureGenerator.generate().encode(bOut);
- }
- armorOut.close();
-
- updateProgress(R.string.progress_done, 100, 100);
- }
-
- public void generateSignature(boolean armored, boolean binary, long signatureKeyId,
- String signaturePassPhrase, int hashAlgorithm, boolean forceV3Signature)
- throws PgpGeneralException, PGPException, IOException, NoSuchAlgorithmException,
- SignatureException {
-
- OutputStream out = null;
-
- // Ascii Armor (Base64)
- ArmoredOutputStream armorOut = null;
- if (armored) {
- armorOut = new ArmoredOutputStream(mOutStream);
- armorOut.setHeader("Version", PgpHelper.getFullVersion(mContext));
- out = armorOut;
- } else {
- out = mOutStream;
- }
-
- PGPSecretKey signingKey = null;
- PGPSecretKeyRing signingKeyRing = null;
- PGPPrivateKey signaturePrivateKey = null;
-
- if (signatureKeyId == 0) {
- throw new PgpGeneralException(mContext.getString(R.string.error_no_signature_key));
- }
-
- signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, signatureKeyId);
- signingKey = PgpKeyHelper.getSigningKey(mContext, signatureKeyId);
- if (signingKey == null) {
- throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
- }
-
- if (signaturePassPhrase == null) {
- throw new PgpGeneralException(mContext.getString(R.string.error_no_signature_passphrase));
- }
-
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassPhrase.toCharArray());
- signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
- if (signaturePrivateKey == null) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_could_not_extract_private_key));
- }
- updateProgress(R.string.progress_preparing_streams, 0, 100);
-
- updateProgress(R.string.progress_preparing_signature, 30, 100);
-
- PGPSignatureGenerator signatureGenerator = null;
- PGPV3SignatureGenerator signatureV3Generator = null;
-
- int type = PGPSignature.CANONICAL_TEXT_DOCUMENT;
- if (binary) {
- type = PGPSignature.BINARY_DOCUMENT;
- }
-
- // content signer based on signing key algorithm and choosen hash algorithm
- JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey
- .getPublicKey().getAlgorithm(), hashAlgorithm)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
-
- if (forceV3Signature) {
- signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
- signatureV3Generator.init(type, signaturePrivateKey);
- } else {
- signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
- signatureGenerator.init(type, signaturePrivateKey);
-
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- String userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signingKeyRing));
- spGen.setSignerUserID(false, userId);
- signatureGenerator.setHashedSubpackets(spGen.generate());
- }
-
- updateProgress(R.string.progress_signing, 40, 100);
-
- InputStream inStream = mData.getInputStream();
- if (binary) {
- byte[] buffer = new byte[1 << 16];
- int n = 0;
- while ((n = inStream.read(buffer)) > 0) {
- if (forceV3Signature) {
- signatureV3Generator.update(buffer, 0, n);
- } else {
- signatureGenerator.update(buffer, 0, n);
- }
- }
- } else {
- final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
- final byte[] newline = "\r\n".getBytes("UTF-8");
-
- while (true) {
- final String line = reader.readLine();
-
- if (line == null) {
- break;
- }
-
- if (forceV3Signature) {
- processLine(line, null, signatureV3Generator);
- signatureV3Generator.update(newline);
- } else {
- processLine(line, null, signatureGenerator);
- signatureGenerator.update(newline);
- }
- }
- }
-
- BCPGOutputStream bOut = new BCPGOutputStream(out);
- if (forceV3Signature) {
- signatureV3Generator.generate().encode(bOut);
- } else {
- signatureGenerator.generate().encode(bOut);
- }
- out.close();
- mOutStream.close();
-
- if (mProgress != null)
- mProgress.setProgress(R.string.progress_done, 100, 100);
- }
-
- 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;
- }
-
- public Bundle decryptAndVerify(String passphrase, boolean assumeSymmetric) throws IOException,
- PgpGeneralException, PGPException, SignatureException {
- if (passphrase == null) {
- passphrase = "";
- }
-
- Bundle returnData = new Bundle();
- InputStream in = PGPUtil.getDecoderStream(mData.getInputStream());
- PGPObjectFactory pgpF = new PGPObjectFactory(in);
- PGPEncryptedDataList enc;
- Object o = pgpF.nextObject();
- long signatureKeyId = 0;
-
- int currentProgress = 0;
- updateProgress(R.string.progress_reading_data, currentProgress, 100);
-
- if (o instanceof PGPEncryptedDataList) {
- enc = (PGPEncryptedDataList) o;
- } else {
- enc = (PGPEncryptedDataList) pgpF.nextObject();
- }
-
- if (enc == null) {
- throw new PgpGeneralException(mContext.getString(R.string.error_invalid_data));
- }
-
- InputStream clear = null;
- PGPEncryptedData encryptedData = null;
-
- currentProgress += 5;
-
- // TODO: currently we always only look at the first known key or symmetric encryption,
- // there might be more...
- if (assumeSymmetric) {
- PGPPBEEncryptedData pbe = null;
- Iterator<?> it = enc.getEncryptedDataObjects();
- // find secret key
- while (it.hasNext()) {
- Object obj = it.next();
- if (obj instanceof PGPPBEEncryptedData) {
- pbe = (PGPPBEEncryptedData) obj;
- break;
- }
- }
-
- if (pbe == null) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_no_symmetric_encryption_packet));
- }
-
- updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
-
- PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build();
- PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder(
- digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- passphrase.toCharArray());
-
- clear = pbe.getDataStream(decryptorFactory);
-
- encryptedData = pbe;
- 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) {
- pbe = encData;
- break;
- }
- }
- }
-
- if (secretKey == null) {
- throw new PgpGeneralException(mContext.getString(R.string.error_no_secret_key_found));
- }
-
- currentProgress += 5;
- updateProgress(R.string.progress_extracting_key, currentProgress, 100);
- PGPPrivateKey privateKey = null;
- try {
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- passphrase.toCharArray());
- privateKey = secretKey.extractPrivateKey(keyDecryptor);
- } catch (PGPException e) {
- throw new PGPException(mContext.getString(R.string.error_wrong_passphrase));
- }
- if (privateKey == null) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_could_not_extract_private_key));
- }
- currentProgress += 5;
- updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
-
- PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey);
-
- clear = pbe.getDataStream(decryptorFactory);
-
- encryptedData = pbe;
- currentProgress += 5;
- }
-
- PGPObjectFactory plainFact = new PGPObjectFactory(clear);
- Object dataChunk = plainFact.nextObject();
- PGPOnePassSignature signature = null;
- PGPPublicKey signatureKey = null;
- int signatureIndex = -1;
-
- if (dataChunk instanceof PGPCompressedData) {
- updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
-
- PGPObjectFactory fact = new PGPObjectFactory(
- ((PGPCompressedData) dataChunk).getDataStream());
- dataChunk = fact.nextObject();
- plainFact = fact;
- currentProgress += 10;
- }
-
- if (dataChunk instanceof PGPOnePassSignatureList) {
- updateProgress(R.string.progress_processing_signature, currentProgress, 100);
-
- returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true);
- PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
- for (int i = 0; i < sigList.size(); ++i) {
- signature = sigList.get(i);
- signatureKey = ProviderHelper
- .getPGPPublicKeyByKeyId(mContext, signature.getKeyID());
- if (signatureKeyId == 0) {
- signatureKeyId = signature.getKeyID();
- }
- if (signatureKey == null) {
- signature = null;
- } else {
- signatureIndex = i;
- signatureKeyId = signature.getKeyID();
- String userId = null;
- PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(
- mContext, signatureKeyId);
- if (signKeyRing != null) {
- userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
- }
- returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId);
- break;
- }
- }
-
- returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId);
-
- if (signature != null) {
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
-
- signature.init(contentVerifierBuilderProvider, signatureKey);
- } else {
- returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true);
- }
-
- dataChunk = plainFact.nextObject();
- currentProgress += 10;
- }
-
- if (dataChunk instanceof PGPSignatureList) {
- dataChunk = plainFact.nextObject();
- }
-
- if (dataChunk instanceof PGPLiteralData) {
- updateProgress(R.string.progress_decrypting, currentProgress, 100);
-
- PGPLiteralData literalData = (PGPLiteralData) dataChunk;
- OutputStream out = mOutStream;
-
- byte[] buffer = new byte[1 << 16];
- InputStream dataIn = literalData.getInputStream();
-
- int startProgress = currentProgress;
- int endProgress = 100;
- if (signature != null) {
- endProgress = 90;
- } else if (encryptedData.isIntegrityProtected()) {
- endProgress = 95;
- }
- int n = 0;
- int done = 0;
- long startPos = mData.getStreamPosition();
- while ((n = dataIn.read(buffer)) > 0) {
- out.write(buffer, 0, n);
- done += n;
- if (signature != null) {
- try {
- signature.update(buffer, 0, n);
- } catch (SignatureException e) {
- returnData
- .putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false);
- signature = null;
- }
- }
- // unknown size, but try to at least have a moving, slowing down progress bar
- currentProgress = startProgress + (endProgress - startProgress) * done
- / (done + 100000);
- if (mData.getSize() - startPos == 0) {
- currentProgress = endProgress;
- } else {
- currentProgress = (int) (startProgress + (endProgress - startProgress)
- * (mData.getStreamPosition() - startPos) / (mData.getSize() - startPos));
- }
- updateProgress(currentProgress, 100);
- }
-
- if (signature != null) {
- updateProgress(R.string.progress_verifying_signature, 90, 100);
-
- PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
- PGPSignature messageSignature = signatureList.get(signatureIndex);
-
- //Now check binding signatures
- boolean keyBinding_isok = verifyKeyBinding(mContext, messageSignature, signatureKey);
- boolean sig_isok = signature.verify(messageSignature);
- returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, keyBinding_isok & sig_isok);
- }
- }
-
- // TODO: add integrity somewhere
- if (encryptedData.isIntegrityProtected()) {
- updateProgress(R.string.progress_verifying_integrity, 95, 100);
-
- if (encryptedData.verify()) {
- // passed
- } else {
- // failed
- }
- } else {
- // no integrity check
- }
-
- updateProgress(R.string.progress_done, 100, 100);
- return returnData;
- }
-
- public Bundle verifyText(boolean lookupUnknownKey) throws IOException, PgpGeneralException,
- PGPException, SignatureException {
- Bundle returnData = new Bundle();
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ArmoredInputStream aIn = new ArmoredInputStream(mData.getInputStream());
-
- updateProgress(R.string.progress_done, 0, 100);
-
- // mostly taken from ClearSignedFileProcessor
- ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
- int lookAhead = readInputLine(lineOut, aIn);
- byte[] lineSep = getLineSeparator();
-
- byte[] line = lineOut.toByteArray();
- out.write(line, 0, getLengthWithoutSeparator(line));
- out.write(lineSep);
-
- while (lookAhead != -1 && aIn.isClearText()) {
- lookAhead = readInputLine(lineOut, lookAhead, aIn);
- line = lineOut.toByteArray();
- out.write(line, 0, getLengthWithoutSeparator(line));
- out.write(lineSep);
- }
-
- out.close();
-
- byte[] clearText = out.toByteArray();
- mOutStream.write(clearText);
-
- returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true);
-
- updateProgress(R.string.progress_processing_signature, 60, 100);
- PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
-
- PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject();
- if (sigList == null) {
- throw new PgpGeneralException(mContext.getString(R.string.error_corrupt_data));
- }
- PGPSignature signature = null;
- 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 key is not known and we want to lookup unknown ones...
- if (signatureKey == null && lookupUnknownKey) {
-
- returnData = new Bundle();
- returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId);
- returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_LOOKUP_KEY, true);
-
- // return directly now, decrypt will be done again after importing unknown key
- return returnData;
- }
-
- if (signatureKey == null) {
- signature = null;
- } else {
- signatureKeyId = signature.getKeyID();
- String userId = null;
- PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(mContext,
- signatureKeyId);
- if (signKeyRing != null) {
- userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
- }
- returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId);
- break;
- }
- }
-
- returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId);
-
- if (signature == null) {
- returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true);
- if (mProgress != null)
- mProgress.setProgress(R.string.progress_done, 100, 100);
- return returnData;
- }
-
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
-
- signature.init(contentVerifierBuilderProvider, signatureKey);
-
- InputStream sigIn = new BufferedInputStream(new ByteArrayInputStream(clearText));
-
- lookAhead = readInputLine(lineOut, sigIn);
-
- processLine(signature, lineOut.toByteArray());
-
- if (lookAhead != -1) {
- do {
- lookAhead = readInputLine(lineOut, lookAhead, sigIn);
-
- signature.update((byte) '\r');
- signature.update((byte) '\n');
-
- processLine(signature, lineOut.toByteArray());
- } while (lookAhead != -1);
- }
-
- boolean sig_isok = signature.verify();
-
- //Now check binding signatures
- boolean keyBinding_isok = verifyKeyBinding(mContext, signature, signatureKey);
-
- returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, sig_isok & keyBinding_isok);
-
- updateProgress(R.string.progress_done, 100, 100);
- return returnData;
- }
-
- public boolean verifyKeyBinding(Context mContext, PGPSignature signature, PGPPublicKey signatureKey)
- {
- long signatureKeyId = signature.getKeyID();
- boolean keyBinding_isok = false;
- String userId = null;
- PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(mContext,
- signatureKeyId);
- PGPPublicKey mKey = null;
- if (signKeyRing != null) {
- mKey = PgpKeyHelper.getMasterKey(signKeyRing);
- }
- if (signature.getKeyID() != mKey.getKeyID()) {
- keyBinding_isok = verifyKeyBinding(mKey, signatureKey);
- } else { //if the key used to make the signature was the master key, no need to check binding sigs
- keyBinding_isok = true;
- }
- return keyBinding_isok;
- }
-
- public boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey)
- {
- boolean subkeyBinding_isok = false;
- boolean tmp_subkeyBinding_isok = false;
- boolean primkeyBinding_isok = false;
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
-
- Iterator<PGPSignature> itr = signingPublicKey.getSignatures();
-
- subkeyBinding_isok = false;
- tmp_subkeyBinding_isok = false;
- primkeyBinding_isok = false;
- while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
- //gpg has an invalid subkey binding error on key import I think, but doesn't shout
- //about keys without subkey signing. Can't get it to import a slightly broken one
- //either, so we will err on bad subkey binding here.
- PGPSignature sig = itr.next();
- if (sig.getKeyID() == masterPublicKey.getKeyID() && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
- //check and if ok, check primary key binding.
- try {
- sig.init(contentVerifierBuilderProvider, masterPublicKey);
- tmp_subkeyBinding_isok = sig.verifyCertification(masterPublicKey, signingPublicKey);
- } catch (PGPException e) {
- continue;
- } catch (SignatureException e) {
- continue;
- }
-
- if (tmp_subkeyBinding_isok)
- subkeyBinding_isok = true;
- if (tmp_subkeyBinding_isok) {
- primkeyBinding_isok = verifyPrimaryBinding(sig.getUnhashedSubPackets(), masterPublicKey, signingPublicKey);
- if (primkeyBinding_isok)
- break;
- primkeyBinding_isok = verifyPrimaryBinding(sig.getHashedSubPackets(), masterPublicKey, signingPublicKey);
- if (primkeyBinding_isok)
- break;
- }
- }
- }
- return (subkeyBinding_isok & primkeyBinding_isok);
- }
-
- private boolean verifyPrimaryBinding(PGPSignatureSubpacketVector Pkts, PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey)
- {
- boolean primkeyBinding_isok = false;
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- PGPSignatureList eSigList;
-
- if (Pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
- try {
- eSigList = Pkts.getEmbeddedSignatures();
- } catch (IOException e) {
- return false;
- } catch (PGPException e) {
- return false;
- }
- for (int j = 0; j < eSigList.size(); ++j) {
- PGPSignature emSig = eSigList.get(j);
- if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
- try {
- emSig.init(contentVerifierBuilderProvider, signingPublicKey);
- primkeyBinding_isok = emSig.verifyCertification(masterPublicKey, signingPublicKey);
- if (primkeyBinding_isok)
- break;
- } catch (PGPException e) {
- continue;
- } catch (SignatureException e) {
- continue;
- }
- }
- }
- }
- return primkeyBinding_isok;
- }
-
- private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput,
- final PGPSignatureGenerator pSignatureGenerator) throws IOException, SignatureException {
-
- if (pLine == null) {
- return;
- }
-
- final char[] chars = pLine.toCharArray();
- int len = chars.length;
-
- while (len > 0) {
- if (!Character.isWhitespace(chars[len - 1])) {
- break;
- }
- len--;
- }
-
- final byte[] data = pLine.substring(0, len).getBytes("UTF-8");
-
- if (pArmoredOutput != null) {
- pArmoredOutput.write(data);
- }
- pSignatureGenerator.update(data);
- }
-
- private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput,
- final PGPV3SignatureGenerator pSignatureGenerator) throws IOException,
- SignatureException {
-
- if (pLine == null) {
- return;
- }
-
- final char[] chars = pLine.toCharArray();
- int len = chars.length;
-
- while (len > 0) {
- if (!Character.isWhitespace(chars[len - 1])) {
- break;
- }
- len--;
- }
-
- final byte[] data = pLine.substring(0, len).getBytes("UTF-8");
-
- if (pArmoredOutput != null) {
- pArmoredOutput.write(data);
- }
- pSignatureGenerator.update(data);
- }
-
- // taken from ClearSignedFileProcessor in BC
- private static void processLine(PGPSignature sig, byte[] line) throws SignatureException,
- IOException {
- int length = getLengthWithoutWhiteSpace(line);
- if (length > 0) {
- sig.update(line, 0, length);
- }
- }
-
- private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
- throws IOException {
- bOut.reset();
-
- int lookAhead = -1;
- int ch;
-
- while ((ch = fIn.read()) >= 0) {
- bOut.write(ch);
- if (ch == '\r' || ch == '\n') {
- lookAhead = readPassedEOL(bOut, ch, fIn);
- break;
- }
- }
-
- return lookAhead;
- }
-
- private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn)
- throws IOException {
- bOut.reset();
-
- int ch = lookAhead;
-
- do {
- bOut.write(ch);
- if (ch == '\r' || ch == '\n') {
- lookAhead = readPassedEOL(bOut, ch, fIn);
- break;
- }
- } while ((ch = fIn.read()) >= 0);
-
- if (ch < 0) {
- lookAhead = -1;
- }
-
- return lookAhead;
- }
-
- private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn)
- throws IOException {
- int lookAhead = fIn.read();
-
- if (lastCh == '\r' && lookAhead == '\n') {
- bOut.write(lookAhead);
- lookAhead = fIn.read();
- }
-
- return lookAhead;
- }
-
- private static int getLengthWithoutSeparator(byte[] line) {
- int end = line.length - 1;
-
- while (end >= 0 && isLineEnding(line[end])) {
- end--;
- }
-
- return end + 1;
- }
-
- private static boolean isLineEnding(byte b) {
- return b == '\r' || b == '\n';
- }
-
- private static int getLengthWithoutWhiteSpace(byte[] line) {
- int end = line.length - 1;
-
- while (end >= 0 && isWhiteSpace(line[end])) {
- end--;
- }
-
- return end + 1;
- }
-
- private static boolean isWhiteSpace(byte b) {
- return b == '\r' || b == '\n' || b == '\t' || b == ' ';
- }
-
- private static byte[] getLineSeparator() {
- String nl = System.getProperty("line.separator");
- byte[] nlBytes = new byte[nl.length()];
-
- for (int i = 0; i != nlBytes.length; i++) {
- nlBytes[i] = (byte) nl.charAt(i);
- }
-
- return nlBytes;
- }
-}
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
new file mode 100644
index 000000000..ba1182c1b
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -0,0 +1,605 @@
+/*
+ * 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");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import android.content.Context;
+
+import org.spongycastle.bcpg.ArmoredOutputStream;
+import org.spongycastle.bcpg.BCPGOutputStream;
+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.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;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.util.InputData;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
+
+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;
+import java.util.Date;
+
+/**
+ * This class uses a Builder pattern!
+ */
+public class PgpSignEncrypt {
+ private Context context;
+ private InputData data;
+ private OutputStream outStream;
+
+ private ProgressDialogUpdater progress;
+ private boolean enableAsciiArmorOutput;
+ private int compressionId;
+ private long[] encryptionKeyIds;
+ private String encryptionPassphrase;
+ private int symmetricEncryptionAlgorithm;
+ private long signatureKeyId;
+ private int signatureHashAlgorithm;
+ private boolean signatureForceV3;
+ private String signaturePassphrase;
+
+ private PgpSignEncrypt(Builder builder) {
+ // private Constructor can only be called from Builder
+ this.context = builder.context;
+ this.data = builder.data;
+ this.outStream = builder.outStream;
+
+ this.progress = builder.progress;
+ this.enableAsciiArmorOutput = builder.enableAsciiArmorOutput;
+ this.compressionId = builder.compressionId;
+ this.encryptionKeyIds = builder.encryptionKeyIds;
+ this.encryptionPassphrase = builder.encryptionPassphrase;
+ this.symmetricEncryptionAlgorithm = builder.symmetricEncryptionAlgorithm;
+ this.signatureKeyId = builder.signatureKeyId;
+ this.signatureHashAlgorithm = builder.signatureHashAlgorithm;
+ this.signatureForceV3 = builder.signatureForceV3;
+ this.signaturePassphrase = builder.signaturePassphrase;
+ }
+
+ public static class Builder {
+ // mandatory parameter
+ private Context context;
+ private InputData data;
+ private OutputStream outStream;
+
+ // optional
+ private ProgressDialogUpdater progress = null;
+ private boolean enableAsciiArmorOutput = false;
+ private int compressionId = Id.choice.compression.none;
+ private long[] encryptionKeyIds = new long[0];
+ private String encryptionPassphrase = null;
+ private int symmetricEncryptionAlgorithm = 0;
+ private long signatureKeyId = Id.key.none;
+ private int signatureHashAlgorithm = 0;
+ private boolean signatureForceV3 = false;
+ private String signaturePassphrase = null;
+
+ public Builder(Context context, InputData data, OutputStream outStream) {
+ this.context = context;
+ this.data = data;
+ this.outStream = outStream;
+ }
+
+ public Builder progress(ProgressDialogUpdater progress) {
+ this.progress = progress;
+ return this;
+ }
+
+ public Builder enableAsciiArmorOutput(boolean enableAsciiArmorOutput) {
+ this.enableAsciiArmorOutput = enableAsciiArmorOutput;
+ return this;
+ }
+
+ public Builder compressionId(int compressionId) {
+ this.compressionId = compressionId;
+ return this;
+ }
+
+ public Builder encryptionKeyIds(long[] encryptionKeyIds) {
+ this.encryptionKeyIds = encryptionKeyIds;
+ return this;
+ }
+
+ public Builder encryptionPassphrase(String encryptionPassphrase) {
+ this.encryptionPassphrase = encryptionPassphrase;
+ return this;
+ }
+
+ public Builder symmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) {
+ this.symmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;
+ return this;
+ }
+
+ public Builder signatureKeyId(long signatureKeyId) {
+ this.signatureKeyId = signatureKeyId;
+ return this;
+ }
+
+ public Builder signatureHashAlgorithm(int signatureHashAlgorithm) {
+ this.signatureHashAlgorithm = signatureHashAlgorithm;
+ return this;
+ }
+
+ public Builder signatureForceV3(boolean signatureForceV3) {
+ this.signatureForceV3 = signatureForceV3;
+ return this;
+ }
+
+ public Builder signaturePassphrase(String signaturePassphrase) {
+ this.signaturePassphrase = signaturePassphrase;
+ return this;
+ }
+
+ public PgpSignEncrypt build() {
+ return new PgpSignEncrypt(this);
+ }
+ }
+
+ public void updateProgress(int message, int current, int total) {
+ if (progress != null) {
+ progress.setProgress(message, current, total);
+ }
+ }
+
+ public void updateProgress(int current, int total) {
+ if (progress != null) {
+ progress.setProgress(current, total);
+ }
+ }
+
+ /**
+ * Signs and/or encrypts data based on parameters of class
+ *
+ * @throws IOException
+ * @throws PgpGeneralException
+ * @throws PGPException
+ * @throws NoSuchProviderException
+ * @throws NoSuchAlgorithmException
+ * @throws SignatureException
+ */
+ public void execute()
+ throws IOException, PgpGeneralException, PGPException, NoSuchProviderException,
+ NoSuchAlgorithmException, SignatureException {
+
+ boolean enableSignature = signatureKeyId != Id.key.none;
+ boolean enableEncryption = (encryptionKeyIds.length != 0 || encryptionPassphrase != null);
+ boolean enableCompression = (enableEncryption && compressionId != Id.choice.compression.none);
+
+ Log.d(Constants.TAG, "enableSignature:" + enableSignature
+ + "\nenableEncryption:" + enableEncryption
+ + "\nenableCompression:" + enableCompression
+ + "\nenableAsciiArmorOutput:" + enableAsciiArmorOutput);
+
+ int signatureType;
+ if (enableAsciiArmorOutput && enableSignature && !enableEncryption && !enableCompression) {
+ // for sign-only ascii text
+ signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
+ } else {
+ signatureType = PGPSignature.BINARY_DOCUMENT;
+ }
+
+ ArmoredOutputStream armorOut = null;
+ OutputStream out;
+ if (enableAsciiArmorOutput) {
+ armorOut = new ArmoredOutputStream(outStream);
+ armorOut.setHeader("Version", PgpHelper.getFullVersion(context));
+ out = armorOut;
+ } else {
+ out = outStream;
+ }
+
+ /* Get keys for signature generation for later usage */
+ PGPSecretKey signingKey = null;
+ PGPSecretKeyRing signingKeyRing = null;
+ PGPPrivateKey signaturePrivateKey = null;
+ if (enableSignature) {
+ signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId);
+ signingKey = PgpKeyHelper.getSigningKey(context, signatureKeyId);
+ if (signingKey == null) {
+ throw new PgpGeneralException(context.getString(R.string.error_signature_failed));
+ }
+
+ if (signaturePassphrase == null) {
+ throw new PgpGeneralException(
+ context.getString(R.string.error_no_signature_passphrase));
+ }
+
+ updateProgress(R.string.progress_extracting_signature_key, 0, 100);
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray());
+ signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
+ if (signaturePrivateKey == null) {
+ throw new PgpGeneralException(
+ context.getString(R.string.error_could_not_extract_private_key));
+ }
+ }
+ updateProgress(R.string.progress_preparing_streams, 5, 100);
+
+ /* Initialize PGPEncryptedDataGenerator for later usage */
+ PGPEncryptedDataGenerator cPk = null;
+ if (enableEncryption) {
+ // has Integrity packet enabled!
+ JcePGPDataEncryptorBuilder encryptorBuilder =
+ new JcePGPDataEncryptorBuilder(symmetricEncryptionAlgorithm)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME)
+ .setWithIntegrityPacket(true);
+
+ cPk = new PGPEncryptedDataGenerator(encryptorBuilder);
+
+ if (encryptionKeyIds.length == 0) {
+ // Symmetric encryption
+ Log.d(Constants.TAG, "encryptionKeyIds length is 0 -> symmetric encryption");
+
+ JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator =
+ new JcePBEKeyEncryptionMethodGenerator(encryptionPassphrase.toCharArray());
+ cPk.addMethod(symmetricEncryptionGenerator);
+ } else {
+ // Asymmetric encryption
+ for (long id : encryptionKeyIds) {
+ PGPPublicKey key = PgpKeyHelper.getEncryptPublicKey(context, id);
+ if (key != null) {
+ JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator =
+ new JcePublicKeyKeyEncryptionMethodGenerator(key);
+ cPk.addMethod(pubKeyEncryptionGenerator);
+ }
+ }
+ }
+ }
+
+ /* Initialize signature generator object for later usage */
+ PGPSignatureGenerator signatureGenerator = null;
+ PGPV3SignatureGenerator signatureV3Generator = null;
+ if (enableSignature) {
+ updateProgress(R.string.progress_preparing_signature, 10, 100);
+
+ // content signer based on signing key algorithm and chosen hash algorithm
+ JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
+ signingKey.getPublicKey().getAlgorithm(), signatureHashAlgorithm)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ if (signatureForceV3) {
+ signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
+ signatureV3Generator.init(signatureType, signaturePrivateKey);
+ } else {
+ signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
+ signatureGenerator.init(signatureType, signaturePrivateKey);
+
+ String userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signingKeyRing));
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ spGen.setSignerUserID(false, userId);
+ signatureGenerator.setHashedSubpackets(spGen.generate());
+ }
+ }
+
+ PGPCompressedDataGenerator compressGen = null;
+ OutputStream pOut;
+ OutputStream encryptionOut = null;
+ BCPGOutputStream bcpgOut;
+ if (enableEncryption) {
+ /* actual encryption */
+
+ encryptionOut = cPk.open(out, new byte[1 << 16]);
+
+ if (enableCompression) {
+ compressGen = new PGPCompressedDataGenerator(compressionId);
+ bcpgOut = new BCPGOutputStream(compressGen.open(encryptionOut));
+ } else {
+ bcpgOut = new BCPGOutputStream(encryptionOut);
+ }
+
+ if (enableSignature) {
+ if (signatureForceV3) {
+ signatureV3Generator.generateOnePassVersion(false).encode(bcpgOut);
+ } else {
+ signatureGenerator.generateOnePassVersion(false).encode(bcpgOut);
+ }
+ }
+
+ PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
+ // file name not needed, so empty string
+ pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "", new Date(),
+ new byte[1 << 16]);
+ updateProgress(R.string.progress_encrypting, 20, 100);
+
+ long progress = 0;
+ int n;
+ byte[] buffer = new byte[1 << 16];
+ InputStream in = data.getInputStream();
+ while ((n = in.read(buffer)) > 0) {
+ pOut.write(buffer, 0, n);
+
+ // update signature buffer if signature is requested
+ if (enableSignature) {
+ if (signatureForceV3) {
+ signatureV3Generator.update(buffer, 0, n);
+ } else {
+ signatureGenerator.update(buffer, 0, n);
+ }
+ }
+
+ progress += n;
+ if (data.getSize() != 0) {
+ updateProgress((int) (20 + (95 - 20) * progress / data.getSize()), 100);
+ }
+ }
+
+ literalGen.close();
+ } else if (enableAsciiArmorOutput && enableSignature && !enableEncryption && !enableCompression) {
+ /* sign-only of ascii text */
+
+ updateProgress(R.string.progress_signing, 40, 100);
+
+ // write directly on armor output stream
+ armorOut.beginClearText(signatureHashAlgorithm);
+
+ InputStream in = data.getInputStream();
+ final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+
+ final byte[] newline = "\r\n".getBytes("UTF-8");
+
+ if (signatureForceV3) {
+ processLine(reader.readLine(), armorOut, signatureV3Generator);
+ } else {
+ processLine(reader.readLine(), armorOut, signatureGenerator);
+ }
+
+ while (true) {
+ String line = reader.readLine();
+
+ if (line == null) {
+ armorOut.write(newline);
+ break;
+ }
+
+ armorOut.write(newline);
+
+ // update signature buffer with input line
+ if (signatureForceV3) {
+ signatureV3Generator.update(newline);
+ processLine(line, armorOut, signatureV3Generator);
+ } else {
+ signatureGenerator.update(newline);
+ processLine(line, armorOut, signatureGenerator);
+ }
+ }
+
+ armorOut.endClearText();
+
+ pOut = new BCPGOutputStream(armorOut);
+ } else {
+ // TODO: implement sign-only for files!
+ pOut = null;
+ Log.e(Constants.TAG, "not supported!");
+ }
+
+ if (enableSignature) {
+ updateProgress(R.string.progress_generating_signature, 95, 100);
+ if (signatureForceV3) {
+ signatureV3Generator.generate().encode(pOut);
+ } else {
+ signatureGenerator.generate().encode(pOut);
+ }
+ }
+
+ // closing outputs
+ // NOTE: closing needs to be done in the correct order!
+ // TODO: closing bcpgOut and pOut???
+ if (enableEncryption) {
+ if (enableCompression) {
+ compressGen.close();
+ }
+
+ encryptionOut.close();
+ }
+ if (enableAsciiArmorOutput) {
+ armorOut.close();
+ }
+
+ out.close();
+ outStream.close();
+
+ updateProgress(R.string.progress_done, 100, 100);
+ }
+
+ // TODO: merge this into execute method!
+ // TODO: allow binary input for this class
+ public void generateSignature()
+ throws PgpGeneralException, PGPException, IOException, NoSuchAlgorithmException,
+ SignatureException {
+
+ OutputStream out;
+ if (enableAsciiArmorOutput) {
+ // Ascii Armor (Radix-64)
+ ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream);
+ armorOut.setHeader("Version", PgpHelper.getFullVersion(context));
+ out = armorOut;
+ } else {
+ out = outStream;
+ }
+
+ if (signatureKeyId == 0) {
+ throw new PgpGeneralException(context.getString(R.string.error_no_signature_key));
+ }
+
+ PGPSecretKeyRing signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId);
+ PGPSecretKey signingKey = PgpKeyHelper.getSigningKey(context, signatureKeyId);
+ if (signingKey == null) {
+ throw new PgpGeneralException(context.getString(R.string.error_signature_failed));
+ }
+
+ if (signaturePassphrase == null) {
+ throw new PgpGeneralException(context.getString(R.string.error_no_signature_passphrase));
+ }
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray());
+ PGPPrivateKey signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
+ if (signaturePrivateKey == null) {
+ throw new PgpGeneralException(
+ context.getString(R.string.error_could_not_extract_private_key));
+ }
+ updateProgress(R.string.progress_preparing_streams, 0, 100);
+
+ updateProgress(R.string.progress_preparing_signature, 30, 100);
+
+ int type = PGPSignature.CANONICAL_TEXT_DOCUMENT;
+// if (binary) {
+// type = PGPSignature.BINARY_DOCUMENT;
+// }
+
+ // content signer based on signing key algorithm and chosen hash algorithm
+ JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey
+ .getPublicKey().getAlgorithm(), signatureHashAlgorithm)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ PGPSignatureGenerator signatureGenerator = null;
+ PGPV3SignatureGenerator signatureV3Generator = null;
+ if (signatureForceV3) {
+ signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
+ signatureV3Generator.init(type, signaturePrivateKey);
+ } else {
+ signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
+ signatureGenerator.init(type, signaturePrivateKey);
+
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ String userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signingKeyRing));
+ spGen.setSignerUserID(false, userId);
+ signatureGenerator.setHashedSubpackets(spGen.generate());
+ }
+
+ updateProgress(R.string.progress_signing, 40, 100);
+
+ InputStream inStream = data.getInputStream();
+// if (binary) {
+// byte[] buffer = new byte[1 << 16];
+// int n = 0;
+// while ((n = inStream.read(buffer)) > 0) {
+// if (signatureForceV3) {
+// signatureV3Generator.update(buffer, 0, n);
+// } else {
+// signatureGenerator.update(buffer, 0, n);
+// }
+// }
+// } else {
+ final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
+ final byte[] newline = "\r\n".getBytes("UTF-8");
+
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (signatureForceV3) {
+ processLine(line, null, signatureV3Generator);
+ signatureV3Generator.update(newline);
+ } else {
+ processLine(line, null, signatureGenerator);
+ signatureGenerator.update(newline);
+ }
+ }
+// }
+
+ BCPGOutputStream bOut = new BCPGOutputStream(out);
+ if (signatureForceV3) {
+ signatureV3Generator.generate().encode(bOut);
+ } else {
+ signatureGenerator.generate().encode(bOut);
+ }
+ out.close();
+ outStream.close();
+
+ updateProgress(R.string.progress_done, 100, 100);
+ }
+
+
+ private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput,
+ final PGPSignatureGenerator pSignatureGenerator)
+ throws IOException, SignatureException {
+
+ if (pLine == null) {
+ return;
+ }
+
+ final char[] chars = pLine.toCharArray();
+ int len = chars.length;
+
+ while (len > 0) {
+ if (!Character.isWhitespace(chars[len - 1])) {
+ break;
+ }
+ len--;
+ }
+
+ final byte[] data = pLine.substring(0, len).getBytes("UTF-8");
+
+ if (pArmoredOutput != null) {
+ pArmoredOutput.write(data);
+ }
+ pSignatureGenerator.update(data);
+ }
+
+ private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput,
+ final PGPV3SignatureGenerator pSignatureGenerator)
+ throws IOException, SignatureException {
+
+ if (pLine == null) {
+ return;
+ }
+
+ final char[] chars = pLine.toCharArray();
+ int len = chars.length;
+
+ while (len > 0) {
+ if (!Character.isWhitespace(chars[len - 1])) {
+ break;
+ }
+ len--;
+ }
+
+ final byte[] data = pLine.substring(0, len).getBytes("UTF-8");
+
+ if (pArmoredOutput != null) {
+ pArmoredOutput.write(data);
+ }
+ pSignatureGenerator.update(data);
+ }
+
+}
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 3a00f3101..5f18ed6f9 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
@@ -110,7 +110,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(Constants.TAG, "Upgrading database from version " + oldVersion + " to " + newVersion);
- // Upgrade from oldVersion through all methods to newest one
+ // 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);
@@ -123,14 +123,17 @@ public class KeychainDatabase extends SQLiteOpenHelper {
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;
default:
break;
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 cd3007353..781f36758 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
@@ -359,7 +359,9 @@ public class KeychainProvider extends ContentProvider {
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.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);
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 12bc33995..ba931c61a 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
@@ -28,6 +28,7 @@ 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.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
@@ -210,6 +211,13 @@ public class ProviderHelper {
++userIdRank;
}
+ for (PGPSignature certification : new IterableIterator<PGPSignature>(masterKey.getSignaturesOfType(PGPSignature.POSITIVE_CERTIFICATION))) {
+ //TODO: how to do this?? we need to verify the signatures again and again when they are displayed...
+// if (certification.verify
+// operations.add(buildPublicKeyOperations(context, keyRingRowId, key, rank));
+ }
+
+
try {
context.getContentResolver().applyBatch(KeychainContract.CONTENT_AUTHORITY, operations);
} catch (RemoteException e) {
@@ -562,6 +570,26 @@ public class ProviderHelper {
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) {
ArrayList<String> output = new ArrayList<String>();
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 5e5735c88..9e517b93e 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
@@ -43,10 +43,11 @@ import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpImportExport;
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
-import org.sufficientlysecure.keychain.pgp.PgpOperation;
+import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
@@ -95,7 +96,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
public static final String ACTION_UPLOAD_KEYRING = Constants.INTENT_PREFIX + "UPLOAD_KEYRING";
public static final String ACTION_DOWNLOAD_AND_IMPORT_KEYS = Constants.INTENT_PREFIX + "QUERY_KEYRING";
- public static final String ACTION_SIGN_KEYRING = Constants.INTENT_PREFIX + "SIGN_KEYRING";
+ public static final String ACTION_CERTIFY_KEYRING = Constants.INTENT_PREFIX + "SIGN_KEYRING";
/* keys for data bundle */
@@ -119,11 +120,9 @@ public class KeychainIntentService extends IntentService implements ProgressDial
public static final String ENCRYPT_PROVIDER_URI = "provider_uri";
// decrypt/verify
- public static final String DECRYPT_SIGNED_ONLY = "signed_only";
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_LOOKUP_UNKNOWN_KEY = "lookup_unknownKey";
// save keyring
public static final String SAVE_KEYRING_NEW_PASSPHRASE = "new_passphrase";
@@ -166,8 +165,8 @@ public class KeychainIntentService extends IntentService implements ProgressDial
public static final String DOWNLOAD_KEY_LIST = "query_key_id";
// sign key
- public static final String SIGN_KEY_MASTER_KEY_ID = "sign_key_master_key_id";
- public static final String SIGN_KEY_PUB_KEY_ID = "sign_key_pub_key_id";
+ public static final String CERTIFY_KEY_MASTER_KEY_ID = "sign_key_master_key_id";
+ public static final String CERTIFY_KEY_PUB_KEY_ID = "sign_key_pub_key_id";
/*
* possible data keys as result send over messenger
@@ -189,10 +188,10 @@ public class KeychainIntentService extends IntentService implements ProgressDial
public static final String RESULT_SIGNATURE = "signature";
public static final String RESULT_SIGNATURE_KEY_ID = "signature_key_id";
public static final String RESULT_SIGNATURE_USER_ID = "signature_user_id";
+ public static final String RESULT_CLEARTEXT_SIGNATURE_ONLY = "signature_only";
public static final String RESULT_SIGNATURE_SUCCESS = "signature_success";
public static final String RESULT_SIGNATURE_UNKNOWN = "signature_unknown";
- public static final String RESULT_SIGNATURE_LOOKUP_KEY = "lookup_key";
// import
public static final String RESULT_IMPORT_ADDED = "added";
@@ -241,7 +240,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
String action = intent.getAction();
- // execute action from extra bundle
+ // executeServiceMethod action from extra bundle
if (ACTION_ENCRYPT_SIGN.equals(action)) {
try {
/* Input */
@@ -322,27 +321,41 @@ public class KeychainIntentService extends IntentService implements ProgressDial
}
/* Operation */
- PgpOperation operation = new PgpOperation(this, this, inputData, outStream);
+ PgpSignEncrypt.Builder builder =
+ new PgpSignEncrypt.Builder(this, inputData, outStream);
+ builder.progress(this);
+
if (generateSignature) {
Log.d(Constants.TAG, "generating signature...");
- operation.generateSignature(useAsciiArmor, false, secretKeyId,
- PassphraseCacheService.getCachedPassphrase(this, secretKeyId),
- Preferences.getPreferences(this).getDefaultHashAlgorithm(), Preferences
- .getPreferences(this).getForceV3Signatures());
+ 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...");
- operation.signText(secretKeyId, PassphraseCacheService.getCachedPassphrase(
- this, secretKeyId), Preferences.getPreferences(this)
- .getDefaultHashAlgorithm(), Preferences.getPreferences(this)
- .getForceV3Signatures());
+ 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...");
- operation.signAndEncrypt(useAsciiArmor, compressionId, encryptionKeyIds,
- encryptionPassphrase, Preferences.getPreferences(this)
- .getDefaultEncryptionAlgorithm(), secretKeyId, Preferences
- .getPreferences(this).getDefaultHashAlgorithm(), Preferences
- .getPreferences(this).getForceV3Signatures(),
- PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
+ 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();
}
outStream.close();
@@ -395,12 +408,9 @@ public class KeychainIntentService extends IntentService implements ProgressDial
long secretKeyId = data.getLong(ENCRYPT_SECRET_KEY_ID);
byte[] bytes = data.getByteArray(DECRYPT_CIPHERTEXT_BYTES);
- boolean signedOnly = data.getBoolean(DECRYPT_SIGNED_ONLY);
boolean returnBytes = data.getBoolean(DECRYPT_RETURN_BYTES);
boolean assumeSymmetricEncryption = data.getBoolean(DECRYPT_ASSUME_SYMMETRIC);
- boolean lookupUnknownKey = data.getBoolean(DECRYPT_LOOKUP_UNKNOWN_KEY);
-
InputStream inStream = null;
long inLength = -1;
InputData inputData = null;
@@ -474,14 +484,13 @@ public class KeychainIntentService extends IntentService implements ProgressDial
// verifyText and decrypt returning additional resultData values for the
// verification of signatures
- PgpOperation operation = new PgpOperation(this, this, inputData, outStream);
- if (signedOnly) {
- resultData = operation.verifyText(lookupUnknownKey);
- } else {
- resultData = operation.decryptAndVerify(
- PassphraseCacheService.getCachedPassphrase(this, secretKeyId),
- assumeSymmetricEncryption);
- }
+ PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, outStream);
+ builder.progress(this);
+
+ builder.assumeSymmetric(assumeSymmetricEncryption)
+ .passphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
+
+ resultData = builder.build().execute();
outStream.close();
@@ -785,19 +794,19 @@ public class KeychainIntentService extends IntentService implements ProgressDial
} catch (Exception e) {
sendErrorToHandler(e);
}
- } else if (ACTION_SIGN_KEYRING.equals(action)) {
+ } else if (ACTION_CERTIFY_KEYRING.equals(action)) {
try {
/* Input */
- long masterKeyId = data.getLong(SIGN_KEY_MASTER_KEY_ID);
- long pubKeyId = data.getLong(SIGN_KEY_PUB_KEY_ID);
+ long masterKeyId = data.getLong(CERTIFY_KEY_MASTER_KEY_ID);
+ long pubKeyId = data.getLong(CERTIFY_KEY_PUB_KEY_ID);
/* Operation */
String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this,
masterKeyId);
PgpKeyOperation keyOperation = new PgpKeyOperation(this, this);
- PGPPublicKeyRing signedPubKeyRing = keyOperation.signKey(masterKeyId, pubKeyId,
+ PGPPublicKeyRing signedPubKeyRing = keyOperation.certifyKey(masterKeyId, pubKeyId,
signaturePassPhrase);
// store the signed key in our local cache
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
index 5a338c757..dfea7eb04 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
@@ -25,6 +25,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
import android.widget.Toast;
public class KeychainIntentServiceHandler extends Handler {
@@ -60,7 +61,14 @@ public class KeychainIntentServiceHandler extends Handler {
}
public void showProgressDialog(FragmentActivity activity) {
- mProgressDialogFragment.show(activity.getSupportFragmentManager(), "progressDialog");
+ // TODO: This is a hack!, see http://stackoverflow.com/questions/10114324/show-dialogfragment-from-onactivityresult
+ final FragmentManager manager = activity.getSupportFragmentManager();
+ Handler handler = new Handler();
+ handler.post(new Runnable() {
+ public void run() {
+ mProgressDialogFragment.show(manager, "progressDialog");
+ }
+ });
}
@Override
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/exception/NoUserIdsException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/exception/NoUserIdsException.java
deleted file mode 100644
index 555303238..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/exception/NoUserIdsException.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.sufficientlysecure.keychain.service.exception;
-
-public class NoUserIdsException extends Exception {
-
- private static final long serialVersionUID = 7009311527126696207L;
-
- public NoUserIdsException(String message) {
- super(message);
- }
-} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/exception/UserInteractionRequiredException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/exception/UserInteractionRequiredException.java
deleted file mode 100644
index 1152d6796..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/exception/UserInteractionRequiredException.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.sufficientlysecure.keychain.service.exception;
-
-public class UserInteractionRequiredException extends Exception {
-
- private static final long serialVersionUID = -60128148603511936L;
-
- public UserInteractionRequiredException(String message) {
- super(message);
- }
-} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/exception/WrongPassphraseException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/exception/WrongPassphraseException.java
deleted file mode 100644
index 14b774eb5..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/exception/WrongPassphraseException.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.sufficientlysecure.keychain.service.exception;
-
-public class WrongPassphraseException extends Exception {
-
- private static final long serialVersionUID = -5309689232853485740L;
-
- public WrongPassphraseException(String message) {
- super(message);
- }
-} \ No newline at end of file
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
index 025929cfa..64c4c5e96 100644
--- 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
@@ -109,6 +109,15 @@ public class AppSettingsFragment extends Fragment implements
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);
@@ -182,7 +191,7 @@ public class AppSettingsFragment extends Fragment implements
// 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);
+ // Animation.RELATIVE_TO_SELF, 0.0f);u
// animation2.setDuration(150);
mAdvancedSettingsButton.setOnClickListener(new OnClickListener() {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java
index 575f76a22..34213bd3b 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 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
@@ -17,108 +17,46 @@
package org.sufficientlysecure.keychain.service.remote;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.regex.Matcher;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
-import org.openintents.openpgp.IOpenPgpCallback;
-import org.openintents.openpgp.IOpenPgpKeyIdsCallback;
import org.openintents.openpgp.IOpenPgpService;
-import org.openintents.openpgp.OpenPgpData;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult;
+import org.openintents.openpgp.util.OpenPgpConstants;
import org.spongycastle.util.Arrays;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.pgp.PgpHelper;
-import org.sufficientlysecure.keychain.pgp.PgpOperation;
-import org.sufficientlysecure.keychain.pgp.exception.NoAsymmetricEncryptionException;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
+import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.service.exception.NoUserIdsException;
-import org.sufficientlysecure.keychain.service.exception.UserInteractionRequiredException;
-import org.sufficientlysecure.keychain.service.exception.WrongPassphraseException;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
public class OpenPgpService extends RemoteService {
- private String getCachedPassphrase(long keyId, boolean allowUserInteraction)
- throws UserInteractionRequiredException {
- String passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), keyId);
-
- if (passphrase == null) {
- if (!allowUserInteraction) {
- throw new UserInteractionRequiredException(
- "Passphrase not found in cache, please enter your passphrase!");
- }
+ private static final int PRIVATE_REQUEST_CODE_PASSPHRASE = 551;
+ private static final int PRIVATE_REQUEST_CODE_USER_IDS = 552;
- Log.d(Constants.TAG, "No passphrase! Activity required!");
-
- // start passphrase dialog
- PassphraseActivityCallback callback = new PassphraseActivityCallback();
- Bundle extras = new Bundle();
- extras.putLong(RemoteServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
- pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_CACHE_PASSPHRASE, callback,
- extras);
-
- if (callback.isSuccess()) {
- Log.d(Constants.TAG, "New passphrase entered!");
-
- // get again after it was entered
- passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), keyId);
- } else {
- Log.d(Constants.TAG, "Passphrase dialog canceled!");
-
- return null;
- }
-
- }
-
- return passphrase;
- }
-
- public class PassphraseActivityCallback extends UserInputCallback {
-
- private boolean success = false;
-
- public boolean isSuccess() {
- return success;
- }
-
- @Override
- public void handleUserInput(Message msg) {
- if (msg.arg1 == OKAY) {
- success = true;
- } else {
- success = false;
- }
- }
- };
/**
* Search database for key ids based on emails.
- *
+ *
* @param encryptionUserIds
* @return
*/
- private long[] getKeyIdsFromEmails(String[] encryptionUserIds, boolean allowUserInteraction)
- throws UserInteractionRequiredException {
+ private Bundle getKeyIdsFromEmails(Bundle params, String[] encryptionUserIds) {
// find key ids to given emails in database
ArrayList<Long> keyIds = new ArrayList<Long>();
@@ -152,96 +90,129 @@ public class OpenPgpService extends RemoteService {
}
// allow the user to verify pub key selection
- if (allowUserInteraction && (missingUserIdsCheck || dublicateUserIdsCheck)) {
- SelectPubKeysActivityCallback callback = new SelectPubKeysActivityCallback();
-
- Bundle extras = new Bundle();
- extras.putLongArray(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
- extras.putStringArrayList(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
- extras.putStringArrayList(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS,
- dublicateUserIds);
-
- pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS, callback,
- extras);
-
- if (callback.isSuccess()) {
- Log.d(Constants.TAG, "New selection of pub keys!");
- keyIdsArray = callback.getPubKeyIds();
- } else {
- Log.d(Constants.TAG, "Pub key selection canceled!");
- return null;
- }
- }
-
- // if no user interaction is allow throw exceptions on duplicate or missing pub keys
- if (!allowUserInteraction) {
- if (missingUserIdsCheck)
- throw new UserInteractionRequiredException(
- "Pub keys for these user ids are missing:" + missingUserIds.toString());
- if (dublicateUserIdsCheck)
- throw new UserInteractionRequiredException(
- "More than one pub key with these user ids exist:"
- + dublicateUserIds.toString());
+ if (missingUserIdsCheck || dublicateUserIdsCheck) {
+ // build PendingIntent for passphrase input
+ 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(OpenPgpConstants.PI_RESULT_PARAMS, params);
+
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_USER_IDS, intent, 0);
+
+ // return PendingIntent to be executed by client
+ Bundle result = new Bundle();
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
+ result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
+
+ return result;
}
if (keyIdsArray.length == 0) {
return null;
}
- return keyIdsArray;
- }
-
- public class SelectPubKeysActivityCallback extends UserInputCallback {
- public static final String PUB_KEY_IDS = "pub_key_ids";
- private boolean success = false;
- private long[] pubKeyIds;
+ Bundle result = new Bundle();
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
+ result.putLongArray(OpenPgpConstants.PARAMS_KEY_IDS, keyIdsArray);
+ return result;
+ }
- public boolean isSuccess() {
- return success;
- }
+ private Bundle getPassphraseBundleIntent(Bundle params, long keyId) {
+ // build PendingIntent for passphrase input
+ Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
+ intent.setAction(RemoteServiceActivity.ACTION_CACHE_PASSPHRASE);
+ 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(OpenPgpConstants.PI_RESULT_PARAMS, params);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_PASSPHRASE, intent, 0);
+
+ // return PendingIntent to be executed by client
+ Bundle result = new Bundle();
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
+ result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
+
+ return result;
+ }
- public long[] getPubKeyIds() {
- return pubKeyIds;
- }
+ private Bundle signImpl(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output,
+ AppSettings appSettings) {
+ try {
+ boolean asciiArmor = params.getBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
- @Override
- public void handleUserInput(Message msg) {
- if (msg.arg1 == OKAY) {
- success = true;
- pubKeyIds = msg.getData().getLongArray(PUB_KEY_IDS);
+ // get passphrase from cache, if key has "no" passphrase, this returns an empty String
+ String passphrase;
+ if (params.containsKey(OpenPgpConstants.PARAMS_PASSPHRASE)) {
+ passphrase = params.getString(OpenPgpConstants.PARAMS_PASSPHRASE);
} else {
- success = false;
+ passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId());
+ }
+ if (passphrase == null) {
+ // get PendingIntent for passphrase input, add it to given params and return to client
+ Bundle passphraseBundle = getPassphraseBundleIntent(params, appSettings.getKeyId());
+ return passphraseBundle;
}
- }
- };
- private synchronized void getKeyIdsSafe(String[] userIds, boolean allowUserInteraction,
- IOpenPgpKeyIdsCallback callback, AppSettings appSettings) {
- try {
- long[] keyIds = getKeyIdsFromEmails(userIds, allowUserInteraction);
- if (keyIds == null) {
- throw new NoUserIdsException("No user ids!");
+ // Get Input- and OutputStream from ParcelFileDescriptor
+ InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
+ OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
+ try {
+ long inputLength = is.available();
+ InputData inputData = new InputData(is, inputLength);
+
+ // sign-only
+ PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
+ builder.enableAsciiArmorOutput(asciiArmor)
+ .signatureHashAlgorithm(appSettings.getHashAlgorithm())
+ .signatureForceV3(false)
+ .signatureKeyId(appSettings.getKeyId())
+ .signaturePassphrase(passphrase);
+ builder.build().execute();
+ } finally {
+ is.close();
+ os.close();
}
- callback.onSuccess(keyIds);
- } catch (UserInteractionRequiredException e) {
- callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
- } catch (NoUserIdsException e) {
- callbackOpenPgpError(callback, OpenPgpError.NO_USER_IDS, e.getMessage());
+ Bundle result = new Bundle();
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
+ return result;
} catch (Exception e) {
- callbackOpenPgpError(callback, OpenPgpError.GENERIC_ERROR, e.getMessage());
+ Bundle result = new Bundle();
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
+ result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
+ new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
+ return result;
}
}
- private synchronized void encryptAndSignSafe(OpenPgpData inputData,
- final OpenPgpData outputData, long[] keyIds, boolean allowUserInteraction,
- IOpenPgpCallback callback, AppSettings appSettings, boolean sign) {
+ private Bundle encryptAndSignImpl(Bundle params, ParcelFileDescriptor input,
+ ParcelFileDescriptor output, AppSettings appSettings,
+ boolean sign) {
try {
- // TODO: other options of OpenPgpData!
- byte[] inputBytes = getInput(inputData);
- boolean asciiArmor = false;
- if (outputData.getType() == OpenPgpData.TYPE_STRING) {
- asciiArmor = true;
+ boolean asciiArmor = params.getBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
+
+ long[] keyIds;
+ if (params.containsKey(OpenPgpConstants.PARAMS_KEY_IDS)) {
+ keyIds = params.getLongArray(OpenPgpConstants.PARAMS_KEY_IDS);
+ } else if (params.containsKey(OpenPgpConstants.PARAMS_USER_IDS)) {
+ // get key ids based on given user ids
+ String[] userIds = params.getStringArray(OpenPgpConstants.PARAMS_USER_IDS);
+ // give params through to activity...
+ Bundle result = getKeyIdsFromEmails(params, userIds);
+
+ if (result.getInt(OpenPgpConstants.RESULT_CODE, 0) == OpenPgpConstants.RESULT_CODE_SUCCESS) {
+ keyIds = result.getLongArray(OpenPgpConstants.PARAMS_KEY_IDS);
+ } else {
+ // if not success -> result contains a PendingIntent for user interaction
+ return result;
+ }
+ } else {
+ Bundle result = new Bundle();
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
+ result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
+ new OpenPgpError(OpenPgpError.GENERIC_ERROR, "Missing parameter user_ids or key_ids!"));
+ return result;
}
// add own key for encryption
@@ -249,350 +220,338 @@ public class OpenPgpService extends RemoteService {
keyIds[keyIds.length - 1] = appSettings.getKeyId();
// build InputData and write into OutputStream
- InputStream inputStream = new ByteArrayInputStream(inputBytes);
- long inputLength = inputBytes.length;
- InputData inputDt = new InputData(inputStream, inputLength);
-
- OutputStream outputStream = new ByteArrayOutputStream();
+ // Get Input- and OutputStream from ParcelFileDescriptor
+ InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
+ OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
+ try {
+ long inputLength = is.available();
+ InputData inputData = new InputData(is, inputLength);
+
+ PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
+ builder.enableAsciiArmorOutput(asciiArmor)
+ .compressionId(appSettings.getCompression())
+ .symmetricEncryptionAlgorithm(appSettings.getEncryptionAlgorithm())
+ .encryptionKeyIds(keyIds);
+
+ if (sign) {
+ String passphrase;
+ if (params.containsKey(OpenPgpConstants.PARAMS_PASSPHRASE)) {
+ passphrase = params.getString(OpenPgpConstants.PARAMS_PASSPHRASE);
+ } else {
+ passphrase = PassphraseCacheService.getCachedPassphrase(getContext(),
+ appSettings.getKeyId());
+ }
+ if (passphrase == null) {
+ // get PendingIntent for passphrase input, add it to given params and return to client
+ Bundle passphraseBundle = getPassphraseBundleIntent(params, appSettings.getKeyId());
+ return passphraseBundle;
+ }
- PgpOperation operation = new PgpOperation(getContext(), null, inputDt, outputStream);
- if (sign) {
- String passphrase = getCachedPassphrase(appSettings.getKeyId(),
- allowUserInteraction);
- if (passphrase == null) {
- throw new WrongPassphraseException("No or wrong passphrase!");
+ // sign and encrypt
+ builder.signatureHashAlgorithm(appSettings.getHashAlgorithm())
+ .signatureForceV3(false)
+ .signatureKeyId(appSettings.getKeyId())
+ .signaturePassphrase(passphrase);
+ } else {
+ // encrypt only
+ builder.signatureKeyId(Id.key.none);
}
-
- operation.signAndEncrypt(asciiArmor, appSettings.getCompression(), keyIds, null,
- appSettings.getEncryptionAlgorithm(), appSettings.getKeyId(),
- appSettings.getHashAlgorithm(), true, passphrase);
- } else {
- operation.signAndEncrypt(asciiArmor, appSettings.getCompression(), keyIds, null,
- appSettings.getEncryptionAlgorithm(), Id.key.none,
- appSettings.getHashAlgorithm(), true, null);
+ // execute PGP operation!
+ builder.build().execute();
+ } finally {
+ is.close();
+ os.close();
}
- outputStream.close();
-
- byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
-
- OpenPgpData output = null;
- if (asciiArmor) {
- output = new OpenPgpData(new String(outputBytes));
- } else {
- output = new OpenPgpData(outputBytes);
- }
-
- // return over handler on client side
- callback.onSuccess(output, null);
- } catch (UserInteractionRequiredException e) {
- callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
- } catch (WrongPassphraseException e) {
- callbackOpenPgpError(callback, OpenPgpError.NO_OR_WRONG_PASSPHRASE, e.getMessage());
- } catch (Exception e) {
- callbackOpenPgpError(callback, OpenPgpError.GENERIC_ERROR, e.getMessage());
- }
- }
-
- // TODO: asciiArmor?!
- private void signSafe(byte[] inputBytes, boolean allowUserInteraction,
- IOpenPgpCallback callback, AppSettings appSettings) {
- try {
- // build InputData and write into OutputStream
- InputStream inputStream = new ByteArrayInputStream(inputBytes);
- long inputLength = inputBytes.length;
- InputData inputData = new InputData(inputStream, inputLength);
-
- OutputStream outputStream = new ByteArrayOutputStream();
-
- String passphrase = getCachedPassphrase(appSettings.getKeyId(), allowUserInteraction);
- if (passphrase == null) {
- throw new WrongPassphraseException("No or wrong passphrase!");
- }
-
- PgpOperation operation = new PgpOperation(getContext(), null, inputData, outputStream);
- operation.signText(appSettings.getKeyId(), passphrase, appSettings.getHashAlgorithm(),
- Preferences.getPreferences(this).getForceV3Signatures());
-
- outputStream.close();
-
- byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
- OpenPgpData output = new OpenPgpData(new String(outputBytes));
-
- // return over handler on client side
- callback.onSuccess(output, null);
- } catch (UserInteractionRequiredException e) {
- callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
- } catch (WrongPassphraseException e) {
- callbackOpenPgpError(callback, OpenPgpError.NO_OR_WRONG_PASSPHRASE, e.getMessage());
+ Bundle result = new Bundle();
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
+ return result;
} catch (Exception e) {
- callbackOpenPgpError(callback, OpenPgpError.GENERIC_ERROR, e.getMessage());
+ Bundle result = new Bundle();
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
+ result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
+ new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
+ return result;
}
}
- private synchronized void decryptAndVerifySafe(byte[] inputBytes, boolean allowUserInteraction,
- IOpenPgpCallback callback, AppSettings appSettings) {
+ private Bundle decryptAndVerifyImpl(Bundle params, ParcelFileDescriptor input,
+ ParcelFileDescriptor output, AppSettings appSettings) {
try {
- // TODO: this is not really needed
- // checked if it is text with BEGIN and END tags
- String message = new String(inputBytes);
- Log.d(Constants.TAG, "in: " + message);
- boolean signedOnly = false;
- Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(message);
- if (matcher.matches()) {
- Log.d(Constants.TAG, "PGP_MESSAGE matched");
- message = matcher.group(1);
- // replace non breakable spaces
- message = message.replaceAll("\\xa0", " ");
-
- // overwrite inputBytes
- inputBytes = message.getBytes();
- } else {
- matcher = PgpHelper.PGP_SIGNED_MESSAGE.matcher(message);
- if (matcher.matches()) {
- signedOnly = true;
- Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched");
- message = matcher.group(1);
- // replace non breakable spaces
- message = message.replaceAll("\\xa0", " ");
-
- // overwrite inputBytes
- inputBytes = message.getBytes();
+ // Get Input- and OutputStream from ParcelFileDescriptor
+ InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
+ OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
+ OpenPgpSignatureResult sigResult = null;
+ try {
+
+// PGPUtil.getDecoderStream(is)
+ // TODOs API 2.0:
+ // implement verify-only!
+ // fix the mess: http://stackoverflow.com/questions/148130/how-do-i-peek-at-the-first-two-bytes-in-an-inputstream
+ // should we allow to decrypt everything under every key id or only the one set?
+ // TODO: instead of trying to get the passphrase before
+ // pause stream when passphrase is missing and then resume
+
+
+ // TODO: this is not really needed
+ // checked if it is text with BEGIN and END tags
+// String message = new String(inputBytes);
+// Log.d(Constants.TAG, "in: " + message);
+// boolean signedOnly = false;
+// Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(message);
+// if (matcher.matches()) {
+// Log.d(Constants.TAG, "PGP_MESSAGE matched");
+// message = matcher.group(1);
+// // replace non breakable spaces
+// message = message.replaceAll("\\xa0", " ");
+//
+// // overwrite inputBytes
+// inputBytes = message.getBytes();
+// } else {
+// matcher = PgpHelper.PGP_SIGNED_MESSAGE.matcher(message);
+// if (matcher.matches()) {
+// signedOnly = true;
+// Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched");
+// message = matcher.group(1);
+// // replace non breakable spaces
+// message = message.replaceAll("\\xa0", " ");
+//
+// // overwrite inputBytes
+// inputBytes = message.getBytes();
+// } else {
+// Log.d(Constants.TAG, "Nothing matched! Binary?");
+// }
+// }
+ // END TODO
+
+// Log.d(Constants.TAG, "in: " + new String(inputBytes));
+
+ // TODO: This allows to decrypt messages with ALL secret keys, not only the one for the
+ // app, Fix this?
+
+// String passphrase = null;
+// if (!signedOnly) {
+// // BEGIN Get key
+// // TODO: this input stream is consumed after PgpMain.getDecryptionKeyId()... do it
+// // better!
+// InputStream inputStream2 = new ByteArrayInputStream(inputBytes);
+//
+// // TODO: duplicates functions from DecryptActivity!
+// long secretKeyId;
+// try {
+// if (inputStream2.markSupported()) {
+// // should probably set this to the max size of two
+// // pgpF objects, if it even needs to be anything other
+// // than 0.
+// inputStream2.mark(200);
+// }
+// secretKeyId = PgpHelper.getDecryptionKeyId(this, inputStream2);
+// if (secretKeyId == Id.key.none) {
+// throw new PgpGeneralException(getString(R.string.error_no_secret_key_found));
+// }
+// } catch (NoAsymmetricEncryptionException e) {
+// if (inputStream2.markSupported()) {
+// inputStream2.reset();
+// }
+// secretKeyId = Id.key.symmetric;
+// if (!PgpDecryptVerify.hasSymmetricEncryption(this, inputStream2)) {
+// throw new PgpGeneralException(
+// getString(R.string.error_no_known_encryption_found));
+// }
+// // we do not support symmetric decryption from the API!
+// throw new Exception("Symmetric decryption is not supported!");
+// }
+//
+// Log.d(Constants.TAG, "secretKeyId " + secretKeyId);
+
+ // NOTE: currently this only gets the passphrase for the key set for this client
+ String passphrase;
+ if (params.containsKey(OpenPgpConstants.PARAMS_PASSPHRASE)) {
+ passphrase = params.getString(OpenPgpConstants.PARAMS_PASSPHRASE);
} else {
- Log.d(Constants.TAG, "Nothing matched! Binary?");
+ passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId());
}
- }
- // END TODO
-
- Log.d(Constants.TAG, "in: " + new String(inputBytes));
-
- // TODO: This allows to decrypt messages with ALL secret keys, not only the one for the
- // app, Fix this?
-
- String passphrase = null;
- if (!signedOnly) {
- // BEGIN Get key
- // TODO: this input stream is consumed after PgpMain.getDecryptionKeyId()... do it
- // better!
- InputStream inputStream2 = new ByteArrayInputStream(inputBytes);
-
- // TODO: duplicates functions from DecryptActivity!
- long secretKeyId;
- try {
- if (inputStream2.markSupported()) {
- // should probably set this to the max size of two
- // pgpF objects, if it even needs to be anything other
- // than 0.
- inputStream2.mark(200);
- }
- secretKeyId = PgpHelper.getDecryptionKeyId(this, inputStream2);
- if (secretKeyId == Id.key.none) {
- throw new PgpGeneralException(getString(R.string.error_no_secret_key_found));
- }
- } catch (NoAsymmetricEncryptionException e) {
- if (inputStream2.markSupported()) {
- inputStream2.reset();
- }
- secretKeyId = Id.key.symmetric;
- if (!PgpOperation.hasSymmetricEncryption(this, inputStream2)) {
- throw new PgpGeneralException(
- getString(R.string.error_no_known_encryption_found));
- }
- // we do not support symmetric decryption from the API!
- throw new Exception("Symmetric decryption is not supported!");
+ if (passphrase == null) {
+ // get PendingIntent for passphrase input, add it to given params and return to client
+ Bundle passphraseBundle = getPassphraseBundleIntent(params, appSettings.getKeyId());
+ return passphraseBundle;
}
+// }
- Log.d(Constants.TAG, "secretKeyId " + secretKeyId);
+ // build InputData and write into OutputStream
+ long inputLength = is.available();
+ InputData inputData = new InputData(is, inputLength);
- passphrase = getCachedPassphrase(secretKeyId, allowUserInteraction);
- if (passphrase == null) {
- throw new WrongPassphraseException("No or wrong passphrase!");
- }
- }
- // build InputData and write into OutputStream
- InputStream inputStream = new ByteArrayInputStream(inputBytes);
- long inputLength = inputBytes.length;
- InputData inputData = new InputData(inputStream, inputLength);
+ Bundle outputBundle;
+ PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, os);
- OutputStream outputStream = new ByteArrayOutputStream();
+// if (signedOnly) {
+// outputBundle = builder.build().verifyText();
+// } else {
+ builder.assumeSymmetric(false)
+ .passphrase(passphrase);
- Bundle outputBundle;
- PgpOperation operation = new PgpOperation(getContext(), null, inputData, outputStream);
- if (signedOnly) {
- // TODO: download missing keys from keyserver?
- outputBundle = operation.verifyText(false);
- } else {
- outputBundle = operation.decryptAndVerify(passphrase, false);
- }
+ // Do we want to do this: instead of trying to get the passphrase before
+ // pause stream when passphrase is missing and then resume???
- outputStream.close();
+ // TODO: this also decrypts with other secret keys without passphrase!!!
+ outputBundle = builder.build().execute();
+// }
- byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
+// outputStream.close();
- // get signature informations from bundle
- boolean signature = outputBundle.getBoolean(KeychainIntentService.RESULT_SIGNATURE);
+// byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
- OpenPgpSignatureResult sigResult = null;
- if (signature) {
- long signatureKeyId = outputBundle
- .getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID);
- String signatureUserId = outputBundle
- .getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID);
- boolean signatureSuccess = outputBundle
- .getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS);
- boolean signatureUnknown = outputBundle
- .getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN);
-
- int signatureStatus = OpenPgpSignatureResult.SIGNATURE_ERROR;
- if (signatureSuccess) {
- signatureStatus = OpenPgpSignatureResult.SIGNATURE_SUCCESS_TRUSTED;
- } else if (signatureUnknown) {
- signatureStatus = OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY;
- }
+ // get signature informations from bundle
+ boolean signature = outputBundle.getBoolean(KeychainIntentService.RESULT_SIGNATURE, false);
- sigResult = new OpenPgpSignatureResult(signatureStatus, signatureUserId,
- signedOnly, signatureKeyId);
+ if (signature) {
+ long signatureKeyId = outputBundle
+ .getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, 0);
+ String signatureUserId = outputBundle
+ .getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID);
+ boolean signatureSuccess = outputBundle
+ .getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false);
+ boolean signatureUnknown = outputBundle
+ .getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, false);
+ boolean signatureOnly = outputBundle
+ .getBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, false);
+
+ int signatureStatus = OpenPgpSignatureResult.SIGNATURE_ERROR;
+ if (signatureSuccess) {
+ signatureStatus = OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED;
+ } else if (signatureUnknown) {
+ signatureStatus = OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY;
+ }
+
+ sigResult = new OpenPgpSignatureResult(signatureStatus, signatureUserId,
+ signatureOnly, signatureKeyId);
+ }
+ } finally {
+ is.close();
+ os.close();
}
- OpenPgpData output = new OpenPgpData(new String(outputBytes));
-
- // return over handler on client side
- callback.onSuccess(output, sigResult);
- } catch (UserInteractionRequiredException e) {
- callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
- } catch (WrongPassphraseException e) {
- callbackOpenPgpError(callback, OpenPgpError.NO_OR_WRONG_PASSPHRASE, e.getMessage());
+
+ Bundle result = new Bundle();
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
+ result.putParcelable(OpenPgpConstants.RESULT_SIGNATURE, sigResult);
+ return result;
} catch (Exception e) {
- callbackOpenPgpError(callback, OpenPgpError.GENERIC_ERROR, e.getMessage());
+ Bundle result = new Bundle();
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
+ result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
+ new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
+ return result;
}
}
+ private Bundle getKeyIdsImpl(Bundle params) {
+ // get key ids based on given user ids
+ String[] userIds = params.getStringArray(OpenPgpConstants.PARAMS_USER_IDS);
+ Bundle result = getKeyIdsFromEmails(params, userIds);
+ return result;
+ }
+
/**
- * Returns error to IOpenPgpCallback
- *
- * @param callback
- * @param errorId
- * @param message
+ * Check requirements:
+ * - params != null
+ * - has supported API version
+ * - is allowed to call the service (access has been granted)
+ *
+ * @param params
+ * @return null if everything is okay, or a Bundle with an error/PendingIntent
*/
- private void callbackOpenPgpError(IOpenPgpCallback callback, int errorId, String message) {
- try {
- callback.onError(new OpenPgpError(0, message));
- } catch (Exception t) {
- Log.e(Constants.TAG,
- "Exception while returning OpenPgpError to client via callback.onError()", t);
+ private Bundle checkRequirements(Bundle params) {
+ // params Bundle is required!
+ if (params == null) {
+ Bundle result = new Bundle();
+ OpenPgpError error = new OpenPgpError(OpenPgpError.GENERIC_ERROR, "params Bundle required!");
+ result.putParcelable(OpenPgpConstants.RESULT_ERRORS, error);
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
+ return result;
}
- }
- private void callbackOpenPgpError(IOpenPgpKeyIdsCallback callback, int errorId, String message) {
- try {
- callback.onError(new OpenPgpError(0, message));
- } catch (Exception t) {
- Log.e(Constants.TAG,
- "Exception while returning OpenPgpError to client via callback.onError()", t);
+ // version code is required and needs to correspond to version code of service!
+ if (params.getInt(OpenPgpConstants.PARAMS_API_VERSION) != OpenPgpConstants.API_VERSION) {
+ Bundle result = new Bundle();
+ OpenPgpError error = new OpenPgpError(OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!");
+ result.putParcelable(OpenPgpConstants.RESULT_ERRORS, error);
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
+ return result;
}
+
+ // check if caller is allowed to access openpgp keychain
+ Bundle result = isAllowed(params);
+ if (result != null) {
+ return result;
+ }
+
+ return null;
}
+ // TODO: multi-threading
private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() {
@Override
- public void encrypt(final OpenPgpData input, final OpenPgpData output, final long[] keyIds,
- final IOpenPgpCallback callback) throws RemoteException {
- final AppSettings settings = getAppSettings();
-
- Runnable r = new Runnable() {
- @Override
- public void run() {
- encryptAndSignSafe(input, output, keyIds, true, callback, settings, false);
- }
- };
-
- checkAndEnqueue(r);
- }
+ public Bundle sign(Bundle params, final ParcelFileDescriptor input, final ParcelFileDescriptor output) {
+ final AppSettings appSettings = getAppSettings();
- @Override
- public void signAndEncrypt(final OpenPgpData input, final OpenPgpData output,
- final long[] keyIds, final IOpenPgpCallback callback) throws RemoteException {
- final AppSettings settings = getAppSettings();
-
- Runnable r = new Runnable() {
- @Override
- public void run() {
- encryptAndSignSafe(input, output, keyIds, true, callback, settings, true);
- }
- };
+ Bundle errorResult = checkRequirements(params);
+ if (errorResult != null) {
+ return errorResult;
+ }
- checkAndEnqueue(r);
+ return signImpl(params, input, output, appSettings);
}
@Override
- public void sign(final OpenPgpData input, final OpenPgpData output,
- final IOpenPgpCallback callback) throws RemoteException {
- final AppSettings settings = getAppSettings();
-
- Runnable r = new Runnable() {
- @Override
- public void run() {
- signSafe(getInput(input), true, callback, settings);
- }
- };
+ public Bundle encrypt(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output) {
+ final AppSettings appSettings = getAppSettings();
+
+ Bundle errorResult = checkRequirements(params);
+ if (errorResult != null) {
+ return errorResult;
+ }
- checkAndEnqueue(r);
+ return encryptAndSignImpl(params, input, output, appSettings, false);
}
@Override
- public void decryptAndVerify(final OpenPgpData input, final OpenPgpData output,
- final IOpenPgpCallback callback) throws RemoteException {
+ public Bundle signAndEncrypt(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output) {
+ final AppSettings appSettings = getAppSettings();
- final AppSettings settings = getAppSettings();
-
- Runnable r = new Runnable() {
- @Override
- public void run() {
- decryptAndVerifySafe(getInput(input), true, callback, settings);
- }
- };
+ Bundle errorResult = checkRequirements(params);
+ if (errorResult != null) {
+ return errorResult;
+ }
- checkAndEnqueue(r);
+ return encryptAndSignImpl(params, input, output, appSettings, true);
}
@Override
- public void getKeyIds(final String[] userIds, final boolean allowUserInteraction,
- final IOpenPgpKeyIdsCallback callback) throws RemoteException {
-
- final AppSettings settings = getAppSettings();
+ public Bundle decryptAndVerify(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output) {
+ final AppSettings appSettings = getAppSettings();
- Runnable r = new Runnable() {
- @Override
- public void run() {
- getKeyIdsSafe(userIds, allowUserInteraction, callback, settings);
- }
- };
+ Bundle errorResult = checkRequirements(params);
+ if (errorResult != null) {
+ return errorResult;
+ }
- checkAndEnqueue(r);
+ return decryptAndVerifyImpl(params, input, output, appSettings);
}
- };
-
- private static byte[] getInput(OpenPgpData data) {
- // TODO: support Uri and ParcelFileDescriptor
-
- byte[] inBytes = null;
- switch (data.getType()) {
- case OpenPgpData.TYPE_STRING:
- inBytes = data.getString().getBytes();
- break;
-
- case OpenPgpData.TYPE_BYTE_ARRAY:
- inBytes = data.getBytes();
- break;
+ @Override
+ public Bundle getKeyIds(Bundle params) {
+ Bundle errorResult = checkRequirements(params);
+ if (errorResult != null) {
+ return errorResult;
+ }
- default:
- Log.e(Constants.TAG, "Uri and ParcelFileDescriptor not supported right now!");
- break;
+ return getKeyIdsImpl(params);
}
- return inBytes;
- }
+ };
@Override
public IBinder onBind(Intent intent) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java
index bc513d532..cfd2b9ec3 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 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
@@ -19,17 +19,16 @@ package org.sufficientlysecure.keychain.service.remote;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.TimeUnit;
+import org.openintents.openpgp.OpenPgpError;
+import org.openintents.openpgp.util.OpenPgpConstants;
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.service.exception.WrongPackageSignatureException;
import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.PausableThreadPoolExecutor;
+import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@@ -50,66 +49,19 @@ import android.os.Messenger;
public abstract class RemoteService extends Service {
Context mContext;
- private final ArrayBlockingQueue<Runnable> mPoolQueue = new ArrayBlockingQueue<Runnable>(100);
- // TODO: Are these parameters okay?
- private PausableThreadPoolExecutor mThreadPool = new PausableThreadPoolExecutor(2, 4, 10,
- TimeUnit.SECONDS, mPoolQueue);
+ private static final int PRIVATE_REQUEST_CODE_REGISTER = 651;
+ private static final int PRIVATE_REQUEST_CODE_ERROR = 652;
- private final Object userInputLock = new Object();
-
- /**
- * Override handleUserInput() to handle OKAY (1) and CANCEL (0). After handling the waiting
- * threads will be notified and the queue resumed
- */
- protected class UserInputCallback extends BaseCallback {
-
- public void handleUserInput(Message msg) {
- }
-
- @Override
- public boolean handleMessage(Message msg) {
- handleUserInput(msg);
-
- // resume
- synchronized (userInputLock) {
- userInputLock.notifyAll();
- }
- mThreadPool.resume();
- return true;
- }
-
- }
-
- /**
- * Extends Handler.Callback with OKAY (1), CANCEL (0) variables
- */
- private class BaseCallback implements Handler.Callback {
- public static final int OKAY = 1;
- public static final int CANCEL = 0;
-
- @Override
- public boolean handleMessage(Message msg) {
- return false;
- }
-
- }
public Context getContext() {
return mContext;
}
- /**
- * Should be used from Stub implementations of AIDL interfaces to enqueue a runnable for
- * execution
- *
- * @param r
- */
- protected void checkAndEnqueue(Runnable r) {
+ protected Bundle isAllowed(Bundle params) {
try {
if (isCallerAllowed(false)) {
- mThreadPool.execute(r);
- Log.d(Constants.TAG, "Enqueued runnable…");
+ return null;
} else {
String[] callingPackages = getPackageManager().getPackagesForUid(
Binder.getCallingUid());
@@ -121,32 +73,46 @@ public abstract class RemoteService extends Service {
packageSignature = getPackageSignature(packageName);
} catch (NameNotFoundException e) {
Log.e(Constants.TAG, "Should not happen, returning!", e);
- return;
- }
- Log.e(Constants.TAG,
- "Not allowed to use service! Starting activity for registration!");
- Bundle extras = new Bundle();
- extras.putString(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName);
- extras.putByteArray(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature);
- RegisterActivityCallback callback = new RegisterActivityCallback();
-
- pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_REGISTER, callback,
- extras);
-
- if (callback.isAllowed()) {
- mThreadPool.execute(r);
- Log.d(Constants.TAG, "Enqueued runnable…");
- } else {
- Log.d(Constants.TAG, "User disallowed app!");
+ // return error
+ Bundle result = new Bundle();
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
+ result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
+ new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
+ return result;
}
+ Log.e(Constants.TAG, "Not allowed to use service! return PendingIntent for registration!");
+
+ Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
+ intent.setAction(RemoteServiceActivity.ACTION_REGISTER);
+ intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature);
+ intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
+
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_REGISTER, intent, 0);
+
+ // return PendingIntent to be executed by client
+ Bundle result = new Bundle();
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
+ result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
+
+ return result;
}
} catch (WrongPackageSignatureException e) {
- Log.e(Constants.TAG, e.getMessage());
+ Log.e(Constants.TAG, "wrong signature!", e);
+
+ 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));
+ intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
+
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_ERROR, intent, 0);
+
+ // return PendingIntent to be executed by client
+ Bundle result = new Bundle();
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
+ result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
- Bundle extras = new Bundle();
- extras.putString(RemoteServiceActivity.EXTRA_ERROR_MESSAGE,
- getString(R.string.api_error_wrong_signature));
- pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_ERROR_MESSAGE, null, extras);
+ return result;
}
}
@@ -161,40 +127,8 @@ public abstract class RemoteService extends Service {
}
/**
- * Locks current thread and pauses execution of runnables and starts activity for user input
- *
- * @param action
- * @param messenger
- * @param extras
- */
- protected void pauseAndStartUserInteraction(String action, BaseCallback callback, Bundle extras) {
- synchronized (userInputLock) {
- mThreadPool.pause();
-
- Log.d(Constants.TAG, "starting activity...");
- Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setAction(action);
-
- Messenger messenger = new Messenger(new Handler(getMainLooper(), callback));
-
- extras.putParcelable(RemoteServiceActivity.EXTRA_MESSENGER, messenger);
- intent.putExtras(extras);
-
- startActivity(intent);
-
- // lock current thread for user input
- try {
- userInputLock.wait();
- } catch (InterruptedException e) {
- Log.e(Constants.TAG, "CryptoService", e);
- }
- }
- }
-
- /**
* Retrieves AppSettings from database for the application calling this remote service
- *
+ *
* @return
*/
protected AppSettings getAppSettings() {
@@ -215,66 +149,11 @@ public abstract class RemoteService extends Service {
return null;
}
- class RegisterActivityCallback extends BaseCallback {
- public static final String PACKAGE_NAME = "package_name";
-
- private boolean allowed = false;
- private String packageName;
-
- public boolean isAllowed() {
- return allowed;
- }
-
- public String getPackageName() {
- return packageName;
- }
-
- @Override
- public boolean handleMessage(Message msg) {
- if (msg.arg1 == OKAY) {
- allowed = true;
- packageName = msg.getData().getString(PACKAGE_NAME);
-
- // resume threads
- try {
- if (isPackageAllowed(packageName)) {
- synchronized (userInputLock) {
- userInputLock.notifyAll();
- }
- mThreadPool.resume();
- } else {
- // Should not happen!
- Log.e(Constants.TAG, "Should not happen! Emergency shutdown!");
- mThreadPool.shutdownNow();
- }
- } catch (WrongPackageSignatureException e) {
- Log.e(Constants.TAG, e.getMessage());
-
- Bundle extras = new Bundle();
- extras.putString(RemoteServiceActivity.EXTRA_ERROR_MESSAGE,
- getString(R.string.api_error_wrong_signature));
- pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_ERROR_MESSAGE, null,
- extras);
- }
- } else {
- allowed = false;
-
- synchronized (userInputLock) {
- userInputLock.notifyAll();
- }
- mThreadPool.resume();
- }
- return true;
- }
-
- }
-
/**
* Checks if process that binds to this service (i.e. the package name corresponding to the
* process) is in the list of allowed package names.
- *
- * @param allowOnlySelf
- * allow only Keychain app itself
+ *
+ * @param allowOnlySelf allow only Keychain app itself
* @return true if process is allowed to use this service
* @throws WrongPackageSignatureException
*/
@@ -308,7 +187,7 @@ public abstract class RemoteService extends Service {
/**
* Checks if packageName is a registered app for the API. Does not return true for own package!
- *
+ *
* @param packageName
* @return
* @throws WrongPackageSignatureException
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java
index f1e203733..af8e3ade8 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 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
@@ -17,8 +17,15 @@
package org.sufficientlysecure.keychain.service.remote;
-import java.util.ArrayList;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.support.v7.app.ActionBarActivity;
+import android.view.View;
+import org.openintents.openpgp.util.OpenPgpConstants;
import org.sufficientlysecure.htmltextview.HtmlTextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
@@ -30,15 +37,7 @@ import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.support.v7.app.ActionBarActivity;
-import android.view.View;
-import android.widget.Toast;
+import java.util.ArrayList;
public class RemoteServiceActivity extends ActionBarActivity {
@@ -64,17 +63,11 @@ public class RemoteServiceActivity extends ActionBarActivity {
// error message
public static final String EXTRA_ERROR_MESSAGE = "error_message";
- private Messenger mMessenger;
-
// register view
private AppSettingsFragment mSettingsFragment;
// select pub keys view
private SelectPublicKeyFragment mSelectFragment;
- // has the user clicked one of the buttons
- // or do we need to handle the callback in onStop()
- private boolean finishHandled;
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -82,36 +75,12 @@ public class RemoteServiceActivity extends ActionBarActivity {
handleActions(getIntent(), savedInstanceState);
}
- @Override
- protected void onStop() {
- super.onStop();
-
- if (!finishHandled) {
- Message msg = Message.obtain();
- msg.arg1 = RemoteService.RegisterActivityCallback.CANCEL;
- try {
- mMessenger.send(msg);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoServiceActivity", e);
- }
- }
- }
-
protected void handleActions(Intent intent, Bundle savedInstanceState) {
- finishHandled = false;
String action = intent.getAction();
- Bundle extras = intent.getExtras();
+ final Bundle extras = intent.getExtras();
- if (extras == null) {
- extras = new Bundle();
- }
- mMessenger = extras.getParcelable(EXTRA_MESSENGER);
-
- /**
- * com.android.crypto actions
- */
if (ACTION_REGISTER.equals(action)) {
final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE);
@@ -125,44 +94,27 @@ public class RemoteServiceActivity extends ActionBarActivity {
// user needs to select a key!
if (mSettingsFragment.getAppSettings().getKeyId() == Id.key.none) {
- Toast.makeText(RemoteServiceActivity.this,
- R.string.api_register_error_select_key, Toast.LENGTH_LONG)
- .show();
+ mSettingsFragment.setErrorOnSelectKeyFragment(
+ getString(R.string.api_register_error_select_key));
} else {
ProviderHelper.insertApiApp(RemoteServiceActivity.this,
mSettingsFragment.getAppSettings());
- Message msg = Message.obtain();
- msg.arg1 = RemoteService.RegisterActivityCallback.OKAY;
- Bundle data = new Bundle();
- data.putString(RemoteService.RegisterActivityCallback.PACKAGE_NAME,
- packageName);
- msg.setData(data);
- try {
- mMessenger.send(msg);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoServiceActivity", e);
- }
-
- finishHandled = true;
- finish();
+ // give params through for new service call
+ Bundle oldParams = extras.getBundle(OpenPgpConstants.PI_RESULT_PARAMS);
+
+ Intent finishIntent = new Intent();
+ finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, oldParams);
+ RemoteServiceActivity.this.setResult(RESULT_OK, finishIntent);
+ RemoteServiceActivity.this.finish();
}
}
}, R.string.api_register_disallow, new View.OnClickListener() {
@Override
public void onClick(View v) {
// Disallow
-
- Message msg = Message.obtain();
- msg.arg1 = RemoteService.RegisterActivityCallback.CANCEL;
- try {
- mMessenger.send(msg);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoServiceActivity", e);
- }
-
- finishHandled = true;
- finish();
+ RemoteServiceActivity.this.setResult(RESULT_CANCELED);
+ RemoteServiceActivity.this.finish();
}
}
);
@@ -176,8 +128,9 @@ public class RemoteServiceActivity extends ActionBarActivity {
mSettingsFragment.setAppSettings(settings);
} else if (ACTION_CACHE_PASSPHRASE.equals(action)) {
long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID);
+ Bundle oldParams = extras.getBundle(OpenPgpConstants.PI_RESULT_PARAMS);
- showPassphraseDialog(secretKeyId);
+ showPassphraseDialog(oldParams, secretKeyId);
} else if (ACTION_SELECT_PUB_KEYS.equals(action)) {
long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
ArrayList<String> missingUserIds = intent
@@ -185,8 +138,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
ArrayList<String> dublicateUserIds = intent
.getStringArrayListExtra(EXTRA_DUBLICATE_USER_IDS);
- String text = new String();
- text += "<b>" + getString(R.string.api_select_pub_keys_text) + "</b>";
+ // TODO: do this with spannable instead of HTML to prevent parsing failures with weird user ids
+ String text = "<b>" + getString(R.string.api_select_pub_keys_text) + "</b>";
text += "<br/><br/>";
if (missingUserIds != null && missingUserIds.size() > 0) {
text += getString(R.string.api_select_pub_keys_missing_text);
@@ -213,40 +166,22 @@ public class RemoteServiceActivity extends ActionBarActivity {
new View.OnClickListener() {
@Override
public void onClick(View v) {
- // ok
-
- Message msg = Message.obtain();
- msg.arg1 = OpenPgpService.SelectPubKeysActivityCallback.OKAY;
- Bundle data = new Bundle();
- data.putLongArray(
- OpenPgpService.SelectPubKeysActivityCallback.PUB_KEY_IDS,
+ // add key ids to params Bundle for new request
+ Bundle params = extras.getBundle(OpenPgpConstants.PI_RESULT_PARAMS);
+ params.putLongArray(OpenPgpConstants.PARAMS_KEY_IDS,
mSelectFragment.getSelectedMasterKeyIds());
- msg.setData(data);
- try {
- mMessenger.send(msg);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoServiceActivity", e);
- }
- finishHandled = true;
- finish();
+ Intent finishIntent = new Intent();
+ finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
+ RemoteServiceActivity.this.setResult(RESULT_OK, finishIntent);
+ RemoteServiceActivity.this.finish();
}
}, R.string.btn_do_not_save, new View.OnClickListener() {
@Override
public void onClick(View v) {
// cancel
-
- Message msg = Message.obtain();
- msg.arg1 = OpenPgpService.SelectPubKeysActivityCallback.CANCEL;
- ;
- try {
- mMessenger.send(msg);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoServiceActivity", e);
- }
-
- finishHandled = true;
- finish();
+ RemoteServiceActivity.this.setResult(RESULT_CANCELED);
+ RemoteServiceActivity.this.finish();
}
}
);
@@ -279,8 +214,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
} else if (ACTION_ERROR_MESSAGE.equals(action)) {
String errorMessage = intent.getStringExtra(EXTRA_ERROR_MESSAGE);
- String text = new String();
- text += "<font color=\"red\">" + errorMessage + "</font>";
+ String text = "<font color=\"red\">" + errorMessage + "</font>";
// Inflate a "Done" custom action bar view
ActionBarHelper.setDoneView(getSupportActionBar(), R.string.btn_okay,
@@ -288,7 +222,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
@Override
public void onClick(View v) {
- finish();
+ RemoteServiceActivity.this.setResult(RESULT_OK);
+ RemoteServiceActivity.this.finish();
}
});
@@ -298,7 +233,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_app_error_message_text);
textView.setHtmlFromString(text);
} else {
- Log.e(Constants.TAG, "Wrong action!");
+ Log.e(Constants.TAG, "Action does not exist!");
+ setResult(RESULT_CANCELED);
finish();
}
}
@@ -308,31 +244,21 @@ public class RemoteServiceActivity extends ActionBarActivity {
* encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks
* for a symmetric passphrase
*/
- private void showPassphraseDialog(long secretKeyId) {
+ private void showPassphraseDialog(final Bundle params, 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) {
- Message msg = Message.obtain();
- msg.arg1 = OpenPgpService.PassphraseActivityCallback.OKAY;
- try {
- mMessenger.send(msg);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoServiceActivity", e);
- }
+ // return given params again, for calling the service method again
+ Intent finishIntent = new Intent();
+ finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
+ RemoteServiceActivity.this.setResult(RESULT_OK, finishIntent);
} else {
- Message msg = Message.obtain();
- msg.arg1 = OpenPgpService.PassphraseActivityCallback.CANCEL;
- try {
- mMessenger.send(msg);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "CryptoServiceActivity", e);
- }
+ RemoteServiceActivity.this.setResult(RESULT_CANCELED);
}
- finishHandled = true;
- finish();
+ RemoteServiceActivity.this.finish();
}
};
@@ -345,9 +271,12 @@ public class RemoteServiceActivity extends ActionBarActivity {
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);
+ Log.d(Constants.TAG, "No passphrase for this secret key, do pgp operation directly!");
+ // return given params again, for calling the service method again
+ Intent finishIntent = new Intent();
+ finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
+ setResult(RESULT_OK, finishIntent);
+ finish();
}
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/exception/WrongPackageSignatureException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java
index cef002265..cc08548e8 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/exception/WrongPackageSignatureException.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java
@@ -1,4 +1,4 @@
-package org.sufficientlysecure.keychain.service.exception;
+package org.sufficientlysecure.keychain.service.remote;
public class WrongPackageSignatureException extends Exception {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SignKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
index d9fb294d5..aab6b81d9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SignKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
@@ -56,7 +56,7 @@ import com.beardedhen.androidbootstrap.BootstrapButton;
/**
* Signs the specified public key with the specified secret master key
*/
-public class SignKeyActivity extends ActionBarActivity implements
+public class CertifyKeyActivity extends ActionBarActivity implements
SelectSecretKeyLayoutFragment.SelectSecretKeyCallback {
private BootstrapButton mSignButton;
private CheckBox mUploadKeyCheckbox;
@@ -72,7 +72,7 @@ public class SignKeyActivity extends ActionBarActivity implements
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.sign_key_activity);
+ setContentView(R.layout.certify_key_activity);
final ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(true);
@@ -164,8 +164,8 @@ public class SignKeyActivity extends ActionBarActivity implements
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
+ Log.d(Constants.TAG, "No passphrase for this secret key!");
+ // send message to handler to start certification directly
returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
}
}
@@ -196,8 +196,8 @@ public class SignKeyActivity extends ActionBarActivity implements
String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
if (passphrase == null) {
showPassphraseDialog(mMasterKeyId);
- return; // bail out; need to wait until the user has entered the passphrase
- // before trying again
+ // bail out; need to wait until the user has entered the passphrase before trying again
+ return;
} else {
startSigning();
}
@@ -218,13 +218,13 @@ public class SignKeyActivity extends ActionBarActivity implements
// Send all information needed to service to sign key in other thread
Intent intent = new Intent(this, KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_SIGN_KEYRING);
+ intent.setAction(KeychainIntentService.ACTION_CERTIFY_KEYRING);
// fill values for this action
Bundle data = new Bundle();
- data.putLong(KeychainIntentService.SIGN_KEY_MASTER_KEY_ID, mMasterKeyId);
- data.putLong(KeychainIntentService.SIGN_KEY_PUB_KEY_ID, mPubKeyId);
+ data.putLong(KeychainIntentService.CERTIFY_KEY_MASTER_KEY_ID, mMasterKeyId);
+ data.putLong(KeychainIntentService.CERTIFY_KEY_PUB_KEY_ID, mPubKeyId);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@@ -237,14 +237,12 @@ public class SignKeyActivity extends ActionBarActivity implements
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- Toast.makeText(SignKeyActivity.this, R.string.key_sign_success,
+ Toast.makeText(CertifyKeyActivity.this, R.string.key_sign_success,
Toast.LENGTH_SHORT).show();
// check if we need to send the key to the server or not
if (mUploadKeyCheckbox.isChecked()) {
- /*
- * upload the newly signed key to the key server
- */
+ // upload the newly signed key to the keyserver
uploadKey();
} else {
setResult(RESULT_OK);
@@ -291,7 +289,7 @@ public class SignKeyActivity extends ActionBarActivity implements
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- Toast.makeText(SignKeyActivity.this, R.string.key_send_success,
+ Toast.makeText(CertifyKeyActivity.this, R.string.key_send_success,
Toast.LENGTH_SHORT).show();
setResult(RESULT_OK);
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 26657479f..652310cd2 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
@@ -34,7 +34,7 @@ import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.pgp.PgpOperation;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.exception.NoAsymmetricEncryptionException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
@@ -43,7 +43,6 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.LookupUnknownKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
@@ -61,7 +60,7 @@ import android.view.animation.AnimationUtils;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
-import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewFlipper;
@@ -78,14 +77,20 @@ public class DecryptActivity extends DrawerActivity {
/* 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 LinearLayout mSignatureLayout = null;
+ private RelativeLayout mSignatureLayout = null;
private ImageView mSignatureStatusImage = null;
private TextView mUserId = null;
private TextView mUserIdRest = null;
@@ -100,6 +105,7 @@ public class DecryptActivity extends DrawerActivity {
private EditText mFilename = null;
private CheckBox mDeleteAfter = null;
private BootstrapButton mBrowse = null;
+ private BootstrapButton mLookupKey = null;
private String mInputFilename = null;
private String mOutputFilename = null;
@@ -107,14 +113,10 @@ public class DecryptActivity extends DrawerActivity {
private Uri mContentUri = null;
private boolean mReturnBinary = false;
- private long mUnknownSignatureKeyId = 0;
-
private long mSecretKeyId = Id.key.none;
private FileDialogFragment mFileDialog;
- private boolean mLookupUnknownKey = true;
-
private boolean mDecryptImmediately = false;
private BootstrapButton mDecryptButton;
@@ -154,7 +156,7 @@ public class DecryptActivity extends DrawerActivity {
mSourceLabel.setOnClickListener(nextSourceClickListener);
mMessage = (EditText) findViewById(R.id.message);
- mSignatureLayout = (LinearLayout) findViewById(R.id.signature);
+ 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);
@@ -171,7 +173,15 @@ public class DecryptActivity extends DrawerActivity {
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FileHelper.openFile(DecryptActivity.this, mFilename.getText().toString(), "*/*",
- Id.request.filename);
+ RESULT_CODE_FILE);
+ }
+ });
+
+ mLookupKey = (BootstrapButton) findViewById(R.id.lookup_key);
+ mLookupKey.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ lookupUnknownKey(mSignatureKeyId);
}
});
@@ -239,7 +249,7 @@ public class DecryptActivity extends DrawerActivity {
DecryptActivity.this, mSignatureKeyId);
if (key != null) {
Intent intent = new Intent(DecryptActivity.this, ImportKeysActivity.class);
- intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEY_SERVER);
+ intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER);
intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, mSignatureKeyId);
startActivity(intent);
}
@@ -263,14 +273,14 @@ public class DecryptActivity extends DrawerActivity {
if (mDecryptImmediately
|| (mSource.getCurrentView().getId() == R.id.sourceMessage && (mMessage.getText()
- .length() > 0 || mContentUri != null))) {
+ .length() > 0 || mContentUri != null))) {
decryptClicked();
}
}
/**
* Handles all actions with this intent
- *
+ *
* @param intent
*/
private void handleActions(Intent intent) {
@@ -287,13 +297,13 @@ public class DecryptActivity extends DrawerActivity {
* Android's Action
*/
if (Intent.ACTION_SEND.equals(action) && type != null) {
- // When sending to Keychain Encrypt via share menu
+ // When sending to Keychain Decrypt via share menu
if ("text/plain".equals(type)) {
// Plain text
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
if (sharedText != null) {
// handle like normal text decryption, override action and extras to later
- // execute ACTION_DECRYPT in main actions
+ // executeServiceMethod ACTION_DECRYPT in main actions
extras.putString(EXTRA_TEXT, sharedText);
action = ACTION_DECRYPT;
}
@@ -375,21 +385,21 @@ public class DecryptActivity extends DrawerActivity {
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.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;
- }
+ case R.id.sourceMessage: {
+ mSourceLabel.setText(R.string.label_message);
+ mDecryptButton.setText(getString(R.string.btn_decrypt));
+ break;
+ }
- default: {
- break;
- }
+ default: {
+ break;
+ }
}
}
@@ -449,7 +459,7 @@ public class DecryptActivity extends DrawerActivity {
} else {
if (mDecryptTarget == Id.target.file) {
askForOutputFilename();
- } else {
+ } else { // mDecryptTarget == Id.target.message
decryptStart();
}
}
@@ -527,7 +537,7 @@ public class DecryptActivity extends DrawerActivity {
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.
+ // objects, if it even needs to be anything other than 0.
}
mSecretKeyId = PgpHelper.getDecryptionKeyId(this, inStream);
if (mSecretKeyId == Id.key.none) {
@@ -539,7 +549,7 @@ public class DecryptActivity extends DrawerActivity {
inStream.reset();
}
mSecretKeyId = Id.key.symmetric;
- if (!PgpOperation.hasSymmetricEncryption(this, inStream)) {
+ if (!PgpDecryptVerify.hasSymmetricEncryption(this, inStream)) {
throw new PgpGeneralException(
getString(R.string.error_no_known_encryption_found));
}
@@ -559,7 +569,7 @@ public class DecryptActivity extends DrawerActivity {
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 });
+ intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, new long[]{mSignatureKeyId});
startActivity(intent);
}
@@ -587,28 +597,10 @@ public class DecryptActivity extends DrawerActivity {
}
private void lookupUnknownKey(long unknownKeyId) {
- // Message is received after passphrase is cached
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == LookupUnknownKeyDialogFragment.MESSAGE_OKAY) {
- // the result is handled by onActivityResult() as LookupUnknownKeyDialogFragment
- // starts a new Intent which then returns data
- } else if (message.what == LookupUnknownKeyDialogFragment.MESSAGE_CANCEL) {
- // decrypt again, but don't lookup unknown keys!
- mLookupUnknownKey = false;
- decryptStart();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- LookupUnknownKeyDialogFragment lookupKeyDialog = LookupUnknownKeyDialogFragment
- .newInstance(messenger, unknownKeyId);
-
- lookupKeyDialog.show(getSupportFragmentManager(), "unknownKeyDialog");
+ 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() {
@@ -644,8 +636,6 @@ public class DecryptActivity extends DrawerActivity {
data.putLong(KeychainIntentService.ENCRYPT_SECRET_KEY_ID, mSecretKeyId);
- data.putBoolean(KeychainIntentService.DECRYPT_SIGNED_ONLY, mSignedOnly);
- data.putBoolean(KeychainIntentService.DECRYPT_LOOKUP_UNKNOWN_KEY, mLookupUnknownKey);
data.putBoolean(KeychainIntentService.DECRYPT_RETURN_BYTES, mReturnBinary);
data.putBoolean(KeychainIntentService.DECRYPT_ASSUME_SYMMETRIC, mAssumeSymmetricEncryption);
@@ -662,15 +652,6 @@ public class DecryptActivity extends DrawerActivity {
// get returned data bundle
Bundle returnData = message.getData();
- // if key is unknown show lookup dialog
- if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE_LOOKUP_KEY)
- && mLookupUnknownKey) {
- mUnknownSignatureKeyId = returnData
- .getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID);
- lookupUnknownKey(mUnknownSignatureKeyId);
- return;
- }
-
mSignatureKeyId = 0;
mSignatureLayout.setVisibility(View.GONE);
@@ -685,26 +666,26 @@ public class DecryptActivity extends DrawerActivity {
}
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;
+ 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;
}
@@ -727,19 +708,24 @@ public class DecryptActivity extends DrawerActivity {
if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS)) {
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
+ mLookupKey.setVisibility(View.GONE);
} else if (returnData
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN)) {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
+ mLookupKey.setVisibility(View.VISIBLE);
Toast.makeText(DecryptActivity.this,
- R.string.unknown_signature_key_touch_to_look_up,
+ R.string.unknown_signature,
Toast.LENGTH_LONG).show();
} else {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
+ mLookupKey.setVisibility(View.GONE);
}
mSignatureLayout.setVisibility(View.VISIBLE);
}
}
- };
+ }
+
+ ;
};
// Create a new Messenger for the communication back
@@ -756,36 +742,37 @@ public class DecryptActivity extends DrawerActivity {
@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!");
+ 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;
}
- return;
- }
- // this request is returned after LookupUnknownKeyDialogFragment started
- // ImportKeysActivity and user looked uo key
- case Id.request.look_up_key_id: {
- Log.d(Constants.TAG, "Returning from Lookup Key...");
- // decrypt again without lookup
- mLookupUnknownKey = false;
- decryptStart();
- return;
- }
+ // 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: {
- break;
- }
- }
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
- super.onActivityResult(requestCode, resultCode, data);
+ break;
+ }
+ }
}
}
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 81446db99..85bfba224 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
@@ -173,7 +173,7 @@ public class EncryptActivity extends DrawerActivity {
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
if (sharedText != null) {
// handle like normal text encryption, override action and extras to later
- // execute ACTION_ENCRYPT in main actions
+ // executeServiceMethod ACTION_ENCRYPT in main actions
extras.putString(EXTRA_TEXT, sharedText);
extras.putBoolean(EXTRA_ASCII_ARMOR, true);
action = ACTION_ENCRYPT;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java
index be7c50dbb..12d689274 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java
@@ -33,18 +33,7 @@ import android.view.ViewGroup;
import android.widget.TextView;
-public class HelpFragmentAbout extends Fragment {
-
- /**
- * Workaround for Android Bug. See
- * http://stackoverflow.com/questions/8748064/starting-activity-from
- * -fragment-causes-nullpointerexception
- */
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- setUserVisibleHint(true);
- }
+public class HelpAboutFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java
index 2fe7c56e6..9ccd7e088 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java
@@ -20,12 +20,13 @@ package org.sufficientlysecure.keychain.ui;
import java.util.ArrayList;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
@@ -37,8 +38,6 @@ public class HelpActivity extends ActionBarActivity {
ViewPager mViewPager;
TabsAdapter mTabsAdapter;
- TextView tabCenter;
- TextView tabText;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -63,93 +62,21 @@ public class HelpActivity extends ActionBarActivity {
}
Bundle startBundle = new Bundle();
- startBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_start);
+ startBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_start);
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_start)),
- HelpFragmentHtml.class, startBundle, (selectedTab == 0 ? true : false));
+ HelpHtmlFragment.class, startBundle, (selectedTab == 0 ? true : false));
Bundle nfcBundle = new Bundle();
- nfcBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_nfc_beam);
+ nfcBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_nfc_beam);
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_nfc_beam)),
- HelpFragmentHtml.class, nfcBundle, (selectedTab == 1 ? true : false));
+ HelpHtmlFragment.class, nfcBundle, (selectedTab == 1 ? true : false));
Bundle changelogBundle = new Bundle();
- changelogBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_changelog);
+ changelogBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_changelog);
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_changelog)),
- HelpFragmentHtml.class, changelogBundle, (selectedTab == 2 ? true : false));
+ HelpHtmlFragment.class, changelogBundle, (selectedTab == 2 ? true : false));
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_about)),
- HelpFragmentAbout.class, null, (selectedTab == 3 ? true : false));
- }
-
- public static class TabsAdapter extends FragmentPagerAdapter implements ActionBar.TabListener,
- ViewPager.OnPageChangeListener {
- private final Context mContext;
- private final ActionBar mActionBar;
- private final ViewPager mViewPager;
- private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
-
- static final class TabInfo {
- private final Class<?> clss;
- private final Bundle args;
-
- TabInfo(Class<?> _class, Bundle _args) {
- clss = _class;
- args = _args;
- }
- }
-
- public TabsAdapter(ActionBarActivity activity, ViewPager pager) {
- super(activity.getSupportFragmentManager());
- mContext = activity;
- mActionBar = activity.getSupportActionBar();
- mViewPager = pager;
- mViewPager.setAdapter(this);
- mViewPager.setOnPageChangeListener(this);
- }
-
- public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args, boolean selected) {
- TabInfo info = new TabInfo(clss, args);
- tab.setTag(info);
- tab.setTabListener(this);
- mTabs.add(info);
- mActionBar.addTab(tab, selected);
- notifyDataSetChanged();
- }
-
- @Override
- public int getCount() {
- return mTabs.size();
- }
-
- @Override
- public Fragment getItem(int position) {
- TabInfo info = mTabs.get(position);
- return Fragment.instantiate(mContext, info.clss.getName(), info.args);
- }
-
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- }
-
- public void onPageSelected(int position) {
- mActionBar.setSelectedNavigationItem(position);
- }
-
- public void onPageScrollStateChanged(int state) {
- }
-
- public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
- Object tag = tab.getTag();
- for (int i = 0; i < mTabs.size(); i++) {
- if (mTabs.get(i) == tag) {
- mViewPager.setCurrentItem(i);
- }
- }
- }
-
- public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
- }
-
- public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
- }
+ HelpAboutFragment.class, null, (selectedTab == 3 ? true : false));
}
} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpFragmentHtml.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java
index fb6747db0..3afb5bbe0 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpFragmentHtml.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java
@@ -28,7 +28,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ScrollView;
-public class HelpFragmentHtml extends Fragment {
+public class HelpHtmlFragment extends Fragment {
private Activity mActivity;
private int htmlFile;
@@ -36,10 +36,10 @@ public class HelpFragmentHtml extends Fragment {
public static final String ARG_HTML_FILE = "htmlFile";
/**
- * Create a new instance of HelpFragmentHtml, providing "htmlFile" as an argument.
+ * Create a new instance of HelpHtmlFragment, providing "htmlFile" as an argument.
*/
- static HelpFragmentHtml newInstance(int htmlFile) {
- HelpFragmentHtml f = new HelpFragmentHtml();
+ static HelpHtmlFragment newInstance(int htmlFile) {
+ HelpHtmlFragment f = new HelpHtmlFragment();
// Supply html raw file input as an argument.
Bundle args = new Bundle();
@@ -49,17 +49,6 @@ public class HelpFragmentHtml extends Fragment {
return f;
}
- /**
- * Workaround for Android Bug. See
- * http://stackoverflow.com/questions/8748064/starting-activity-from
- * -fragment-causes-nullpointerexception
- */
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- setUserVisibleHint(true);
- }
-
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mActivity = getActivity();
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 dc3a07def..a5027ac1c 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,5 +1,5 @@
/*
- * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2011 Senecaso
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -47,16 +47,19 @@ import android.support.v7.app.ActionBar;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
-import android.widget.Toast;
import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.devspark.appmsg.AppMsg;
public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNavigationListener {
public static final String ACTION_IMPORT_KEY = Constants.INTENT_PREFIX + "IMPORT_KEY";
public static final String ACTION_IMPORT_KEY_FROM_QR_CODE = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_QR_CODE";
- public static final String ACTION_IMPORT_KEY_FROM_KEY_SERVER = Constants.INTENT_PREFIX
- + "IMPORT_KEY_FROM_KEY_SERVER";
+ 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";
// Actions for internal use only:
public static final String ACTION_IMPORT_KEY_FROM_FILE = Constants.INTENT_PREFIX
@@ -67,7 +70,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
// only used by ACTION_IMPORT_KEY
public static final String EXTRA_KEY_BYTES = "key_bytes";
- // only used by ACTION_IMPORT_KEY_FROM_KEY_SERVER
+ // only used by ACTION_IMPORT_KEY_FROM_KEYSERVER
public static final String EXTRA_QUERY = "query";
public static final String EXTRA_KEY_ID = "key_id";
public static final String EXTRA_FINGERPRINT = "fingerprint";
@@ -78,6 +81,16 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
private Fragment mCurrentFragment;
private BootstrapButton mImportButton;
+ private static final Class[] NAVIGATION_CLASSES = new Class[]{
+ ImportKeysServerFragment.class,
+ ImportKeysFileFragment.class,
+ ImportKeysQrCodeFragment.class,
+ ImportKeysClipboardFragment.class,
+ ImportKeysNFCFragment.class
+ };
+
+ private int mCurrentNavPostition = -1;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -107,6 +120,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
handleActions(savedInstanceState, getIntent());
}
+
protected void handleActions(Bundle savedInstanceState, Intent intent) {
String action = intent.getAction();
Bundle extras = intent.getExtras();
@@ -125,24 +139,23 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
if (scheme != null && scheme.toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) {
/* Scanning a fingerprint directly with Barcode Scanner */
- getSupportActionBar().setSelectedNavigationItem(0);
- loadFragment(ImportKeysQrCodeFragment.class, null, mNavigationStrings[0]);
- loadFromFingerprintUri(dataUri);
+ loadFromFingerprintUri(savedInstanceState, dataUri);
} else if (ACTION_IMPORT_KEY.equals(action)) {
/* Keychain's own Actions */
- getSupportActionBar().setSelectedNavigationItem(1);
- loadFragment(ImportKeysFileFragment.class, null, mNavigationStrings[1]);
+
+ // display file fragment
+ loadNavFragment(1, null);
if (dataUri != null) {
- // directly load data
+ // action: directly load data
startListFragment(savedInstanceState, null, dataUri, null);
} else if (extras.containsKey(EXTRA_KEY_BYTES)) {
byte[] importData = intent.getByteArrayExtra(EXTRA_KEY_BYTES);
- // directly load data
+ // action: directly load data
startListFragment(savedInstanceState, importData, null, null);
}
- } else if (ACTION_IMPORT_KEY_FROM_KEY_SERVER.equals(action)) {
+ } else if (ACTION_IMPORT_KEY_FROM_KEYSERVER.equals(action)) {
String query = null;
if (extras.containsKey(EXTRA_QUERY)) {
query = extras.getString(EXTRA_QUERY);
@@ -161,82 +174,97 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
return;
}
- // search directly
- getSupportActionBar().setSelectedNavigationItem(0);
+ // display keyserver fragment with query
Bundle args = new Bundle();
args.putString(ImportKeysServerFragment.ARG_QUERY, query);
- loadFragment(ImportKeysServerFragment.class, args, mNavigationStrings[0]);
+ loadNavFragment(0, args);
+ // action: search immediately
startListFragment(savedInstanceState, null, null, query);
- } else {
- // Other actions
+ } else if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) {
+
+ // NOTE: this only displays the appropriate fragment, no actions are taken
+ loadNavFragment(1, null);
+
+ // no immediate actions!
startListFragment(savedInstanceState, null, null, null);
+ } else if (ACTION_IMPORT_KEY_FROM_QR_CODE.equals(action)) {
+ // also exposed in AndroidManifest
- if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) {
- getSupportActionBar().setSelectedNavigationItem(1);
- loadFragment(ImportKeysFileFragment.class, null, mNavigationStrings[1]);
- } else if (ACTION_IMPORT_KEY_FROM_QR_CODE.equals(action)) {
- // also exposed in AndroidManifest
- getSupportActionBar().setSelectedNavigationItem(2);
- loadFragment(ImportKeysQrCodeFragment.class, null, mNavigationStrings[2]);
- } else if (ACTION_IMPORT_KEY_FROM_NFC.equals(action)) {
- getSupportActionBar().setSelectedNavigationItem(3);
- loadFragment(ImportKeysNFCFragment.class, null, mNavigationStrings[3]);
- }
+ // NOTE: this only displays the appropriate fragment, no actions are taken
+ loadNavFragment(2, null);
+
+ // no immediate actions!
+ startListFragment(savedInstanceState, null, null, null);
+ } else if (ACTION_IMPORT_KEY_FROM_NFC.equals(action)) {
+
+ // NOTE: this only displays the appropriate fragment, no actions are taken
+ loadNavFragment(3, null);
+
+ // no immediate actions!
+ startListFragment(savedInstanceState, null, null, null);
+ } else {
+ startListFragment(savedInstanceState, null, null, null);
}
}
private void startListFragment(Bundle savedInstanceState, byte[] bytes, Uri dataUri, String serverQuery) {
- // Check that the activity is using the layout version with
- // the fragment_container FrameLayout
- if (findViewById(R.id.import_keys_list_container) != null) {
-
- // 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;
- }
+ // 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
- mListFragment = ImportKeysListFragment.newInstance(bytes, dataUri, serverQuery);
+ // Create an instance of the fragment
+ mListFragment = ImportKeysListFragment.newInstance(bytes, dataUri, serverQuery);
- // Add the fragment to the 'fragment_container' FrameLayout
- // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
- getSupportFragmentManager().beginTransaction()
- .replace(R.id.import_keys_list_container, mListFragment)
- .commitAllowingStateLoss();
- // do it immediately!
- getSupportFragmentManager().executePendingTransactions();
- }
+ // Add the fragment to the 'fragment_container' FrameLayout
+ // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.import_keys_list_container, mListFragment)
+ .commitAllowingStateLoss();
+ // do it immediately!
+ getSupportFragmentManager().executePendingTransactions();
}
+ /**
+ * "Basically, when using a list navigation, onNavigationItemSelected() is automatically
+ * called when your activity is created/re-created, whether you like it or not. To prevent
+ * your Fragment's onCreateView() from being called twice, this initial automatic call to
+ * 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
+ *
+ * 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.
+ *
+ * Our solution:
+ * To prevent that a fragment will be loaded again even if it was already loaded loadNavFragment
+ * checks against mCurrentNavPostition.
+ *
+ * @param itemPosition
+ * @param itemId
+ * @return
+ */
@Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
- // Create new fragment from our own Fragment class
- switch (itemPosition) {
- case 0:
- loadFragment(ImportKeysServerFragment.class, null, mNavigationStrings[itemPosition]);
- break;
- case 1:
- loadFragment(ImportKeysFileFragment.class, null, mNavigationStrings[itemPosition]);
- break;
- case 2:
- loadFragment(ImportKeysQrCodeFragment.class, null, mNavigationStrings[itemPosition]);
- break;
- case 3:
- loadFragment(ImportKeysClipboardFragment.class, null, mNavigationStrings[itemPosition]);
- break;
- case 4:
- loadFragment(ImportKeysNFCFragment.class, null, mNavigationStrings[itemPosition]);
- break;
-
- default:
- break;
- }
+ Log.d(Constants.TAG, "onNavigationItemSelected");
+
+ loadNavFragment(itemPosition, null);
+
return true;
}
+ private void loadNavFragment(int itemPosition, Bundle args) {
+ if (mCurrentNavPostition != itemPosition) {
+ getSupportActionBar().setSelectedNavigationItem(itemPosition);
+ loadFragment(NAVIGATION_CLASSES[itemPosition], args, mNavigationStrings[itemPosition]);
+ mCurrentNavPostition = itemPosition;
+ }
+ }
+
private void loadFragment(Class<?> clss, Bundle args, String tag) {
mCurrentFragment = Fragment.instantiate(this, clss.getName(), args);
@@ -248,26 +276,26 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
ft.commit();
}
- public void loadFromFingerprintUri(Uri dataUri) {
+ public void loadFromFingerprintUri(Bundle savedInstanceState, Uri dataUri) {
String fingerprint = dataUri.toString().split(":")[1].toLowerCase(Locale.ENGLISH);
Log.d(Constants.TAG, "fingerprint: " + fingerprint);
if (fingerprint.length() < 16) {
- Toast.makeText(this, R.string.import_qr_code_too_short_fingerprint,
- Toast.LENGTH_LONG).show();
+ AppMsg.makeText(this, R.string.import_qr_code_too_short_fingerprint,
+ AppMsg.STYLE_ALERT).show();
return;
}
String query = "0x" + fingerprint;
- // search directly
- getSupportActionBar().setSelectedNavigationItem(0);
+ // display keyserver fragment with query
Bundle args = new Bundle();
args.putString(ImportKeysServerFragment.ARG_QUERY, query);
- loadFragment(ImportKeysServerFragment.class, args, mNavigationStrings[0]);
+ loadNavFragment(0, args);
- startListFragment(null, null, null, query);
+ // action: search directly
+ startListFragment(savedInstanceState, null, null, query);
}
public void loadCallback(byte[] importData, Uri dataUri, String serverQuery, String keyServer) {
@@ -364,7 +392,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
} else {
toastMessage = getString(R.string.no_keys_added_or_updated);
}
- Toast.makeText(ImportKeysActivity.this, toastMessage, Toast.LENGTH_SHORT)
+ AppMsg.makeText(ImportKeysActivity.this, toastMessage, AppMsg.STYLE_INFO)
.show();
if (bad > 0) {
AlertDialog.Builder alert = new AlertDialog.Builder(
@@ -446,7 +474,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
// start service with intent
startService(intent);
} else {
- Toast.makeText(this, R.string.error_nothing_import, Toast.LENGTH_LONG).show();
+ AppMsg.makeText(this, R.string.error_nothing_import, AppMsg.STYLE_ALERT).show();
}
}
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 68d318491..b52de5bc9 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
@@ -122,7 +122,7 @@ public class ImportKeysListFragment extends ListFragment implements
mKeyBytes = getArguments().getByteArray(ARG_BYTES);
mServerQuery = getArguments().getString(ARG_SERVER_QUERY);
- // TODO: this is used when scanning QR Code. Currently it simply uses key server nr 0
+ // TODO: this is used when scanning QR Code. Currently it simply uses keyserver nr 0
mKeyServer = Preferences.getPreferences(getActivity())
.getKeyServers()[0];
@@ -136,7 +136,7 @@ public class ImportKeysListFragment extends ListFragment implements
getLoaderManager().initLoader(LOADER_ID_BYTES, null, this);
}
- if (mServerQuery != null) {
+ if (mServerQuery != null && mKeyServer != null) {
// Start out with a progress indicator.
setListShown(false);
@@ -165,14 +165,19 @@ public class ImportKeysListFragment extends ListFragment implements
mServerQuery = serverQuery;
mKeyServer = keyServer;
- // Start out with a progress indicator.
- setListShown(false);
+ if (mKeyBytes != null || mDataUri != null) {
+ // Start out with a progress indicator.
+ setListShown(false);
- if (mKeyBytes != null || mDataUri != null)
getLoaderManager().restartLoader(LOADER_ID_BYTES, null, this);
+ }
+
+ if (mServerQuery != null && mKeyServer != null) {
+ // Start out with a progress indicator.
+ setListShown(false);
- if (mServerQuery != null && mKeyServer != null)
getLoaderManager().restartLoader(LOADER_ID_SERVER_QUERY, null, this);
+ }
}
@Override
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 63727ad26..ee91b2434 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
@@ -98,22 +98,29 @@ public class ImportKeysQrCodeFragment extends Fragment {
IntentResult scanResult = IntentIntegratorSupportV4.parseActivityResult(requestCode,
resultCode, data);
if (scanResult != null && scanResult.getFormatName() != null) {
+ String scannedContent = scanResult.getContents();
- Log.d(Constants.TAG, "scanResult content: " + scanResult.getContents());
+ Log.d(Constants.TAG, "scannedContent: " + scannedContent);
// look if it's fingerprint only
- if (scanResult.getContents().toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) {
+ if (scannedContent.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) {
importFingerprint(Uri.parse(scanResult.getContents()));
return;
}
// look if it is the whole key
- String[] parts = scanResult.getContents().split(",");
+ String[] parts = scannedContent.split(",");
if (parts.length == 3) {
importParts(parts);
return;
}
+ // is this a full key encoded as qr code?
+ if (scannedContent.startsWith("-----BEGIN PGP")) {
+ mImportActivity.loadCallback(scannedContent.getBytes(), null, null, null);
+ return;
+ }
+
// fail...
Toast.makeText(getActivity(), R.string.import_qr_code_wrong, Toast.LENGTH_LONG)
.show();
@@ -130,7 +137,7 @@ public class ImportKeysQrCodeFragment extends Fragment {
}
public void importFingerprint(Uri dataUri) {
- mImportActivity.loadFromFingerprintUri(dataUri);
+ mImportActivity.loadFromFingerprintUri(null, dataUri);
}
private void importParts(String[] parts) {
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 2e0956d8b..d77015aa7 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
@@ -127,21 +127,21 @@ public class ImportKeysServerFragment extends Fragment {
mImportActivity = (ImportKeysActivity) getActivity();
// set displayed values
- if (getArguments() != null && getArguments().containsKey(ARG_QUERY)) {
- String query = getArguments().getString(ARG_QUERY);
- mQueryEditText.setText(query, TextView.BufferType.EDITABLE);
+ if (getArguments() != null) {
+ if (getArguments().containsKey(ARG_QUERY)) {
+ String query = getArguments().getString(ARG_QUERY);
+ mQueryEditText.setText(query, TextView.BufferType.EDITABLE);
+
+ Log.d(Constants.TAG, "query: " + query);
+ }
- String keyServer = null;
if (getArguments().containsKey(ARG_KEY_SERVER)) {
- keyServer = getArguments().getString(ARG_KEY_SERVER);
+ String keyServer = getArguments().getString(ARG_KEY_SERVER);
int keyServerPos = mServerAdapter.getPosition(keyServer);
mServerSpinner.setSelection(keyServerPos);
- } else {
- keyServer = (String) mServerSpinner.getSelectedItem();
- }
- Log.d(Constants.TAG, "query: " + query);
- Log.d(Constants.TAG, "keyServer: " + keyServer);
+ Log.d(Constants.TAG, "keyServer: " + keyServer);
+ }
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
index 6ae2b9bf9..c28d57627 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.ui;
+import java.util.ArrayList;
import java.util.Set;
import org.sufficientlysecure.keychain.Id;
@@ -30,12 +31,16 @@ import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import se.emilsjolander.stickylistheaders.ApiLevelTooLowException;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
+
import android.annotation.SuppressLint;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.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;
@@ -50,6 +55,7 @@ import android.view.ViewGroup;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView;
import android.widget.ListView;
+import android.widget.Toast;
import com.beardedhen.androidbootstrap.BootstrapButton;
@@ -63,7 +69,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
private KeyListPublicAdapter mAdapter;
private StickyListHeadersListView mStickyList;
- // empty layout
+ // empty list layout
private BootstrapButton mButtonEmptyCreate;
private BootstrapButton mButtonEmptyImport;
@@ -92,9 +98,9 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
@Override
public void onClick(View v) {
- Intent intentImportFromFile = new Intent(getActivity(), ImportKeysActivity.class);
- intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE);
- startActivityForResult(intentImportFromFile, 0);
+ Intent intent = new Intent(getActivity(), ImportKeysActivity.class);
+ intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE);
+ startActivityForResult(intent, 0);
}
});
@@ -109,7 +115,6 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- // mKeyListPublicActivity = (KeyListPublicActivity) getActivity();
mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list);
mStickyList.setOnItemClickListener(this);
@@ -159,18 +164,16 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
}
switch (item.getItemId()) {
- case R.id.menu_key_list_public_multi_encrypt: {
- encrypt(ids);
-
- break;
- }
- case R.id.menu_key_list_public_multi_delete: {
- showDeleteKeyDialog(ids);
-
- break;
- }
+ case R.id.menu_key_list_public_multi_encrypt: {
+ encrypt(mode, ids);
+ break;
+ }
+ case R.id.menu_key_list_public_multi_delete: {
+ showDeleteKeyDialog(mode, ids);
+ break;
+ }
}
- return false;
+ return true;
}
@Override
@@ -181,7 +184,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
@Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
- boolean checked) {
+ boolean checked) {
if (checked) {
count++;
mAdapter.setNewSelection(position, checked);
@@ -212,8 +215,12 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
}
// These are the rows that we will retrieve.
- static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID,
- UserIds.USER_ID };
+ static final String[] PROJECTION = new String[]{
+ KeychainContract.KeyRings._ID,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.UserIds.USER_ID,
+ KeychainContract.Keys.IS_REVOKED
+ };
static final int USER_ID_INDEX = 2;
@@ -270,7 +277,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
startActivity(viewIntent);
}
- public void encrypt(long[] keyRingRowIds) {
+ public void encrypt(ActionMode mode, long[] keyRingRowIds) {
// get master key ids from row ids
long[] keyRingIds = new long[keyRingRowIds.length];
for (int i = 0; i < keyRingRowIds.length; i++) {
@@ -282,15 +289,44 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte
intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, keyRingIds);
// used instead of startActivity set actionbar based on callingPackage
startActivityForResult(intent, 0);
+
+ mode.finish();
}
/**
* Show dialog to delete key
- *
+ *
* @param keyRingRowIds
*/
- public void showDeleteKeyDialog(long[] keyRingRowIds) {
- DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(null,
+ public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) {
+ // 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();
+ }
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(returnHandler);
+
+ DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
keyRingRowIds, Id.type.public_key);
deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
index f2e6131f6..f9d267f27 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
@@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.ui;
+import java.util.ArrayList;
import java.util.Set;
import org.sufficientlysecure.keychain.Id;
@@ -33,6 +34,9 @@ import android.database.Cursor;
import android.net.Uri;
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.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
@@ -45,6 +49,7 @@ import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView.OnItemClickListener;
+import android.widget.Toast;
public class KeyListSecretFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener {
@@ -103,13 +108,12 @@ public class KeyListSecretFragment extends ListFragment implements
}
switch (item.getItemId()) {
- case R.id.menu_key_list_public_multi_delete: {
- showDeleteKeyDialog(ids);
-
- break;
- }
+ case R.id.menu_key_list_public_multi_delete: {
+ showDeleteKeyDialog(mode, ids);
+ break;
+ }
}
- return false;
+ return true;
}
@Override
@@ -120,7 +124,7 @@ public class KeyListSecretFragment extends ListFragment implements
@Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
- boolean checked) {
+ boolean checked) {
if (checked) {
count++;
mAdapter.setNewSelection(position, checked);
@@ -153,8 +157,8 @@ public class KeyListSecretFragment extends ListFragment implements
}
// These are the rows that we will retrieve.
- static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID,
- UserIds.USER_ID };
+ static final String[] PROJECTION = new String[]{KeyRings._ID, KeyRings.MASTER_KEY_ID,
+ UserIds.USER_ID};
static final String SORT_ORDER = UserIds.USER_ID + " COLLATE LOCALIZED ASC";
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
@@ -202,13 +206,27 @@ public class KeyListSecretFragment extends ListFragment implements
/**
* Show dialog to delete key
- *
+ *
* @param keyRingRowIds
*/
- public void showDeleteKeyDialog(long[] keyRingRowIds) {
- DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(null,
+ public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) {
+ // Message is received after key is deleted
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
+ mode.finish();
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(returnHandler);
+
+ DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
keyRingRowIds, Id.type.secret_key);
deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
}
+
}
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 e3881503b..550d3047d 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
@@ -40,7 +40,7 @@ import android.widget.Toast;
import com.beardedhen.androidbootstrap.BootstrapButton;
/**
- * Sends the selected public key to a key server
+ * Sends the selected public key to a keyserver
*/
public class UploadKeyActivity extends ActionBarActivity {
private BootstrapButton mUploadButton;
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 99d93fbbd..0a452bc8a 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,8 +18,17 @@
package org.sufficientlysecure.keychain.ui;
-import java.util.ArrayList;
-import java.util.Date;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
@@ -27,63 +36,27 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.ui.adapter.ViewKeyKeysAdapter;
-import org.sufficientlysecure.keychain.ui.adapter.ViewKeyUserIdsAdapter;
+import org.sufficientlysecure.keychain.ui.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 android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.support.v4.app.LoaderManager;
-import android.support.v4.content.CursorLoader;
-import android.support.v4.content.Loader;
-import android.support.v7.app.ActionBarActivity;
-import android.text.format.DateFormat;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.beardedhen.androidbootstrap.BootstrapButton;
+import java.util.ArrayList;
-public class ViewKeyActivity extends ActionBarActivity implements
- LoaderManager.LoaderCallbacks<Cursor> {
+public class ViewKeyActivity extends ActionBarActivity {
ExportHelper mExportHelper;
protected Uri mDataUri;
- private TextView mName;
- private TextView mEmail;
- private TextView mComment;
- private TextView mAlgorithm;
- private TextView mKeyId;
- private TextView mExpiry;
- private TextView mCreation;
- private TextView mFingerprint;
- private BootstrapButton mActionEncrypt;
-
- private ListView mUserIds;
- private ListView mKeys;
-
- private static final int LOADER_ID_KEYRING = 0;
- private static final int LOADER_ID_USER_IDS = 1;
- private static final int LOADER_ID_KEYS = 2;
- private ViewKeyUserIdsAdapter mUserIdsAdapter;
- private ViewKeyKeysAdapter mKeysAdapter;
+ public static final String EXTRA_SELECTED_TAB = "selectedTab";
+
+ ViewPager mViewPager;
+ TabsAdapter mTabsAdapter;
+
+ private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -91,25 +64,36 @@ public class ViewKeyActivity extends ActionBarActivity implements
mExportHelper = new ExportHelper(this);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setIcon(android.R.color.transparent);
- getSupportActionBar().setHomeButtonEnabled(true);
+ // let the actionbar look like Android's contact app
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setIcon(android.R.color.transparent);
+ actionBar.setHomeButtonEnabled(true);
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
setContentView(R.layout.view_key_activity);
- mName = (TextView) findViewById(R.id.name);
- mEmail = (TextView) findViewById(R.id.email);
- mComment = (TextView) findViewById(R.id.comment);
- mKeyId = (TextView) findViewById(R.id.key_id);
- mAlgorithm = (TextView) findViewById(R.id.algorithm);
- mCreation = (TextView) findViewById(R.id.creation);
- mExpiry = (TextView) findViewById(R.id.expiry);
- mFingerprint = (TextView) findViewById(R.id.fingerprint);
- mActionEncrypt = (BootstrapButton) findViewById(R.id.action_encrypt);
- mUserIds = (ListView) findViewById(R.id.user_ids);
- mKeys = (ListView) findViewById(R.id.keys);
-
- loadData(getIntent());
+ mViewPager = (ViewPager) findViewById(R.id.pager);
+
+ mTabsAdapter = new TabsAdapter(this, mViewPager);
+
+ int selectedTab = 0;
+ Intent intent = getIntent();
+ if (intent.getExtras() != null && intent.getExtras().containsKey(EXTRA_SELECTED_TAB)) {
+ selectedTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
+ }
+
+ mDataUri = getIntent().getData();
+
+ Bundle mainBundle = new Bundle();
+ mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, mDataUri);
+ mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_main)),
+ ViewKeyMainFragment.class, mainBundle, (selectedTab == 0 ? true : false));
+
+ Bundle certBundle = new Bundle();
+ certBundle.putParcelable(ViewKeyCertsFragment.ARG_DATA_URI, mDataUri);
+ mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_certs)),
+ ViewKeyCertsFragment.class, certBundle, (selectedTab == 1 ? true : false));
}
@Override
@@ -130,9 +114,6 @@ public class ViewKeyActivity extends ActionBarActivity implements
case R.id.menu_key_view_update:
updateFromKeyserver(mDataUri);
return true;
- case R.id.menu_key_view_sign:
- signKey(mDataUri);
- return true;
case R.id.menu_key_view_export_keyserver:
uploadToKeyserver(mDataUri);
return true;
@@ -159,230 +140,13 @@ public class ViewKeyActivity extends ActionBarActivity implements
copyToClipboard(mDataUri);
return true;
case R.id.menu_key_view_delete: {
- // Message is received after key is deleted
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
- setResult(RESULT_CANCELED);
- finish();
- }
- }
- };
-
- mExportHelper.deleteKey(mDataUri, Id.type.public_key, returnHandler);
+ deleteKey(mDataUri);
return true;
}
}
return super.onOptionsItemSelected(item);
}
- private void loadData(Intent intent) {
- if (intent.getData().equals(mDataUri)) {
- Log.d(Constants.TAG, "Same URI, no need to load the data again!");
- return;
- }
-
- mDataUri = intent.getData();
- if (mDataUri == null) {
- Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
- finish();
- return;
- }
-
- Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
-
- mActionEncrypt.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- long keyId = ProviderHelper.getMasterKeyId(ViewKeyActivity.this, mDataUri);
-
- long[] encryptionKeyIds = new long[]{keyId};
- Intent intent = new Intent(ViewKeyActivity.this, EncryptActivity.class);
- intent.setAction(EncryptActivity.ACTION_ENCRYPT);
- intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
- // used instead of startActivity set actionbar based on callingPackage
- startActivityForResult(intent, 0);
- }
- });
-
- mUserIdsAdapter = new ViewKeyUserIdsAdapter(this, null, 0);
-
- mUserIds.setAdapter(mUserIdsAdapter);
- // mUserIds.setEmptyView(findViewById(android.R.id.empty));
- // mUserIds.setClickable(true);
- // mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- // @Override
- // public void onItemClick(AdapterView<?> arg0, View arg1, int position, long id) {
- // }
- // });
-
- mKeysAdapter = new ViewKeyKeysAdapter(this, null, 0);
-
- mKeys.setAdapter(mKeysAdapter);
-
- // Prepare the loader. Either re-connect with an existing one,
- // or start a new one.
- getSupportLoaderManager().initLoader(LOADER_ID_KEYRING, null, this);
- getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
- getSupportLoaderManager().initLoader(LOADER_ID_KEYS, null, this);
- }
-
- static final String[] KEYRING_PROJECTION = new String[]{KeyRings._ID, KeyRings.MASTER_KEY_ID,
- UserIds.USER_ID};
- static final 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[]{UserIds._ID, UserIds.USER_ID,
- UserIds.RANK,};
- // not the main user id
- static final String USER_IDS_SELECTION = UserIds.RANK + " > 0 ";
- static final String USER_IDS_SORT_ORDER = UserIds.USER_ID + " COLLATE LOCALIZED ASC";
-
- static final String[] KEYS_PROJECTION = new String[]{Keys._ID, Keys.KEY_ID,
- Keys.IS_MASTER_KEY, Keys.ALGORITHM, Keys.KEY_SIZE, Keys.CAN_CERTIFY, Keys.CAN_SIGN,
- Keys.CAN_ENCRYPT, Keys.CREATION, Keys.EXPIRY, Keys.FINGERPRINT};
- static final String KEYS_SORT_ORDER = 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;
-
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- switch (id) {
- case LOADER_ID_KEYRING: {
- Uri baseUri = mDataUri;
-
- // Now create and return a CursorLoader that will take care of
- // creating a Cursor for the data being displayed.
- return new CursorLoader(this, baseUri, KEYRING_PROJECTION, null, null, null);
- }
- case LOADER_ID_USER_IDS: {
- Uri baseUri = UserIds.buildUserIdsUri(mDataUri);
-
- // Now create and return a CursorLoader that will take care of
- // creating a Cursor for the data being displayed.
- return new CursorLoader(this, baseUri, USER_IDS_PROJECTION, USER_IDS_SELECTION, null,
- USER_IDS_SORT_ORDER);
- }
- case LOADER_ID_KEYS: {
- Uri baseUri = Keys.buildKeysUri(mDataUri);
-
- // Now create and return a CursorLoader that will take care of
- // creating a Cursor for the data being displayed.
- return new CursorLoader(this, baseUri, KEYS_PROJECTION, null, null, KEYS_SORT_ORDER);
- }
-
- default:
- return null;
- }
- }
-
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- // Swap the new cursor in. (The framework will take care of closing the
- // old cursor once we return.)
- switch (loader.getId()) {
- case LOADER_ID_KEYRING:
- if (data.moveToFirst()) {
- // get name, email, and comment from USER_ID
- String[] mainUserId = PgpKeyHelper.splitUserId(data
- .getString(KEYRING_INDEX_USER_ID));
- if (mainUserId[0] != null) {
- setTitle(mainUserId[0]);
- mName.setText(mainUserId[0]);
- } else {
- setTitle(R.string.user_id_no_name);
- mName.setText(R.string.user_id_no_name);
- }
- 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);
-
- String keyIdStr = "0x" + PgpKeyHelper.convertKeyIdToHex(keyId);
- mKeyId.setText(keyIdStr);
-
- // get creation date from CREATION
- if (data.isNull(KEYS_INDEX_CREATION)) {
- mCreation.setText(R.string.none);
- } else {
- Date creationDate = new Date(data.getLong(KEYS_INDEX_CREATION) * 1000);
-
- mCreation.setText(DateFormat.getDateFormat(getApplicationContext()).format(
- creationDate));
- }
-
- // get expiry date from EXPIRY
- if (data.isNull(KEYS_INDEX_EXPIRY)) {
- mExpiry.setText(R.string.none);
- } else {
- Date expiryDate = new Date(data.getLong(KEYS_INDEX_EXPIRY) * 1000);
-
- mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(
- expiryDate));
- }
-
- String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
- data.getInt(KEYS_INDEX_ALGORITHM), data.getInt(KEYS_INDEX_KEY_SIZE));
- mAlgorithm.setText(algorithmStr);
-
- byte[] fingerprintBlob = data.getBlob(KEYS_INDEX_FINGERPRINT);
- if (fingerprintBlob == null) {
- // FALLBACK for old database entries
- fingerprintBlob = ProviderHelper.getFingerprint(this, mDataUri);
- }
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true);
- fingerprint = fingerprint.replace(" ", "\n");
-
- mFingerprint.setText(fingerprint);
- }
-
- mKeysAdapter.swapCursor(data);
- break;
-
- default:
- break;
- }
- }
-
- /**
- * This is called when the last Cursor provided to onLoadFinished() above is about to be closed.
- * We need to make sure we are no longer using it.
- */
- public void onLoaderReset(Loader<Cursor> loader) {
- switch (loader.getId()) {
- case LOADER_ID_KEYRING:
- // 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;
- }
- }
-
private void uploadToKeyserver(Uri dataUri) {
Intent uploadIntent = new Intent(this, UploadKeyActivity.class);
uploadIntent.setData(dataUri);
@@ -398,21 +162,15 @@ public class ViewKeyActivity extends ActionBarActivity implements
}
Intent queryIntent = new Intent(this, ImportKeysActivity.class);
- queryIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEY_SERVER);
+ queryIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER);
queryIntent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, updateKeyId);
- // TODO: lookup??
- startActivityForResult(queryIntent, Id.request.look_up_key_id);
- }
-
- private void signKey(Uri dataUri) {
- Intent signIntent = new Intent(this, SignKeyActivity.class);
- signIntent.setData(dataUri);
- startActivity(signIntent);
+ // TODO: lookup with onactivityresult!
+ startActivityForResult(queryIntent, RESULT_CODE_LOOKUP_KEY);
}
private void shareKey(Uri dataUri, boolean fingerprintOnly) {
- String content = null;
+ String content;
if (fingerprintOnly) {
byte[] fingerprintBlob = ProviderHelper.getFingerprint(this, dataUri);
String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false);
@@ -465,4 +223,29 @@ public class ViewKeyActivity extends ActionBarActivity implements
dialog.show(getSupportFragmentManager(), "shareNfcDialog");
}
+ private void deleteKey(Uri dataUri) {
+ // 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)) {
+ // 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();
+ }
+ }
+ }
+ };
+
+ mExportHelper.deleteKey(dataUri, Id.type.public_key, returnHandler);
+ }
+
}
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 a2e3e4339..59037d9ff 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
@@ -23,7 +23,6 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import android.annotation.TargetApi;
-import android.database.Cursor;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
@@ -35,12 +34,11 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.support.v4.app.LoaderManager;
import android.widget.Toast;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class ViewKeyActivityJB extends ViewKeyActivity implements CreateNdefMessageCallback,
- OnNdefPushCompleteCallback, LoaderManager.LoaderCallbacks<Cursor> {
+ OnNdefPushCompleteCallback {
// NFC
private NfcAdapter mNfcAdapter;
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
new file mode 100644
index 000000000..36d3e6ace
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.content.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 com.beardedhen.androidbootstrap.BootstrapButton;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.util.Log;
+
+
+public class ViewKeyCertsFragment extends Fragment {
+
+ public static final String ARG_DATA_URI = "uri";
+
+ private BootstrapButton mActionCertify;
+
+ private Uri mDataUri;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.view_key_certs_fragment, container, false);
+
+ mActionCertify = (BootstrapButton) view.findViewById(R.id.action_certify);
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
+ if (dataUri == null) {
+ Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
+ getActivity().finish();
+ return;
+ }
+
+ loadData(dataUri);
+ }
+
+ private void loadData(Uri dataUri) {
+ if (dataUri.equals(mDataUri)) {
+ Log.d(Constants.TAG, "Same URI, no need to load the data again!");
+ return;
+ }
+
+ mDataUri = dataUri;
+
+ Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
+
+ mActionCertify.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ certifyKey(mDataUri);
+ }
+ });
+
+ }
+
+ private void certifyKey(Uri dataUri) {
+ Intent signIntent = new Intent(getActivity(), CertifyKeyActivity.class);
+ signIntent.setData(dataUri);
+ startActivity(signIntent);
+ }
+
+} \ No newline at end of file
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
new file mode 100644
index 000000000..495764eaf
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.content.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.text.format.DateFormat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+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.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.adapter.ViewKeyKeysAdapter;
+import org.sufficientlysecure.keychain.ui.adapter.ViewKeyUserIdsAdapter;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.Date;
+
+
+public class ViewKeyMainFragment extends Fragment implements
+ LoaderManager.LoaderCallbacks<Cursor>{
+
+ public static final String ARG_DATA_URI = "uri";
+
+ private TextView mName;
+ private TextView mEmail;
+ private TextView mComment;
+ private TextView mAlgorithm;
+ private TextView mKeyId;
+ private TextView mExpiry;
+ private TextView mCreation;
+ private TextView mFingerprint;
+ private BootstrapButton mActionEncrypt;
+
+ private ListView mUserIds;
+ private ListView mKeys;
+
+ private static final int LOADER_ID_KEYRING = 0;
+ private static final int LOADER_ID_USER_IDS = 1;
+ private static final int LOADER_ID_KEYS = 2;
+
+ private ViewKeyUserIdsAdapter mUserIdsAdapter;
+ private ViewKeyKeysAdapter mKeysAdapter;
+
+ private Uri mDataUri;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.view_key_main_fragment, container, false);
+
+ mName = (TextView) view.findViewById(R.id.name);
+ mEmail = (TextView) view.findViewById(R.id.email);
+ mComment = (TextView) view.findViewById(R.id.comment);
+ mKeyId = (TextView) view.findViewById(R.id.key_id);
+ mAlgorithm = (TextView) view.findViewById(R.id.algorithm);
+ mCreation = (TextView) view.findViewById(R.id.creation);
+ mExpiry = (TextView) view.findViewById(R.id.expiry);
+ mFingerprint = (TextView) view.findViewById(R.id.fingerprint);
+ mUserIds = (ListView) view.findViewById(R.id.user_ids);
+ mKeys = (ListView) view.findViewById(R.id.keys);
+ mActionEncrypt = (BootstrapButton) view.findViewById(R.id.action_encrypt);
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
+ if (dataUri == null) {
+ Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
+ getActivity().finish();
+ return;
+ }
+
+ loadData(dataUri);
+ }
+
+ private void loadData(Uri dataUri) {
+ if (dataUri.equals(mDataUri)) {
+ Log.d(Constants.TAG, "Same URI, no need to load the data again!");
+ return;
+ }
+
+ mDataUri = dataUri;
+
+ Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
+
+ mActionEncrypt.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ encryptToContact(mDataUri);
+ }
+ });
+
+ mUserIdsAdapter = new ViewKeyUserIdsAdapter(getActivity(), null, 0);
+ mUserIds.setAdapter(mUserIdsAdapter);
+
+ mKeysAdapter = new ViewKeyKeysAdapter(getActivity(), null, 0);
+ mKeys.setAdapter(mKeysAdapter);
+
+ // 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_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,};
+ // not the main user id
+ static final String USER_IDS_SELECTION = KeychainContract.UserIds.RANK + " > 0 ";
+ static final String USER_IDS_SORT_ORDER = KeychainContract.UserIds.USER_ID + " 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;
+
+ 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_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, USER_IDS_SELECTION, null,
+ USER_IDS_SORT_ORDER);
+ }
+ 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);
+ }
+
+ default:
+ return null;
+ }
+ }
+
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ switch (loader.getId()) {
+ case LOADER_ID_KEYRING:
+ if (data.moveToFirst()) {
+ // get name, email, and comment from USER_ID
+ String[] mainUserId = PgpKeyHelper.splitUserId(data
+ .getString(KEYRING_INDEX_USER_ID));
+ if (mainUserId[0] != null) {
+ getActivity().setTitle(mainUserId[0]);
+ mName.setText(mainUserId[0]);
+ } else {
+ getActivity().setTitle(R.string.user_id_no_name);
+ mName.setText(R.string.user_id_no_name);
+ }
+ 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);
+
+ String keyIdStr = "0x" + PgpKeyHelper.convertKeyIdToHex(keyId);
+ mKeyId.setText(keyIdStr);
+
+ // get creation date from CREATION
+ if (data.isNull(KEYS_INDEX_CREATION)) {
+ mCreation.setText(R.string.none);
+ } else {
+ Date creationDate = new Date(data.getLong(KEYS_INDEX_CREATION) * 1000);
+
+ mCreation.setText(DateFormat.getDateFormat(getActivity().getApplicationContext()).format(
+ creationDate));
+ }
+
+ // get expiry date from EXPIRY
+ if (data.isNull(KEYS_INDEX_EXPIRY)) {
+ mExpiry.setText(R.string.none);
+ } else {
+ Date expiryDate = new Date(data.getLong(KEYS_INDEX_EXPIRY) * 1000);
+
+ mExpiry.setText(DateFormat.getDateFormat(getActivity().getApplicationContext()).format(
+ expiryDate));
+ }
+
+ String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
+ data.getInt(KEYS_INDEX_ALGORITHM), data.getInt(KEYS_INDEX_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);
+ fingerprint = fingerprint.replace(" ", "\n");
+
+ mFingerprint.setText(fingerprint);
+ }
+
+ mKeysAdapter.swapCursor(data);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /**
+ * This is called when the last Cursor provided to onLoadFinished() above is about to be closed.
+ * We need to make sure we are no longer using it.
+ */
+ public void onLoaderReset(Loader<Cursor> loader) {
+ switch (loader.getId()) {
+ case LOADER_ID_KEYRING:
+ // 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;
+ }
+ }
+
+
+ private void encryptToContact(Uri dataUri) {
+ long keyId = ProviderHelper.getMasterKeyId(getActivity(), dataUri);
+
+ 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);
+ // used instead of startActivity set actionbar based on callingPackage
+ startActivityForResult(intent, 0);
+ }
+
+ private void certifyKey(Uri dataUri) {
+ Intent signIntent = new Intent(getActivity(), CertifyKeyActivity.class);
+ signIntent.setData(dataUri);
+ startActivity(signIntent);
+ }
+
+
+} \ No newline at end of file
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 a850fc020..52186b662 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
@@ -90,16 +90,11 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
View view = mInflater.inflate(R.layout.import_keys_list_entry, null);
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
- mainUserId.setText(R.string.user_id_no_name);
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- mainUserIdRest.setText("");
TextView keyId = (TextView) view.findViewById(R.id.keyId);
- keyId.setText(R.string.no_key);
TextView fingerprint = (TextView) view.findViewById(R.id.fingerprint);
TextView algorithm = (TextView) view.findViewById(R.id.algorithm);
- algorithm.setText("");
TextView status = (TextView) view.findViewById(R.id.status);
- status.setText("");
// main user id
String userId = entry.userIds.get(0);
@@ -113,6 +108,8 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
mainUserId.setTextColor(Color.RED);
}
mainUserId.setText(userIdSplit[0]);
+ } else {
+ mainUserId.setText(R.string.user_id_no_name);
}
// email
@@ -124,12 +121,18 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
}
keyId.setText(entry.hexKeyId);
- fingerprint.setText(mActivity.getString(R.string.fingerprint) + " " + entry.fingerPrint);
+
+ if (entry.fingerPrint != null) {
+ fingerprint.setText(mActivity.getString(R.string.fingerprint) + " " + entry.fingerPrint);
+ fingerprint.setVisibility(View.VISIBLE);
+ } else {
+ fingerprint.setVisibility(View.GONE);
+ }
algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm);
if (entry.revoked) {
- status.setText("revoked");
+ status.setText(R.string.revoked);
} else {
status.setVisibility(View.GONE);
}
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 5094e8abd..4a7a9c93a 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
@@ -48,7 +48,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
private boolean selected;
- private byte[] bytes = new byte[] {};
+ private byte[] bytes = new byte[]{};
public ImportKeysListEntry(ImportKeysListEntry b) {
this.userIds = b.userIds;
@@ -167,7 +167,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
this.revoked = pgpKeyRing.getPublicKey().isRevoked();
this.fingerPrint = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey()
.getFingerprint(), true);
- this.hexKeyId = PgpKeyHelper.convertKeyIdToHex(keyId);
+ this.hexKeyId = "0x" + PgpKeyHelper.convertKeyIdToHex(keyId);
this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength();
int algorithm = pgpKeyRing.getPublicKey().getAlgorithm();
if (algorithm == PGPPublicKey.RSA_ENCRYPT || algorithm == PGPPublicKey.RSA_GENERAL
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 753994450..b3bc39127 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
@@ -79,7 +79,7 @@ public class ImportKeysListServerLoader extends AsyncTaskLoader<List<ImportKeysL
}
/**
- * Query key server
+ * Query keyserver
*/
private void queryServer(String query, String keyServer) {
HkpKeyServer server = new HkpKeyServer(keyServer);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
index f0e926655..257136cbd 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
@@ -23,10 +23,11 @@ import java.util.Set;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.util.Log;
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
+
import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
@@ -35,6 +36,7 @@ import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.RelativeLayout;
import android.widget.TextView;
/**
@@ -44,6 +46,7 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
private LayoutInflater mInflater;
private int mSectionColumnIndex;
private int mIndexUserId;
+ private int mIndexIsRevoked;
@SuppressLint("UseSparseArrays")
private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();
@@ -66,48 +69,59 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
/**
* Get column indexes for performance reasons just once in constructor and swapCursor. For a
* performance comparison see http://stackoverflow.com/a/17999582
- *
+ *
* @param cursor
*/
private void initIndex(Cursor cursor) {
if (cursor != null) {
- mIndexUserId = cursor.getColumnIndexOrThrow(UserIds.USER_ID);
+ mIndexUserId = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.USER_ID);
+ mIndexIsRevoked = cursor.getColumnIndexOrThrow(KeychainContract.Keys.IS_REVOKED);
}
}
/**
* Bind cursor data to the item list view
- *
+ * <p/>
* NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method. Thus
* no ViewHolder is required here.
*/
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
- mainUserId.setText(R.string.user_id_no_name);
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- mainUserIdRest.setText("");
+ TextView revoked = (TextView) view.findViewById(R.id.revoked);
String userId = cursor.getString(mIndexUserId);
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
-
if (userIdSplit[0] != null) {
mainUserId.setText(userIdSplit[0]);
+ } else {
+ mainUserId.setText(R.string.user_id_no_name);
}
if (userIdSplit[1] != null) {
mainUserIdRest.setText(userIdSplit[1]);
+ mainUserIdRest.setVisibility(View.VISIBLE);
+ } else {
+ mainUserIdRest.setVisibility(View.GONE);
+ }
+
+ boolean isRevoked = cursor.getInt(mIndexIsRevoked) > 0;
+ if (isRevoked) {
+ revoked.setVisibility(View.VISIBLE);
+ } else {
+ revoked.setVisibility(View.GONE);
}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.key_list_item, null);
+ return mInflater.inflate(R.layout.key_list_public_item, null);
}
/**
* Creates a new header view and binds the section headers to it. It uses the ViewHolder
* pattern. Most functionality is similar to getView() from Android's CursorAdapter.
- *
+ * <p/>
* NOTE: The variables mDataValid and mCursor are available due to the super class
* CursorAdapter.
*/
@@ -159,8 +173,7 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
}
// return the first character of the name as ID because this is what
- // headers private HashMap<Integer, Boolean> mSelection = new HashMap<Integer,
- // Boolean>();are based upon
+ // headers are based upon
String userId = mCursor.getString(mSectionColumnIndex);
if (userId != null && userId.length() > 0) {
return userId.subSequence(0, 1).charAt(0);
@@ -173,7 +186,9 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
TextView text;
}
- /** -------------------------- MULTI-SELECTION METHODS -------------- */
+ /**
+ * -------------------------- MULTI-SELECTION METHODS --------------
+ */
public void setNewSelection(int position, boolean value) {
mSelection.put(position, value);
notifyDataSetChanged();
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
index ce9b48bff..11d1e8c17 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
@@ -71,24 +71,26 @@ public class KeyListSecretAdapter extends CursorAdapter {
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
- mainUserId.setText(R.string.user_id_no_name);
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- mainUserIdRest.setText("");
String userId = cursor.getString(mIndexUserId);
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
if (userIdSplit[0] != null) {
mainUserId.setText(userIdSplit[0]);
+ } else {
+ mainUserId.setText(R.string.user_id_no_name);
}
if (userIdSplit[1] != null) {
mainUserIdRest.setText(userIdSplit[1]);
+ } else {
+ mainUserIdRest.setText("");
}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.key_list_item, null);
+ return mInflater.inflate(R.layout.key_list_secret_item, null);
}
/** -------------------------- MULTI-SELECTION METHODS -------------- */
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 7e01faf9b..d44dd5890 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
@@ -96,27 +96,31 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
boolean valid = cursor.getInt(mIndexProjectionValid) > 0;
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
- mainUserId.setText(R.string.user_id_no_name);
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- mainUserIdRest.setText("");
TextView keyId = (TextView) view.findViewById(R.id.keyId);
- keyId.setText(R.string.no_key);
TextView status = (TextView) view.findViewById(R.id.status);
- status.setText(R.string.unknown_status);
String userId = cursor.getString(mIndexUserId);
String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
if (userIdSplit[0] != null) {
mainUserId.setText(userIdSplit[0]);
+ } else {
+ mainUserId.setText(R.string.user_id_no_name);
}
if (userIdSplit[1] != null) {
mainUserIdRest.setText(userIdSplit[1]);
+ } else {
+ mainUserIdRest.setText("");
}
+ // TODO: needed to key id to no?
+ keyId.setText(R.string.no_key);
long masterKeyId = cursor.getLong(mIndexMasterKeyId);
keyId.setText(PgpKeyHelper.convertKeyIdToHex(masterKeyId));
+ // TODO: needed to set unknown_status?
+ status.setText(R.string.unknown_status);
if (valid) {
if (mKeyType == Id.type.public_key) {
status.setText(R.string.can_encrypt);
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
new file mode 100644
index 000000000..924a70897
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java
@@ -0,0 +1,84 @@
+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.FragmentStatePagerAdapter;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarActivity;
+
+import java.util.ArrayList;
+
+public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.TabListener,
+ ViewPager.OnPageChangeListener {
+ private final Context mContext;
+ private final ActionBar mActionBar;
+ private final ViewPager mViewPager;
+ private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
+
+ static final class TabInfo {
+ private final Class<?> clss;
+ private final Bundle args;
+
+ TabInfo(Class<?> _class, Bundle _args) {
+ clss = _class;
+ args = _args;
+ }
+ }
+
+ public TabsAdapter(ActionBarActivity activity, ViewPager pager) {
+ super(activity.getSupportFragmentManager());
+ mContext = activity;
+ mActionBar = activity.getSupportActionBar();
+ mViewPager = pager;
+ mViewPager.setAdapter(this);
+ mViewPager.setOnPageChangeListener(this);
+ }
+
+ public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args, boolean selected) {
+ TabInfo info = new TabInfo(clss, args);
+ tab.setTag(info);
+ tab.setTabListener(this);
+ mTabs.add(info);
+ mActionBar.addTab(tab, selected);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount() {
+ return mTabs.size();
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ TabInfo info = mTabs.get(position);
+ return Fragment.instantiate(mContext, info.clss.getName(), info.args);
+ }
+
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ }
+
+ public void onPageSelected(int position) {
+ mActionBar.setSelectedNavigationItem(position);
+ }
+
+ public void onPageScrollStateChanged(int state) {
+ }
+
+ public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
+ Object tag = tab.getTag();
+ for (int i = 0; i < mTabs.size(); i++) {
+ if (mTabs.get(i) == tag) {
+ mViewPager.setCurrentItem(i);
+ }
+ }
+ }
+
+ public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
+ }
+
+ public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
+ }
+} \ No newline at end of file
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 d5162c403..54c7eb60e 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
@@ -76,38 +76,39 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
@Override
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);
+ 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);
+
String keyIdStr = "0x" + PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId));
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(cursor.getInt(mIndexAlgorithm),
cursor.getInt(mIndexKeySize));
- TextView keyId = (TextView) view.findViewById(R.id.keyId);
keyId.setText(keyIdStr);
- TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
keyDetails.setText("(" + algorithmStr + ")");
- ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
if (cursor.getInt(mIndexIsMasterKey) != 1) {
masterKeyIcon.setVisibility(View.INVISIBLE);
} else {
masterKeyIcon.setVisibility(View.VISIBLE);
}
- ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
if (cursor.getInt(mIndexCanCertify) != 1) {
certifyIcon.setVisibility(View.GONE);
} else {
certifyIcon.setVisibility(View.VISIBLE);
}
- ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
if (cursor.getInt(mIndexCanEncrypt) != 1) {
encryptIcon.setVisibility(View.GONE);
} else {
encryptIcon.setVisibility(View.VISIBLE);
}
- ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
if (cursor.getInt(mIndexCanSign) != 1) {
signIcon.setVisibility(View.GONE);
} else {
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 109bfe929..3c44bff81 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
@@ -23,12 +23,16 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
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;
@@ -36,6 +40,8 @@ import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
+import java.util.ArrayList;
+
public class DeleteKeyDialogFragment extends DialogFragment {
private static final String ARG_MESSENGER = "messenger";
private static final String ARG_DELETE_KEY_RING_ROW_IDS = "delete_file";
@@ -43,13 +49,15 @@ public class DeleteKeyDialogFragment extends DialogFragment {
public static final int MESSAGE_OKAY = 1;
+ public static final String MESSAGE_NOT_DELETED = "not_deleted";
+
private Messenger mMessenger;
/**
* Creates new instance of this delete file dialog fragment
*/
public static DeleteKeyDialogFragment newInstance(Messenger messenger, long[] keyRingRowIds,
- int keyType) {
+ int keyType) {
DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment();
Bundle args = new Bundle();
@@ -77,20 +85,13 @@ public class DeleteKeyDialogFragment extends DialogFragment {
builder.setTitle(R.string.warning);
if (keyRingRowIds.length == 1) {
- // TODO: better way to do this?
- String userId = activity.getString(R.string.user_id_no_name);
-
+ Uri dataUri;
if (keyType == Id.type.public_key) {
- PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByRowId(activity,
- keyRingRowIds[0]);
- userId = PgpKeyHelper.getMainUserIdSafe(activity,
- PgpKeyHelper.getMasterKey(keyRing));
+ dataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowIds[0]));
} else {
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByRowId(activity,
- keyRingRowIds[0]);
- userId = PgpKeyHelper.getMainUserIdSafe(activity,
- PgpKeyHelper.getMasterKey(keyRing));
+ dataUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowIds[0]));
}
+ String userId = ProviderHelper.getUserId(activity, dataUri);
builder.setMessage(getString(
keyType == Id.type.public_key ? R.string.key_deletion_confirmation
@@ -104,9 +105,61 @@ public class DeleteKeyDialogFragment extends DialogFragment {
@Override
public void onClick(DialogInterface dialog, int id) {
+ ArrayList<String> notDeleted = new ArrayList<String>();
+
if (keyType == Id.type.public_key) {
- for (long keyRowId : keyRingRowIds) {
- ProviderHelper.deletePublicKeyRing(activity, keyRowId);
+ 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) {
@@ -116,7 +169,13 @@ public class DeleteKeyDialogFragment extends DialogFragment {
dismiss();
- sendMessageToHandler(MESSAGE_OKAY);
+ if (notDeleted.size() > 0) {
+ Bundle data = new Bundle();
+ data.putStringArrayList(MESSAGE_NOT_DELETED, notDeleted);
+ sendMessageToHandler(MESSAGE_OKAY, data);
+ } else {
+ sendMessageToHandler(MESSAGE_OKAY, null);
+ }
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@@ -131,13 +190,15 @@ public class DeleteKeyDialogFragment extends DialogFragment {
/**
* Send message back to handler which is initialized in a activity
- *
- * @param what
- * Message integer you want to send
+ *
+ * @param what Message integer you want to send
*/
- private void sendMessageToHandler(Integer what) {
+ private void sendMessageToHandler(Integer what, Bundle data) {
Message msg = Message.obtain();
msg.what = what;
+ if (data != null) {
+ msg.setData(data);
+ }
try {
mMessenger.send(msg);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/LookupUnknownKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/LookupUnknownKeyDialogFragment.java
deleted file mode 100644
index a0592285f..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/LookupUnknownKeyDialogFragment.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.ui.dialog;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.ui.ImportKeysActivity;
-import org.sufficientlysecure.keychain.util.Log;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.support.v4.app.DialogFragment;
-
-public class LookupUnknownKeyDialogFragment extends DialogFragment {
- private static final String ARG_MESSENGER = "messenger";
- private static final String ARG_UNKNOWN_KEY_ID = "unknown_key_id";
-
- public static final int MESSAGE_OKAY = 1;
- public static final int MESSAGE_CANCEL = 2;
-
- private Messenger mMessenger;
-
- /**
- * Creates new instance of this dialog fragment
- *
- * @param messenger
- * @param unknownKeyId
- * @return
- */
- public static LookupUnknownKeyDialogFragment newInstance(Messenger messenger, long unknownKeyId) {
- LookupUnknownKeyDialogFragment frag = new LookupUnknownKeyDialogFragment();
- Bundle args = new Bundle();
- args.putLong(ARG_UNKNOWN_KEY_ID, unknownKeyId);
- args.putParcelable(ARG_MESSENGER, messenger);
-
- frag.setArguments(args);
-
- return frag;
- }
-
- /**
- * Creates dialog
- */
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final Activity activity = getActivity();
-
- final long unknownKeyId = getArguments().getLong(ARG_UNKNOWN_KEY_ID);
- mMessenger = getArguments().getParcelable(ARG_MESSENGER);
-
- AlertDialog.Builder alert = new AlertDialog.Builder(activity);
-
- alert.setIcon(android.R.drawable.ic_dialog_alert);
- alert.setTitle(R.string.title_unknown_signature_key);
- alert.setMessage(getString(R.string.lookup_unknown_key,
- PgpKeyHelper.convertKeyIdToHex(unknownKeyId)));
-
- alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int id) {
- dismiss();
-
- sendMessageToHandler(MESSAGE_OKAY);
-
- Intent intent = new Intent(activity, ImportKeysActivity.class);
- intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEY_SERVER);
- intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, unknownKeyId);
- startActivityForResult(intent, Id.request.look_up_key_id);
- }
- });
- alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int id) {
- dismiss();
-
- sendMessageToHandler(MESSAGE_CANCEL);
- }
- });
- alert.setCancelable(true);
- alert.setOnCancelListener(new OnCancelListener() {
-
- @Override
- public void onCancel(DialogInterface dialog) {
- sendMessageToHandler(MESSAGE_CANCEL);
- }
- });
-
- return alert.create();
- }
-
- /**
- * Send message back to handler which is initialized in a activity
- *
- * @param what
- * Message integer you want to send
- */
- private void sendMessageToHandler(Integer what) {
- Message msg = Message.obtain();
- msg.what = what;
-
- try {
- mMessenger.send(msg);
- } catch (RemoteException e) {
- Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
- } catch (NullPointerException e) {
- Log.w(Constants.TAG, "Messenger is null!", e);
- }
- }
-} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java
index 05e52fb47..61fe13ffb 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
@@ -50,6 +50,13 @@ import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
import android.text.Html;
+/**
+ * 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;
@@ -181,8 +188,8 @@ public class HkpKeyServer extends KeyServer {
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));
- info.fingerPrint = PgpKeyHelper.convertKeyIdToHex(info.keyId);
String chunks[] = matcher.group(4).split("-");
GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
diff --git a/OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml b/OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml
index 79daef590..c60416494 100644
--- a/OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml
@@ -1,25 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:orientation="vertical" >
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
- <TextView
- android:id="@+id/api_register_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingBottom="3dip"
- android:text="@string/api_register_text"
- android:textAppearance="?android:attr/textAppearanceLarge" />
-
- <fragment
- android:id="@+id/api_app_settings_fragment"
- android:name="org.sufficientlysecure.keychain.service.remote.AppSettingsFragment"
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- tools:layout="@layout/api_app_settings_fragment" />
+ android:layout_height="match_parent"
+ android:padding="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/api_register_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="3dip"
+ android:text="@string/api_register_text"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <fragment
+ android:id="@+id/api_app_settings_fragment"
+ android:name="org.sufficientlysecure.keychain.service.remote.AppSettingsFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:layout="@layout/api_app_settings_fragment" />
-</LinearLayout> \ No newline at end of file
+ </LinearLayout>
+</ScrollView> \ 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 e60ad50e3..d4fb5103a 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
@@ -1,17 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<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"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:orientation="vertical" >
+ android:layout_height="match_parent">
- <fragment
- android:id="@+id/api_app_settings_fragment"
- android:name="org.sufficientlysecure.keychain.service.remote.AppSettingsFragment"
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- tools:layout="@layout/api_app_settings_fragment" />
+ android:layout_height="match_parent"
+ android:padding="16dp"
+ android:orientation="vertical">
-</LinearLayout> \ No newline at end of file
+ <fragment
+ android:id="@+id/api_app_settings_fragment"
+ android:name="org.sufficientlysecure.keychain.service.remote.AppSettingsFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:layout="@layout/api_app_settings_fragment" />
+
+ </LinearLayout>
+</ScrollView> \ No newline at end of file
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 0695e5922..a8b68859b 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
@@ -1,129 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="wrap_content" >
+ 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_app_settings_app_icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginRight="6dp"
+ android:src="@drawable/icon" />
+
+ <TextView
+ android:id="@+id/api_app_settings_app_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toRightOf="@+id/api_app_settings_app_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_app_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_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: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" />
- <RelativeLayout
+ <TextView
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_app_settings_app_icon"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_alignParentBottom="true"
- android:layout_alignParentTop="true"
- android:layout_marginRight="6dp"
- android:src="@drawable/icon" />
-
- <TextView
- android:id="@+id/api_app_settings_app_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_toRightOf="@+id/api_app_settings_app_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_app_settings_select_key_fragment"
- android:name="org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment"
+ 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"
- tools:layout="@layout/select_secret_key_layout_fragment" />
+ android:text="@string/label_message_compression"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
- <com.beardedhen.androidbootstrap.BootstrapButton
- android:id="@+id/api_app_settings_advanced_button"
+ <Spinner
+ android:id="@+id/api_app_settings_compression"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
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:text="@string/api_settings_package_name"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:id="@+id/api_app_settings_package_name"
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" />
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/api_settings_package_name"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <TextView
- android:id="@+id/api_app_settings_package_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="com.example"
- android:textAppearance="?android:attr/textAppearanceSmall" />
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/api_settings_package_signature"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <TextView
- android:id="@+id/api_app_settings_package_signature"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Base64 encoded signature"
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </LinearLayout>
- </LinearLayout>
+ android:text="com.example"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/api_settings_package_signature"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
-</ScrollView> \ No newline at end of file
+ <TextView
+ android:id="@+id/api_app_settings_package_signature"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Base64 encoded signature"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/api_apps_adapter_list_item.xml b/OpenPGP-Keychain/src/main/res/layout/api_apps_adapter_list_item.xml
index cb20a20af..e70a79589 100644
--- a/OpenPGP-Keychain/src/main/res/layout/api_apps_adapter_list_item.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/api_apps_adapter_list_item.xml
@@ -1,28 +1,26 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:gravity="left"
- android:orientation="horizontal" >
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
<ImageView
android:id="@+id/api_apps_adapter_item_icon"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_alignParentBottom="true"
- android:layout_alignParentTop="true"
android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
+ android:layout_centerVertical="true"
android:src="@drawable/icon" />
<TextView
android:id="@+id/api_apps_adapter_item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:text="Application Name"
+ android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_centerVertical="true"
- android:layout_toRightOf="@+id/api_apps_adapter_item_icon"
- android:gravity="center_vertical"
- android:orientation="vertical"
- android:text="Set in-code!"
- android:textAppearance="?android:attr/textAppearanceMedium" />
+ android:layout_toRightOf="@+id/api_apps_adapter_item_icon" />
</RelativeLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/sign_key_activity.xml b/OpenPGP-Keychain/src/main/res/layout/certify_key_activity.xml
index 07f63b91b..ddb424ee8 100644
--- a/OpenPGP-Keychain/src/main/res/layout/sign_key_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/certify_key_activity.xml
@@ -18,7 +18,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginTop="14dp"
- android:text="@string/section_signing_key" />
+ android:text="@string/section_certification_key" />
<fragment
android:id="@+id/sign_key_select_key_fragment"
@@ -60,7 +60,7 @@
android:layout_height="60dp"
android:layout_marginBottom="4dp"
android:layout_marginTop="14dp"
- android:text="@string/btn_sign"
+ android:text="@string/btn_certify"
bootstrapbutton:bb_icon_left="fa-pencil"
bootstrapbutton:bb_type="info" />
</LinearLayout>
diff --git a/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml b/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml
index c6834d745..e6c81c3fc 100644
--- a/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml
@@ -20,10 +20,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
+ android:paddingTop="4dp"
android:paddingLeft="10dp"
android:paddingRight="10dp">
- <LinearLayout
+ <RelativeLayout
android:id="@+id/signature"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -35,7 +36,8 @@
<RelativeLayout
android:layout_width="wrap_content"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:id="@+id/relativeLayout">
<ImageView
android:id="@+id/ic_signature"
@@ -50,29 +52,40 @@
android:src="@drawable/overlay_error" />
</RelativeLayout>
- <LinearLayout
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/lookup_key"
+ android:visibility="gone"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="5dip">
+ 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="Main User Id"
- android:textAppearance="?android:attr/textAppearanceMedium" />
+ <TextView
+ android:id="@+id/mainUserId"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"
+ android:text="Main User Id"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_alignTop="@+id/linearLayout"
+ 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" />
- </LinearLayout>
- </LinearLayout>
+ <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"
diff --git a/OpenPGP-Keychain/src/main/res/layout/drawer_list_item.xml b/OpenPGP-Keychain/src/main/res/layout/drawer_list_item.xml
index 14760e79d..72f4fec50 100644
--- a/OpenPGP-Keychain/src/main/res/layout/drawer_list_item.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/drawer_list_item.xml
@@ -1,28 +1,33 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fontawesometext="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
+ android:layout_height="wrap_content">
<com.beardedhen.androidbootstrap.FontAwesomeText
android:id="@+id/drawer_item_icon"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:layout_margin="10dp"
+ android:layout_width="30dp"
+ android:layout_height="wrap_content"
android:gravity="center_vertical"
android:textSize="24sp"
- fontawesometext:fa_icon="fa-github" />
+ android:layout_marginLeft="8dp"
+ fontawesometext:fa_icon="fa-github"
+ android:layout_centerVertical="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
<TextView
android:id="@+id/drawer_item_text"
+ android:text="Test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
+ android:layout_marginLeft="8dp"
android:paddingBottom="16dp"
- android:paddingLeft="4dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
android:textAppearance="@android:style/TextAppearance.Medium"
- android:textColor="#111" />
+ android:textColor="#111"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@+id/drawer_item_icon" />
-</LinearLayout>
+</RelativeLayout>
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml
deleted file mode 100644
index 2571bb6e7..000000000
--- a/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:layout_marginRight="?android:attr/scrollbarSize"
- android:orientation="vertical"
- android:paddingLeft="8dp"
- android:singleLine="true" >
-
- <TextView
- android:id="@+id/mainUserId"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Main User ID"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <TextView
- android:id="@+id/mainUserIdRest"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="&lt;user@example.com>"
- android:textAppearance="?android:attr/textAppearanceSmall" />
-
-</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_public_item.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_public_item.xml
new file mode 100644
index 000000000..9307ab2e5
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/key_list_public_item.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:layout_marginRight="?android:attr/scrollbarSize"
+ android:gravity="center_vertical"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:singleLine="true">
+
+ <TextView
+ android:id="@+id/mainUserId"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Main User ID"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ <TextView
+ android:id="@+id/mainUserIdRest"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="&lt;user@example.com>"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_below="@+id/mainUserId"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ <TextView
+ android:id="@+id/revoked"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="@string/revoked"
+ android:textColor="#e00"
+ android:visibility="gone"
+ 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/key_list_secret_activity.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_secret_activity.xml
index 13370f2e5..cd208a545 100644
--- a/OpenPGP-Keychain/src/main/res/layout/key_list_secret_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/key_list_secret_activity.xml
@@ -2,17 +2,21 @@
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_height="match_parent">
<fragment
android:id="@+id/key_list_secret_fragment"
android:name="org.sufficientlysecure.keychain.ui.KeyListSecretFragment"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"
+ android:paddingBottom="16dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:scrollbarStyle="outsideOverlay" />
</FrameLayout>
<include layout="@layout/drawer_list" />
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_secret_item.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_secret_item.xml
new file mode 100644
index 000000000..1ed86f730
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/key_list_secret_item.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:paddingLeft="8dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:singleLine="true">
+
+ <TextView
+ android:id="@+id/mainUserId"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Main User ID"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ <TextView
+ android:id="@+id/mainUserIdRest"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="&lt;user@example.com>"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_below="@+id/mainUserId"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_activity.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_activity.xml
index 73a86a725..58e4919dc 100644
--- a/OpenPGP-Keychain/src/main/res/layout/view_key_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/view_key_activity.xml
@@ -1,220 +1,12 @@
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
- <LinearLayout
+ <android.support.v4.view.ViewPager
+ android:id="@+id/pager"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="16dp"
- android:paddingRight="16dp">
+ android:layout_height="match_parent" />
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_master_user_id" />
-
- <TableLayout
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:stretchColumns="1">
-
- <TableRow>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:paddingRight="10dip"
- android:text="@string/label_name" />
-
- <TextView
- android:id="@+id/name"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:paddingRight="5dip"
- android:text="" />
- </TableRow>
-
- <TableRow>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:paddingRight="10dip"
- android:text="@string/label_email" />
-
- <TextView
- android:id="@+id/email"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:paddingRight="5dip"
- android:text="" />
- </TableRow>
-
- <TableRow>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:paddingRight="10dip"
- android:text="@string/label_comment" />
-
- <TextView
- android:id="@+id/comment"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:paddingRight="5dip"
- android:text="" />
- </TableRow>
- </TableLayout>
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_master_key" />
-
- <TableLayout
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:stretchColumns="1">
-
- <TableRow>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
- android:text="@string/label_key_id" />
-
- <TextView
- android:id="@+id/key_id"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingRight="5dip"
- android:text=""
- android:typeface="monospace" />
- </TableRow>
-
- <TableRow>
-
- <TextView
- android:id="@+id/label_algorithm"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
- android:text="@string/label_algorithm" />
-
- <TextView
- android:id="@+id/algorithm"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingRight="5dip"
- android:text="" />
- </TableRow>
-
- <TableRow>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
- android:text="@string/label_creation" />
-
- <TextView
- android:id="@+id/creation"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- </TableRow>
-
- <TableRow>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
- android:text="@string/label_expiry" />
-
- <TextView
- android:id="@+id/expiry"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- </TableRow>
-
- <TableRow>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
- android:text="@string/label_fingerprint" />
-
- <TextView
- android:id="@+id/fingerprint"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:typeface="monospace" />
- </TableRow>
- </TableLayout>
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_user_ids" />
-
- <org.sufficientlysecure.keychain.ui.widget.FixedListView
- android:id="@+id/user_ids"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_keys" />
-
- <org.sufficientlysecure.keychain.ui.widget.FixedListView
- android:id="@+id/keys"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_actions" />
-
- <com.beardedhen.androidbootstrap.BootstrapButton
- android:id="@+id/action_encrypt"
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:padding="4dp"
- android:text="@string/key_view_action_encrypt"
- bootstrapbutton:bb_icon_left="fa-lock"
- bootstrapbutton:bb_type="info" />
- </LinearLayout>
-
-</ScrollView> \ No newline at end of file
+</LinearLayout> \ No newline at end of file
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
new file mode 100644
index 000000000..299471c66
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml
@@ -0,0 +1,38 @@
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="14dp"
+ android:text="Display of existing certifications is a planned feature for a later release of OpenPGP Keychain. Stay tuned for updates!" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_actions" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/action_certify"
+ android:layout_width="match_parent"
+ android:layout_height="60dp"
+ android:padding="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
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 b50253980..c44835bb0 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
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:layout_marginRight="?android:attr/scrollbarSize"
+ android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="8dip"
android:paddingRight="3dip"
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
new file mode 100644
index 000000000..055687183
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml
@@ -0,0 +1,226 @@
+<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">
+
+ <!-- focusable and related properties to workaround http://stackoverflow.com/q/16182331-->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:descendantFocusability="beforeDescendants"
+ android:orientation="vertical"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp">
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_master_user_id" />
+
+ <TableLayout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:stretchColumns="1">
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:paddingRight="10dip"
+ android:text="@string/label_name" />
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:text="" />
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:paddingRight="10dip"
+ android:text="@string/label_email" />
+
+ <TextView
+ android:id="@+id/email"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:text="" />
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:paddingRight="10dip"
+ android:text="@string/label_comment" />
+
+ <TextView
+ android:id="@+id/comment"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:text="" />
+ </TableRow>
+ </TableLayout>
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_master_key" />
+
+ <TableLayout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:stretchColumns="1">
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_key_id" />
+
+ <TextView
+ android:id="@+id/key_id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:text=""
+ android:typeface="monospace" />
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_algorithm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_algorithm" />
+
+ <TextView
+ android:id="@+id/algorithm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:text="" />
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_creation" />
+
+ <TextView
+ android:id="@+id/creation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_expiry" />
+
+ <TextView
+ android:id="@+id/expiry"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_fingerprint" />
+
+ <TextView
+ android:id="@+id/fingerprint"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:typeface="monospace" />
+ </TableRow>
+ </TableLayout>
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_user_ids" />
+
+ <org.sufficientlysecure.keychain.ui.widget.FixedListView
+ android:id="@+id/user_ids"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_keys" />
+
+ <org.sufficientlysecure.keychain.ui.widget.FixedListView
+ android:id="@+id/keys"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_actions" />
+
+ <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:text="@string/key_view_action_encrypt"
+ bootstrapbutton:bb_icon_left="fa-lock"
+ bootstrapbutton:bb_type="info" />
+
+ </LinearLayout>
+
+</ScrollView> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_userids_item.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_userids_item.xml
index 2d022ba13..508d080a6 100644
--- a/OpenPGP-Keychain/src/main/res/layout/view_key_userids_item.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/view_key_userids_item.xml
@@ -1,16 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:layout_marginRight="?android:attr/scrollbarSize"
+ android:layout_height="wrap_content"
android:orientation="vertical"
- android:singleLine="true" >
+ android:paddingRight="3dip"
+ android:singleLine="true">
<TextView
android:id="@+id/userId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingRight="3dip"
android:text="User ID"
android:textAppearance="?android:attr/textAppearanceSmall" />
diff --git a/OpenPGP-Keychain/src/main/res/menu/key_view.xml b/OpenPGP-Keychain/src/main/res/menu/key_view.xml
index acf3eb099..59247221a 100644
--- a/OpenPGP-Keychain/src/main/res/menu/key_view.xml
+++ b/OpenPGP-Keychain/src/main/res/menu/key_view.xml
@@ -65,10 +65,6 @@
</menu>
</item>
<item
- android:id="@+id/menu_key_view_sign"
- app:showAsAction="ifRoom"
- android:title="@string/menu_sign_key" />
- <item
android:id="@+id/menu_key_view_export_file"
app:showAsAction="never"
android:title="@string/menu_export_key" />
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 0ebedb1a1..89f74e9b0 100644
--- a/OpenPGP-Keychain/src/main/res/raw-de/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-de/help_about.html
@@ -23,7 +23,9 @@
<h2>Bibliotheken</h2>
<ul>
<li>
-<a href="http://actionbarsherlock.com">ActionBarSherlock</a> (Apache Lizenz v2)</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 Lizenz v2)</li>
<li>
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 f6f6844a2..56dcf0c8b 100644
--- a/OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html
@@ -3,10 +3,14 @@
<body>
<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>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
<li>fix upload of key from signing screen</li>
</ul>
<h2>2.2</h2>
@@ -42,7 +46,7 @@
</ul>
<h2>1.0.8</h2>
<ul>
-<li>Einfacher Schlüsselserversupport</li>
+<li>basic keyserver support</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>
diff --git a/OpenPGP-Keychain/src/main/res/raw-de/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-de/help_nfc_beam.html
index 2e7e637e5..88492731c 100644
--- a/OpenPGP-Keychain/src/main/res/raw-de/help_nfc_beam.html
+++ b/OpenPGP-Keychain/src/main/res/raw-de/help_nfc_beam.html
@@ -3,7 +3,7 @@
<body>
<h2>How to receive keys</h2>
<ol>
-<li>Go to your partners 'Manage Public Keys' and long press on the key you want to share.</li>
+<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>
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 1386e8cc1..198dfe64f 100644
--- a/OpenPGP-Keychain/src/main/res/raw-de/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-de/help_start.html
@@ -1,22 +1,19 @@
<html>
<head></head>
<body>
-<h2>EXPERIMENTAL software</h2>
-<p>This is EXPERIMENTAL software. Use at your own risk!</p>
-
<h2>Getting started</h2>
-<p>First you need some keys. Import or create them via the option menus in "My Secret Keys".<!--<p>Install K-9 Mail for the best integration, it will supports OpenPGP Keychain for PGP/INLINE and lets you directly encrypt/decrypt emails.-->
-<br>It is recommended that you install OI File Manager to be able to use the browse button for file selection in OpenPGP Keychain.</p>
+<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>Big ToDos</h2>
-<ul>
-<li>K9 Mail integration not published</li>
-<li>Importing existing keys will be stripped of certificates right now</li>
-<li>PGP/MIME in K9 Mail is missing</li>
-</ul>
-<p>If you want to contribute, fork it and do a pull request on Github: <a href="https://github.com/dschuermann/openpgp-keychain">https://github.com/dschuermann/openpgp-keychain</a></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 OpenPGP Keychain!</h2>
-<p>Please report it in the <a href="https://github.com/dschuermann/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+
+<h2>Contribute</h2>
+<p>If you want to help us developing OpenPGP Keychain 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 OpenPGP Keychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenPGP Keychain on Transifex</a>.</p>
+
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-de/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-de/nfc_beam_share.html
index 453d435e3..083e055c7 100644
--- a/OpenPGP-Keychain/src/main/res/raw-de/nfc_beam_share.html
+++ b/OpenPGP-Keychain/src/main/res/raw-de/nfc_beam_share.html
@@ -3,8 +3,8 @@
<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>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>
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 7db2f83de..773d11fa7 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
@@ -23,7 +23,9 @@
<h2>Libraries</h2>
<ul>
<li>
-<a href="http://actionbarsherlock.com">ActionBarSherlock</a> (Apache License v2)</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>
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 32ad1a13d..abf660ba8 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
@@ -3,10 +3,14 @@
<body>
<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>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
<li>fix upload of key from signing screen</li>
</ul>
<h2>2.2</h2>
@@ -42,7 +46,7 @@
</ul>
<h2>1.0.8</h2>
<ul>
-<li>basic key server support</li>
+<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>
diff --git a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_nfc_beam.html
index 2e7e637e5..88492731c 100644
--- a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_nfc_beam.html
+++ b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_nfc_beam.html
@@ -3,7 +3,7 @@
<body>
<h2>How to receive keys</h2>
<ol>
-<li>Go to your partners 'Manage Public Keys' and long press on the key you want to share.</li>
+<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>
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 1386e8cc1..198dfe64f 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
@@ -1,22 +1,19 @@
<html>
<head></head>
<body>
-<h2>EXPERIMENTAL software</h2>
-<p>This is EXPERIMENTAL software. Use at your own risk!</p>
-
<h2>Getting started</h2>
-<p>First you need some keys. Import or create them via the option menus in "My Secret Keys".<!--<p>Install K-9 Mail for the best integration, it will supports OpenPGP Keychain for PGP/INLINE and lets you directly encrypt/decrypt emails.-->
-<br>It is recommended that you install OI File Manager to be able to use the browse button for file selection in OpenPGP Keychain.</p>
+<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>Big ToDos</h2>
-<ul>
-<li>K9 Mail integration not published</li>
-<li>Importing existing keys will be stripped of certificates right now</li>
-<li>PGP/MIME in K9 Mail is missing</li>
-</ul>
-<p>If you want to contribute, fork it and do a pull request on Github: <a href="https://github.com/dschuermann/openpgp-keychain">https://github.com/dschuermann/openpgp-keychain</a></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 OpenPGP Keychain!</h2>
-<p>Please report it in the <a href="https://github.com/dschuermann/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+
+<h2>Contribute</h2>
+<p>If you want to help us developing OpenPGP Keychain 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 OpenPGP Keychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenPGP Keychain on Transifex</a>.</p>
+
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-es-rCO/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-es-rCO/nfc_beam_share.html
index 453d435e3..083e055c7 100644
--- a/OpenPGP-Keychain/src/main/res/raw-es-rCO/nfc_beam_share.html
+++ b/OpenPGP-Keychain/src/main/res/raw-es-rCO/nfc_beam_share.html
@@ -3,8 +3,8 @@
<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>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>
diff --git a/OpenPGP-Keychain/src/main/res/raw-es/help_about.html b/OpenPGP-Keychain/src/main/res/raw-es/help_about.html
new file mode 100644
index 000000000..a81789cec
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-es/help_about.html
@@ -0,0 +1,43 @@
+<html>
+<head></head>
+<body>
+<p><a href="http://sufficientlysecure.org/keychain">http://sufficientlysecure.org/keychain</a></p>
+<p><a href="http://sufficientlysecure.org/keychain">OpenPGP Keychain</a> es una implementación de OpenPGP para Android. Su desarrollo comenzó como un fork de Android Privacy Guard (APG).</p>
+<p>Licencia: GPLv3+</p>
+
+<h2>Desarrolladores de OpenPGP Keychain</h2>
+<ul>
+<li>Dominik Schürmann (Desarrollador principal)</li>
+<li>Ash Hughes (Parches cryptográficos)</li>
+<li>Brian C. Barnes</li>
+<li>Bahtiar 'kalkin' Gadimov (UI)</li>
+
+</ul>
+<h2>Desarrolladores de APG 1.x</h2>
+<ul>
+<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>
+<ul>
+<li>
+<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v4</a> (Licencia Apache v2)</li>
+<li>
+<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v7 'appcompat'</a> (Licencia Apache v2)</li>
+<li>
+<a href="https://github.com/emilsjolander/StickyListHeaders">StickyListHeaders</a> (Licencia Apache v2)</li>
+<li>
+<a href="https://github.com/Bearded-Hen/Android-Bootstrap">Android-Bootstrap</a> (Licencia MIT)</li>
+<li>
+<a href="http://code.google.com/p/zxing/">ZXing</a> (Licencia Apache v2)</li>
+<li>
+<a href="http://rtyley.github.com/spongycastle/">SpongyCastle</a> (Licencia MIT X11)</li>
+<li>
+<a href="https://github.com/dschuermann/html-textview">HtmlTextView</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
new file mode 100644
index 000000000..dfb51dc81
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-es/help_changelog.html
@@ -0,0 +1,108 @@
+<html>
+<head></head>
+<body>
+<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>
+<li>corrige la configuración de la fecha de caducidad en las claves (gracias a Ash Hughes)</li>
+<li>más correcciones internas cuando se editan claves (gracias a Ash Hughes)</li>
+<li>consultar los servidores de claves directamente desde la ventana de importación</li>
+<li>corrige el diseño y estilo de mensajes en Android 2.2-3.0</li>
+<li>corrige error en claves con IDs de usuario vacías</li>
+<li>corrige fallo y listados vacíos cuando se regresa desde la pantalla de firma</li>
+<li>Bouncy Castle (librería criptográfica) actualizada de 1.47 a 1.50 y compilada desde la fuente</li>
+<li>corrige la carga de la clave desde la pantalla de firma</li>
+</ul>
+<h2>2.2</h2>
+<ul>
+<li>nuevo diseño con Navigation Drawer</li>
+<li>nuevo diseño de la lista de clave pública</li>
+<li>nueva vista de la clave pública</li>
+<li>correcciones en la importación de claves</li>
+<li>clave de certificación cruzada (gracias a Ash Hughes)</li>
+<li>manejo correcto de las contraseñas UTF-8 (gracias a Ash Hughes)</li>
+<li>primera versión con nuevos idiomas (gracias a los colaboradores en Transifex)</li>
+<li>compartir claves a través de códigos QR corregido y mejorado</li>
+<li>verificación por API del paquete de firma</li>
+</ul>
+<h2>2.1.1</h2>
+<ul>
+<li>Actualizaciones de la API, preparación para la integración con K-9 Mail</li>
+</ul>
+<h2>2.1</h2>
+<ul>
+<li>corrección de muchos bugs</li>
+<li>nueva API para desarrolladores</li>
+<li>corrección del bug PRNG por Google</li>
+</ul>
+<h2>2.0</h2>
+<ul>
+<li>completo rediseño</li>
+<li>compartir claves públicas a través de códigos QR, NFC, Beam</li>
+<li>claves de firma</li>
+<li>cargar claves al servidor</li>
+<li>corrige problemas importantes</li>
+<li>nueva API AIDL</li>
+</ul>
+<h2>1.0.8</h2>
+<ul>
+<li>compatibilidad básica de los servidores de claves</li>
+<li>app-a-sd</li>
+<li>más opciones para la caché de la frase de contraseña: 1, 2, 4, 8 horas</li>
+<li>traducciones: noruego (gracias, Sander Danielsen), chino (gracias, Zhang Fredrick)</li>
+<li>correcciones de errores</li>
+<li>optimizaciones</li>
+</ul>
+<h2>1.0.7</h2>
+<ul>
+<li>corregido el problema con la verificación de firma de textos que arrastran a una nueva línea</li>
+<li>más opciones para el tiempo de la caché de la frase de contraseña hasta ahora (20, 40, 60 mins)</li>
+</ul>
+<h2>1.0.6</h2>
+<ul>
+<li>corregido el problema al añadir cuentas en Froyo</li>
+<li>borrado seguro de archivo</li>
+<li>opción para borrar el archivo de clave después de importarlo</li>
+<li>flujo de cifrado/descifrado (galería, etc.)</li>
+<li>nuevas opciones (idioma, forzar firmas v3)</li>
+<li>cambios en la interfaz</li>
+<li>correcciones de errores</li>
+</ul>
+<h2>1.0.5</h2>
+<ul>
+<li>traducciones a alemán e italiano</li>
+<li>paquete de mucho menos tamaño, debido a fuentes BC reducidas</li>
+<li>nuevas preferencias en la GUI</li>
+<li>ajuste del diseño para localización</li>
+<li>corrección de error en la firma</li>
+</ul>
+<h2>1.0.4</h2>
+<ul>
+<li>corregido otro error causado por algún bug en el SDK con el constructor de consultas</li>
+</ul>
+<h2>1.0.3</h2>
+<ul>
+<li>corregidos los errores durante el cifrado/firma y probablemente en la exportación de la clave</li>
+</ul>
+<h2>1.0.2</h2>
+<ul>
+<li>listas de claves con filtro</li>
+<li>preselección de claves de cifrado más inteligente</li>
+<li>nuevo intento en el manejo para VER y ENVIAR, permite que los archivos sean cifrados/descifrados fuera de los gestores de archivos</li>
+<li>corrige y añade características (preselección de clave) para K-9 Mail, nueva compilación disponible</li>
+</ul>
+<h2>1.0.1</h2>
+<ul>
+<li>La enumeración de cuentas de GMail no funcionaba en 1.0.0, corregida de nuevo</li>
+</ul>
+<h2>1.0.0</h2>
+<ul>
+<li>integración con K-9 Mail, APG compatible con la compilación beta de K-9 Mail</li>
+<li>compatibilidad para más gestores de archivos (incluyendo ASTRO)</li>
+<li>traducción al esloveno</li>
+<li>nueva base de datos, más rápida, con menos demanda de memoria</li>
+<li>definidos los intentos y el proveedor de contenido para otras aplicaciones</li>
+<li>correcciones de errores</li>
+</ul>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-es/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-es/help_nfc_beam.html
new file mode 100644
index 000000000..4a95680b5
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-es/help_nfc_beam.html
@@ -0,0 +1,12 @@
+<html>
+<head></head>
+<body>
+<h2>Cómo recibir las claves</h2>
+<ol>
+<li>Vete a los contactos de tu compañero y abre el contacto con el que quieres compartir</li>
+<li>Mantén los dos dispositivos de con ambos reversos juntos (tienen que estar casi en contacto) y notarás una vibración.</li>
+<li>Después de que vibre, verás el contenido en el dispositivo de tu compañero convertirse en una especie de ficha con una animación de Star Trek de fondo.</li>
+<li>Toca la ficha y el contenido se cargará en tu dispositivo.</li>
+</ol>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-es/help_start.html b/OpenPGP-Keychain/src/main/res/raw-es/help_start.html
new file mode 100644
index 000000000..d0eee8adb
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-es/help_start.html
@@ -0,0 +1,19 @@
+<html>
+<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>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>
+
+<h2>¡He encontrado un bug en OpenPGP Keychain!</h2>
+<p>Por favor, informa de errores usando el <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">seguimiento de incidencias de OpenPGP Keychain</a>.</p>
+
+<h2>Aportar</h2>
+<p>Si quieres ayudarnos con el desarrollo de OpenPGP Keychain aportando código <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">sigue nuestra pequeña guía en Github</a>.</p>
+
+<h2>Traducciones</h2>
+<p>¡Ayúdanos a traducir OpenPGP Keychain! Todo el mundo es bienvenido en <a href="https://www.transifex.com/projects/p/openpgp-keychain/">Transifex - OpenPGP Keychain</a>.</p>
+
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-es/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-es/nfc_beam_share.html
new file mode 100644
index 000000000..b6c2a2278
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-es/nfc_beam_share.html
@@ -0,0 +1,11 @@
+<html>
+<head></head>
+<body>
+<ol>
+<li>Asegúrate de que NFC está encendido en Ajustes &gt; Más &gt; NFC, y asegúrate de que Android Beam está también activado en ese mismo apartado.</li>
+<li>Mantén los dos dispositivos con ambos reversos juntos (deben estar casi en contacto) y notarás una vibración.</li>
+<li>Después de la vibración verás el contenido de tu dispositivo convertirse en una especie de ficha con una animación de Star Trek de fondo.</li>
+<li>Pulsa la ficha y el contenido será cargado en el dispositivo de la otra persona.</li>
+</ol>
+</body>
+</html>
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 6bede7bd6..0833c35d9 100644
--- a/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html
@@ -23,7 +23,9 @@
<h2>Bibliothèques</h2>
<ul>
<li>
-<a href="http://actionbarsherlock.com">ActionBarSherlock</a> (Licence Apache v2)</li>
+<a href="http://developer.android.com/tools/support-library/index.html">Bibliothèque de soutien Android v4</a> (Licence Apache v2)</li>
+<li>
+<a href="http://developer.android.com/tools/support-library/index.html">Bibliothèque de soutien Android v7 « appcompat »</a> (Licence Apache v2)</li>
<li>
<a href="https://github.com/emilsjolander/StickyListHeaders">StickyListHeaders</a> (Licence Apache v2)</li>
<li>
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 d85882a9b..c86c4a465 100644
--- a/OpenPGP-Keychain/src/main/res/raw-fr/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-fr/help_changelog.html
@@ -3,10 +3,14 @@
<body>
<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>
+<li>correctif de définition de la date date de péremption des clefs (merci à Ash Hughes)</li>
+<li>autres correctifs internes affectant la modifications des clefs (merci à Ash hughes)</li>
+<li>interrogation des serveurs de clefs directement depuis l'écran d'importation</li>
+<li>correctif de mise en page et du style des fenêtres de dialogue sur Android 2.2-3.0</li>
<li>corrige un plantage pour les clefs avec des ID utilisateur vides</li>
<li>corrige un plantage et des listes vides en revenant de l'écran de signature</li>
<li>Bouncy Castle (bibliothèque cryptographique) mise à jour de 1.47 à 1.50 et compilée depuis la source</li>
-<li>supprimer l'exportation non nécessaire des clefs publiques lors de l'exportation d'une clef secrète</li>
<li>correction du téléversement d'une clef depuis l'écran de signature</li>
</ul>
<h2>2.2</h2>
diff --git a/OpenPGP-Keychain/src/main/res/raw-fr/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-fr/help_nfc_beam.html
index 6d6e7d693..673e5b224 100644
--- a/OpenPGP-Keychain/src/main/res/raw-fr/help_nfc_beam.html
+++ b/OpenPGP-Keychain/src/main/res/raw-fr/help_nfc_beam.html
@@ -3,7 +3,7 @@
<body>
<h2>Comment recevoir des clefs</h2>
<ol>
-<li>Aller à la « Gestion des clefs publiques » de votre partenaire et appuyer longuement sur la clef que vous voulez partager.</li>
+<li>Allez aux contacts de votre partenaire et ouvrez le contact que vous voulez partager.</li>
<li>Tenir les deux appareils dos à dos (se touchant presque) et une vibration sera ressentie.</li>
<li>Après la vibration, le contenu de l'appareil de votre partenaire deviendra un objet en forme de carte avec une animation à la Star Trek en arrière-plan.</li>
<li>Toquer la carte et le contenu se chargera alors sur votre appareil.</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 bec69318b..4be071ec9 100644
--- a/OpenPGP-Keychain/src/main/res/raw-fr/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-fr/help_start.html
@@ -1,22 +1,19 @@
<html>
<head></head>
<body>
-<h2>Logiciel EXPÉRIMENTAL</h2>
-<p>Ce logiciel est EXPÉRIMENTAL. À utiliser à vos propres risques !</p>
-
<h2>Commencer</h2>
-<p>Il vous faut d'abord des clefs. Importez ou créez-les depuis le menu des options de « Mes clefs secrètes ».<!--<p>Install K-9 Mail for the best integration, it will supports OpenPGP Keychain for PGP/INLINE and lets you directly encrypt/decrypt emails.-->
-<br>Il est recommandé que vous installiez le Gestionnaire de fichiers OI afin de pouvoir utiliser le bouton Parcourir pour choisir des fichiers depuis le Porte-clefs OpenPGP.</p>
+<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>
-<h2>Les gros morceaux à faire</h2>
-<ul>
-<li>L'intégration à K-9 Mail n'est pas publiée</li>
-<li>L'importation de clefs existantes sera dépouillé de certificats pour l'instant</li>
-<li>PGP/MIME est manquant dans K-9 Mail</li>
-</ul>
-<p>Si vous voulez contribuer, bifurquer et faire une demande d'extraction sur Github : <a href="https://github.com/dschuermann/openpgp-keychain">https://github.com/dschuermann/openpgp-keychain</a></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>
<h2>J'ai trouvé un bogue dans le Porte-clefs OpenPGP !</h2>
-<p>Veuillez le rapporter avec le <a href="https://github.com/dschuermann/openpgp-keychain/issues">gestionnaire de bogues du Porte-clefs OpenPGP</a>.</p>
+<p>Veuillez rapporter le bogue en utilisant le <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">gestionnaire de bogues du Porte-clefs OpenPGP</a>.</p>
+
+<h2>Contribuer</h2>
+<p>Si vous voulez nous aider à développer le Porte-clefs OpenPGP en y contribuant par du code, <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">veuillez suivre notre petit guide sur Github</a>.</p>
+
+<h2>Traductions</h2>
+<p>Aidez-nous à traduire le Porte-clefs OpenPGP ! Tout le monde peut y participer sur la <a href="https://www.transifex.com/projects/p/openpgp-keychain/">page Transifex du Porte-clefs OpenPGP Keychain</a>.</p>
+
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-fr/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-fr/nfc_beam_share.html
index b2592fd8e..b63c9ac84 100644
--- a/OpenPGP-Keychain/src/main/res/raw-fr/nfc_beam_share.html
+++ b/OpenPGP-Keychain/src/main/res/raw-fr/nfc_beam_share.html
@@ -4,7 +4,7 @@
<ol>
<li>Assurez-vous que la NFC est activée dans Paramètres &gt; Paramètres supplémentaires &gt; NFC, ainsi que Android Beam. </li>
<li>Tenir les deux appareils dos à dos (se touchant presque) et une vibration sera ressentie.</li>
-<li>Après la vibration, le contenu de l'appareil de votre partenaire deviendra un objet en forme de carte avec une animation à la Star Trek en arrière-plan.</li>
+<li>Après la vibration, le contenu de votre appareil deviendra un objet en forme de carte avec une animation à la Star Trek en arrière-plan.</li>
<li>Toquer la carte et le contenu se chargera alors sur votre appareil.</li>
</ol>
</body>
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 7db2f83de..773d11fa7 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
@@ -23,7 +23,9 @@
<h2>Libraries</h2>
<ul>
<li>
-<a href="http://actionbarsherlock.com">ActionBarSherlock</a> (Apache License v2)</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>
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 32ad1a13d..abf660ba8 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
@@ -3,10 +3,14 @@
<body>
<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>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
<li>fix upload of key from signing screen</li>
</ul>
<h2>2.2</h2>
@@ -42,7 +46,7 @@
</ul>
<h2>1.0.8</h2>
<ul>
-<li>basic key server support</li>
+<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>
diff --git a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_nfc_beam.html
index 2e7e637e5..88492731c 100644
--- a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_nfc_beam.html
+++ b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_nfc_beam.html
@@ -3,7 +3,7 @@
<body>
<h2>How to receive keys</h2>
<ol>
-<li>Go to your partners 'Manage Public Keys' and long press on the key you want to share.</li>
+<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>
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 1386e8cc1..198dfe64f 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
@@ -1,22 +1,19 @@
<html>
<head></head>
<body>
-<h2>EXPERIMENTAL software</h2>
-<p>This is EXPERIMENTAL software. Use at your own risk!</p>
-
<h2>Getting started</h2>
-<p>First you need some keys. Import or create them via the option menus in "My Secret Keys".<!--<p>Install K-9 Mail for the best integration, it will supports OpenPGP Keychain for PGP/INLINE and lets you directly encrypt/decrypt emails.-->
-<br>It is recommended that you install OI File Manager to be able to use the browse button for file selection in OpenPGP Keychain.</p>
+<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>Big ToDos</h2>
-<ul>
-<li>K9 Mail integration not published</li>
-<li>Importing existing keys will be stripped of certificates right now</li>
-<li>PGP/MIME in K9 Mail is missing</li>
-</ul>
-<p>If you want to contribute, fork it and do a pull request on Github: <a href="https://github.com/dschuermann/openpgp-keychain">https://github.com/dschuermann/openpgp-keychain</a></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 OpenPGP Keychain!</h2>
-<p>Please report it in the <a href="https://github.com/dschuermann/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+
+<h2>Contribute</h2>
+<p>If you want to help us developing OpenPGP Keychain 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 OpenPGP Keychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenPGP Keychain on Transifex</a>.</p>
+
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-it-rIT/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-it-rIT/nfc_beam_share.html
index 453d435e3..083e055c7 100644
--- a/OpenPGP-Keychain/src/main/res/raw-it-rIT/nfc_beam_share.html
+++ b/OpenPGP-Keychain/src/main/res/raw-it-rIT/nfc_beam_share.html
@@ -3,8 +3,8 @@
<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>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>
diff --git a/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html b/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html
new file mode 100644
index 000000000..51e0b8c25
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html
@@ -0,0 +1,43 @@
+<html>
+<head></head>
+<body>
+<p><a href="http://sufficientlysecure.org/keychain">http://sufficientlysecure.org/keychain</a></p>
+<p><a href="http://sufficientlysecure.org/keychain">OpenPGP Keychain</a> は AndroidにおけるOpenPGP実装の一つです。開発はAndroid Privacy Guard (APG)から分岐して始まりました。</p>
+<p>ライセンス: GPLv3以降</p>
+
+<h2>OpenPGP Keychain開発者</h2>
+<ul>
+<li>Dominik Schürmann (主任開発者)</li>
+<li>Ash Hughes (暗号関係パッチ提供)</li>
+<li>Brian C. Barnes</li>
+<li>Bahtiar 'kalkin' Gadimov (UI)</li>
+
+</ul>
+<h2>APG 1.xの開発者達</h2>
+<ul>
+<li>'Thialfihar' (主任開発者)</li>
+<li>'Senecaso' (QRコード, 鍵署名, 鍵アップロード関係)</li>
+<li>Oliver Runge</li>
+<li>Markus Doits</li>
+</ul>
+<h2>ライブラリ</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>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
new file mode 100644
index 000000000..4878a3b55
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-ja/help_changelog.html
@@ -0,0 +1,108 @@
+<html>
+<head></head>
+<body>
+<h2>2.3</h2>
+<ul>
+<li>秘密鍵のエクスポート時における必要でない公開鍵のエクスポートの削除 (thanks to Ash Hughes)</li>
+<li>鍵の期限日時設定の修正 (thanks to Ash Hughes)</li>
+<li>鍵編集時のさらなる内部修正 (thanks to Ash Hughes)</li>
+<li>インポート画面から直接鍵サーバへ要求するようにした</li>
+<li>Android 2.2から3.0でのレイアウトとダイアログスタイルの修正</li>
+<li>空ユーザIDの鍵でのクラッシュ修正</li>
+<li>署名画面から戻ってきたときにリストが空だとクラッシュするのを修正</li>
+<li>Bouncy Castle (cryptography library) を1.47 から 1.50アップデートおよびソースからのビルド</li>
+<li>署名画面からの鍵のアップロード修正</li>
+</ul>
+<h2>2.2</h2>
+<ul>
+<li>ナビゲーションドロワーの新しいデザイン</li>
+<li>新しい公開鍵リストデザイン</li>
+<li>新しい公開鍵ビュー</li>
+<li>鍵のインポート時のバグ修正</li>
+<li>鍵のクロス証明 (thanks to Ash Hughes)</li>
+<li>適切な UTF-8 パスワード処理 (thanks to Ash Hughes)</li>
+<li>新しい言語での最初のバージョン (thanks to the contributors on Transifex)</li>
+<li>QRコードによる鍵共有の修正と改善</li>
+<li>APIでのパッケージ署名検証</li>
+</ul>
+<h2>2.1.1</h2>
+<ul>
+<li>APIアップデート、K-9 Mail統合準備</li>
+</ul>
+<h2>2.1</h2>
+<ul>
+<li>たくさんのバグ修正</li>
+<li>開発者向け新API</li>
+<li>Googleによる擬似乱数生成器バグの修正</li>
+</ul>
+<h2>2.0</h2>
+<ul>
+<li>再デザイン完了</li>
+<li>QRコード、NFCビームでの公開鍵共有</li>
+<li>鍵への署名</li>
+<li>鍵サーバへアップロード</li>
+<li>インポート問題修正</li>
+<li>新しいAIDL API</li>
+</ul>
+<h2>1.0.8</h2>
+<ul>
+<li>鍵サーバの基本サポート</li>
+<li>App2SD</li>
+<li>パスフレーズのキャッシュ時間について1,2,4,8時間の選択肢追加</li>
+<li>翻訳: ノルウェー語 (thanks, Sander Danielsen), 中国語 (thanks, Zhang Fredrick) 追加</li>
+<li>バグ修正</li>
+<li>最適化</li>
+</ul>
+<h2>1.0.7</h2>
+<ul>
+<li>改行を含まないテキストの署名検証問題の修正</li>
+<li>パスフレーズのキャッシュ時間 (20,40,60分) のオプション追加</li>
+</ul>
+<h2>1.0.6</h2>
+<ul>
+<li>Froyo でのアカウント追加時クラッシュの修正</li>
+<li>セキュアファイル削除</li>
+<li>鍵ファイルインポート後の削除オプション</li>
+<li>ストリーム暗号化/復号化 (ギャラリーなど)</li>
+<li>新しいオプション (言語、強制V3署名)</li>
+<li>インタフェース変更</li>
+<li>バグ修正</li>
+</ul>
+<h2>1.0.5</h2>
+<ul>
+<li>ドイツ語およびイタリア語翻訳追加</li>
+<li>BCソースが重複してしまっていたことによる、より小さいパッケージ化</li>
+<li>新しい設定GUI</li>
+<li>ローカライズでのレイアウトを適合化</li>
+<li>署名バグ修正</li>
+</ul>
+<h2>1.0.4</h2>
+<ul>
+<li>クエリービルダーによるSDKのいくつかのバグによるクラッシュの修正</li>
+</ul>
+<h2>1.0.3</h2>
+<ul>
+<li>鍵エクスポートできる時と暗号化/署名中のクラッシュ修正</li>
+</ul>
+<h2>1.0.2</h2>
+<ul>
+<li>鍵リストのフィルタ可能化</li>
+<li>暗号化鍵の事前選択のよりスマートな実装</li>
+<li>VIEWおよびSENDについて新しいインテントのハンドリング、ファイルマネージャ外のファイルを暗号化/復号化するのを受け付けるようになる。</li>
+<li>K-9 Mailにおける修正と追加機能 (鍵事前選択)、新しいベータビルド提供</li>
+</ul>
+<h2>1.0.1</h2>
+<ul>
+<li>1.0.0で壊れたGmailアカウントリストアップを再度修正</li>
+</ul>
+<h2>1.0.0</h2>
+<ul>
+<li>K-9 Mail 統合、K-9 MailでのAPG サポートのベータビルド</li>
+<li>(ASTROを含む)ファイルマネージャのさらなるサポート</li>
+<li>スロベニア語翻訳追加</li>
+<li>より早くてメモリ使用量の少ない新しいデータベース</li>
+<li>他のアプリでのインテントおよびコンテンツプロバイダの定義</li>
+<li>バグ修正</li>
+</ul>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-ja/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-ja/help_nfc_beam.html
new file mode 100644
index 000000000..c19280fd1
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-ja/help_nfc_beam.html
@@ -0,0 +1,12 @@
+<html>
+<head></head>
+<body>
+<h2>鍵の受信方法</h2>
+<ol>
+<li>パートナーの連絡先に行き、共有したい連絡先を開きます。</li>
+<li>2つのデバイスを背中合せ(ほとんどすべてのタッチ方法)にしてバイブを感じるまで保持しておいてください。</li>
+<li>バイブの後、相手のデバイスでスタートレック風のバックグラウンドアニメーションしているカード風のコンテンツを見ると思います。</li>
+<li>カードをタップしコンテンツをあなたのデバイスにロードします。</li>
+</ol>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-ja/help_start.html b/OpenPGP-Keychain/src/main/res/raw-ja/help_start.html
new file mode 100644
index 000000000..5c78a1273
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-ja/help_start.html
@@ -0,0 +1,19 @@
+<html>
+<head></head>
+<body>
+<h2>入門</h2>
+<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>
+
+<h2>OpenPGP Keychainにバグを発見した!</h2>
+<p><a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">OpenPGP KeychainのIssueトラッカー</a>を使ってバグレポートを送ってください。</p>
+
+<h2>寄贈</h2>
+<p>もし、あなたが OpenPGP Keychain の開発を助け、コードを寄贈するというなら、<a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">Githubの寄贈ガイドを確認</a>して下さい。</p>
+
+<h2>翻訳</h2>
+<p>OpenPGP Keychainの翻訳を補助してください! だれでも、<a href="https://www.transifex.com/projects/p/openpgp-keychain/">TransifexでのOpenPGP Keychainプロジェクト</a>に参加できます。</p>
+
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-ja/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-ja/nfc_beam_share.html
new file mode 100644
index 000000000..422423a5d
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-ja/nfc_beam_share.html
@@ -0,0 +1,11 @@
+<html>
+<head></head>
+<body>
+<ol>
+<li>設定 &gt; その他 &gt; NFC からNFCを有効にしてください、そしてAndroid Beamもまた選択してください。</li>
+<li>2つのデバイスを背中合せ(ほとんどすべてのタッチ方法)にしてバイブを感じるまで保持しておいてください。</li>
+<li>バイブの後、相手のデバイスでスタートレック風のバックグラウンドアニメーションしているカード風のコンテンツを見ると思います。</li>
+<li>カードをタップしコンテンツを他のデバイスに読み込ませてください。</li>
+</ol>
+</body>
+</html>
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 7db2f83de..773d11fa7 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
@@ -23,7 +23,9 @@
<h2>Libraries</h2>
<ul>
<li>
-<a href="http://actionbarsherlock.com">ActionBarSherlock</a> (Apache License v2)</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>
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 32ad1a13d..abf660ba8 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
@@ -3,10 +3,14 @@
<body>
<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>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
<li>fix upload of key from signing screen</li>
</ul>
<h2>2.2</h2>
@@ -42,7 +46,7 @@
</ul>
<h2>1.0.8</h2>
<ul>
-<li>basic key server support</li>
+<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>
diff --git a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_nfc_beam.html
index 2e7e637e5..88492731c 100644
--- a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_nfc_beam.html
+++ b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_nfc_beam.html
@@ -3,7 +3,7 @@
<body>
<h2>How to receive keys</h2>
<ol>
-<li>Go to your partners 'Manage Public Keys' and long press on the key you want to share.</li>
+<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>
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 1386e8cc1..198dfe64f 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
@@ -1,22 +1,19 @@
<html>
<head></head>
<body>
-<h2>EXPERIMENTAL software</h2>
-<p>This is EXPERIMENTAL software. Use at your own risk!</p>
-
<h2>Getting started</h2>
-<p>First you need some keys. Import or create them via the option menus in "My Secret Keys".<!--<p>Install K-9 Mail for the best integration, it will supports OpenPGP Keychain for PGP/INLINE and lets you directly encrypt/decrypt emails.-->
-<br>It is recommended that you install OI File Manager to be able to use the browse button for file selection in OpenPGP Keychain.</p>
+<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>Big ToDos</h2>
-<ul>
-<li>K9 Mail integration not published</li>
-<li>Importing existing keys will be stripped of certificates right now</li>
-<li>PGP/MIME in K9 Mail is missing</li>
-</ul>
-<p>If you want to contribute, fork it and do a pull request on Github: <a href="https://github.com/dschuermann/openpgp-keychain">https://github.com/dschuermann/openpgp-keychain</a></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 OpenPGP Keychain!</h2>
-<p>Please report it in the <a href="https://github.com/dschuermann/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+
+<h2>Contribute</h2>
+<p>If you want to help us developing OpenPGP Keychain 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 OpenPGP Keychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenPGP Keychain on Transifex</a>.</p>
+
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/nfc_beam_share.html
index 453d435e3..083e055c7 100644
--- a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/nfc_beam_share.html
+++ b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/nfc_beam_share.html
@@ -3,8 +3,8 @@
<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>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>
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 7db2f83de..773d11fa7 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
@@ -23,7 +23,9 @@
<h2>Libraries</h2>
<ul>
<li>
-<a href="http://actionbarsherlock.com">ActionBarSherlock</a> (Apache License v2)</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>
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 32ad1a13d..abf660ba8 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
@@ -3,10 +3,14 @@
<body>
<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>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
<li>fix upload of key from signing screen</li>
</ul>
<h2>2.2</h2>
@@ -42,7 +46,7 @@
</ul>
<h2>1.0.8</h2>
<ul>
-<li>basic key server support</li>
+<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>
diff --git a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_nfc_beam.html
index 2e7e637e5..88492731c 100644
--- a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_nfc_beam.html
+++ b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_nfc_beam.html
@@ -3,7 +3,7 @@
<body>
<h2>How to receive keys</h2>
<ol>
-<li>Go to your partners 'Manage Public Keys' and long press on the key you want to share.</li>
+<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>
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 1386e8cc1..198dfe64f 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
@@ -1,22 +1,19 @@
<html>
<head></head>
<body>
-<h2>EXPERIMENTAL software</h2>
-<p>This is EXPERIMENTAL software. Use at your own risk!</p>
-
<h2>Getting started</h2>
-<p>First you need some keys. Import or create them via the option menus in "My Secret Keys".<!--<p>Install K-9 Mail for the best integration, it will supports OpenPGP Keychain for PGP/INLINE and lets you directly encrypt/decrypt emails.-->
-<br>It is recommended that you install OI File Manager to be able to use the browse button for file selection in OpenPGP Keychain.</p>
+<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>Big ToDos</h2>
-<ul>
-<li>K9 Mail integration not published</li>
-<li>Importing existing keys will be stripped of certificates right now</li>
-<li>PGP/MIME in K9 Mail is missing</li>
-</ul>
-<p>If you want to contribute, fork it and do a pull request on Github: <a href="https://github.com/dschuermann/openpgp-keychain">https://github.com/dschuermann/openpgp-keychain</a></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 OpenPGP Keychain!</h2>
-<p>Please report it in the <a href="https://github.com/dschuermann/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+
+<h2>Contribute</h2>
+<p>If you want to help us developing OpenPGP Keychain 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 OpenPGP Keychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenPGP Keychain on Transifex</a>.</p>
+
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/nfc_beam_share.html
index 453d435e3..083e055c7 100644
--- a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/nfc_beam_share.html
+++ b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/nfc_beam_share.html
@@ -3,8 +3,8 @@
<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>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>
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 566029511..b11aaab35 100644
--- a/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html
@@ -23,7 +23,9 @@
<h2>Компоненты</h2>
<ul>
<li>
-<a href="http://actionbarsherlock.com">ActionBarSherlock</a> (Apache License v2)</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>
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 d923de570..2a324202f 100644
--- a/OpenPGP-Keychain/src/main/res/raw-ru/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-ru/help_changelog.html
@@ -3,10 +3,14 @@
<body>
<h2>2.3</h2>
<ul>
+<li>удален не требующийся экспорт публичного ключа при экспорте секретного ключа (спасибо, Ash Hughes)</li>
+<li>исправлена ошибка срока годности ключей (спасибо, Ash Hughes)</li>
+<li>исправления ошибок при изменении ключей (спасибо, Ash Hughes)</li>
+<li>запрос ключа с сервера прямо из окна импорта ключей</li>
+<li>исправление внешнего вида для Android 2.2-3.0</li>
<li>исправлено падение когда ключ не содержал имя пользователя</li>
<li>исправлено падение и пустой список при возвращении из окна подписания</li>
<li>криптографическая библиотека Bouncy Castle обновлена до версии 1.50</li>
-<li>удален не требующийся экспорт публичного ключа при экспорте секретного ключа (спасибо, Ash Hughes)</li>
<li>исправлена загрузка ключа из окна подписания</li>
</ul>
<h2>2.2</h2>
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 d55f3296b..99e6f263d 100644
--- a/OpenPGP-Keychain/src/main/res/raw-ru/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-ru/help_start.html
@@ -1,22 +1,19 @@
<html>
<head></head>
<body>
-<h2>ЭКСПЕРИМЕНТАЛЬНАЯ программа</h2>
-<p>Это ЭКСПЕРИМЕНТАЛЬНАЯ версия. Используйте на свой страх и риск!</p>
-
<h2>Приступая</h2>
-<p>Для начала Вам потребуются ключи. Воспользуйтесь функцией Импорт в меню раздела "Мои ключи".<!--<p>Install K-9 Mail for the best integration, it will supports OpenPGP Keychain for PGP/INLINE and lets you directly encrypt/decrypt emails.-->
-<br>Для удобства выбора файлов рекомендуется установить OI File Manager. Он будет вызываться при нажатии кнопки выбора файла.</p>
+<p>Для начала вам понадобится своя пара ключей. Воспользуйтесь меню в разделе "Мои ключи", что бы создать новую, или добавьте ранее созданную пару в разделе "Импорт ключей". После этого вы сможете скачать ключи ваших друзей или обменяться ключами посредством QR кодов или NFC.</p>
-<h2>Над чем еще ведётся работа</h2>
-<ul>
-<li>Интеграция с почтовой программой K9 Mail.</li>
-<li>При импорте сущестующих ключей теряются подписи</li>
-<li>Отсутсвует поддержка PGP/MIME в K9 Mail</li>
-</ul>
-<p>Если вы ходите принять участие в доработке и развитии проекта, посетите страницу проекта на Github: <a href="https://github.com/dschuermann/openpgp-keychain">https://github.com/dschuermann/openpgp-keychain</a></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>
<h2>Я нашел ошибку в OpenPGP Keychain!</h2>
-<p>Пожалуйста, сообщите о ней в разделе '<a href="https://github.com/dschuermann/openpgp-keychain/issues">Проблемы с OpenPGP Keychain</a>'.</p>
+<p>Пожалуйста, сообщайте о возникших проблемах и найденных ошибках в разделе <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">Решение проблем OpenPGP Keychain</a>.</p>
+
+<h2>Вклад в развитие</h2>
+<p>Если Вы хотите помочь в разработке OpenPGP Keychain, обратитесь к <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">инструкции на Github</a>.</p>
+
+<h2>Перевод</h2>
+<p>Помогите переводить OpenPGP Keychain! Каждый может принять участие в переводе <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenPGP Keychain на Transifex</a>.</p>
+
</body>
</html>
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 7db2f83de..773d11fa7 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
@@ -23,7 +23,9 @@
<h2>Libraries</h2>
<ul>
<li>
-<a href="http://actionbarsherlock.com">ActionBarSherlock</a> (Apache License v2)</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>
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 32ad1a13d..abf660ba8 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
@@ -3,10 +3,14 @@
<body>
<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>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
<li>fix upload of key from signing screen</li>
</ul>
<h2>2.2</h2>
@@ -42,7 +46,7 @@
</ul>
<h2>1.0.8</h2>
<ul>
-<li>basic key server support</li>
+<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>
diff --git a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_nfc_beam.html
index 2e7e637e5..88492731c 100644
--- a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_nfc_beam.html
+++ b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_nfc_beam.html
@@ -3,7 +3,7 @@
<body>
<h2>How to receive keys</h2>
<ol>
-<li>Go to your partners 'Manage Public Keys' and long press on the key you want to share.</li>
+<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>
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 1386e8cc1..198dfe64f 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
@@ -1,22 +1,19 @@
<html>
<head></head>
<body>
-<h2>EXPERIMENTAL software</h2>
-<p>This is EXPERIMENTAL software. Use at your own risk!</p>
-
<h2>Getting started</h2>
-<p>First you need some keys. Import or create them via the option menus in "My Secret Keys".<!--<p>Install K-9 Mail for the best integration, it will supports OpenPGP Keychain for PGP/INLINE and lets you directly encrypt/decrypt emails.-->
-<br>It is recommended that you install OI File Manager to be able to use the browse button for file selection in OpenPGP Keychain.</p>
+<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>Big ToDos</h2>
-<ul>
-<li>K9 Mail integration not published</li>
-<li>Importing existing keys will be stripped of certificates right now</li>
-<li>PGP/MIME in K9 Mail is missing</li>
-</ul>
-<p>If you want to contribute, fork it and do a pull request on Github: <a href="https://github.com/dschuermann/openpgp-keychain">https://github.com/dschuermann/openpgp-keychain</a></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 OpenPGP Keychain!</h2>
-<p>Please report it in the <a href="https://github.com/dschuermann/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+
+<h2>Contribute</h2>
+<p>If you want to help us developing OpenPGP Keychain 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 OpenPGP Keychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenPGP Keychain on Transifex</a>.</p>
+
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/nfc_beam_share.html
index 453d435e3..083e055c7 100644
--- a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/nfc_beam_share.html
+++ b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/nfc_beam_share.html
@@ -3,8 +3,8 @@
<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>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>
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 3d4549cbd..3a95c8c16 100644
--- a/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html
@@ -23,7 +23,9 @@
<h2>Kütüphaneler</h2>
<ul>
<li>
-<a href="http://actionbarsherlock.com">ActionBarSherlock</a> (Apache License v2)</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>
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 32ad1a13d..abf660ba8 100644
--- a/OpenPGP-Keychain/src/main/res/raw-tr/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-tr/help_changelog.html
@@ -3,10 +3,14 @@
<body>
<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>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
<li>fix upload of key from signing screen</li>
</ul>
<h2>2.2</h2>
@@ -42,7 +46,7 @@
</ul>
<h2>1.0.8</h2>
<ul>
-<li>basic key server support</li>
+<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>
diff --git a/OpenPGP-Keychain/src/main/res/raw-tr/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-tr/help_nfc_beam.html
index 2e7e637e5..88492731c 100644
--- a/OpenPGP-Keychain/src/main/res/raw-tr/help_nfc_beam.html
+++ b/OpenPGP-Keychain/src/main/res/raw-tr/help_nfc_beam.html
@@ -3,7 +3,7 @@
<body>
<h2>How to receive keys</h2>
<ol>
-<li>Go to your partners 'Manage Public Keys' and long press on the key you want to share.</li>
+<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>
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 1386e8cc1..198dfe64f 100644
--- a/OpenPGP-Keychain/src/main/res/raw-tr/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-tr/help_start.html
@@ -1,22 +1,19 @@
<html>
<head></head>
<body>
-<h2>EXPERIMENTAL software</h2>
-<p>This is EXPERIMENTAL software. Use at your own risk!</p>
-
<h2>Getting started</h2>
-<p>First you need some keys. Import or create them via the option menus in "My Secret Keys".<!--<p>Install K-9 Mail for the best integration, it will supports OpenPGP Keychain for PGP/INLINE and lets you directly encrypt/decrypt emails.-->
-<br>It is recommended that you install OI File Manager to be able to use the browse button for file selection in OpenPGP Keychain.</p>
+<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>Big ToDos</h2>
-<ul>
-<li>K9 Mail integration not published</li>
-<li>Importing existing keys will be stripped of certificates right now</li>
-<li>PGP/MIME in K9 Mail is missing</li>
-</ul>
-<p>If you want to contribute, fork it and do a pull request on Github: <a href="https://github.com/dschuermann/openpgp-keychain">https://github.com/dschuermann/openpgp-keychain</a></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 OpenPGP Keychain!</h2>
-<p>Please report it in the <a href="https://github.com/dschuermann/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+
+<h2>Contribute</h2>
+<p>If you want to help us developing OpenPGP Keychain 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 OpenPGP Keychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenPGP Keychain on Transifex</a>.</p>
+
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-tr/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-tr/nfc_beam_share.html
index 453d435e3..083e055c7 100644
--- a/OpenPGP-Keychain/src/main/res/raw-tr/nfc_beam_share.html
+++ b/OpenPGP-Keychain/src/main/res/raw-tr/nfc_beam_share.html
@@ -3,8 +3,8 @@
<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>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>
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 7b6f22e94..c6c2e1eed 100644
--- a/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html
@@ -23,7 +23,9 @@
<h2>Бібліотеки</h2>
<ul>
<li>
-<a href="http://actionbarsherlock.com">ActionBarSherlock</a> (ліцензія Apache в.2)</li>
+<a href="http://developer.android.com/tools/support-library/index.html">Бібліотека підтримки Android в.4</a> (Ліцензія Apache в. 2)</li>
+<li>
+<a href="http://developer.android.com/tools/support-library/index.html">Бібліотека підтримки Android в.7 'appcompat'</a> (Ліцензія Apache в.2)</li>
<li>
<a href="https://github.com/emilsjolander/StickyListHeaders">StickyListHeaders</a> (ліцензія Apache в. 2)</li>
<li>
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 174a2b0d4..0b67fa3a9 100644
--- a/OpenPGP-Keychain/src/main/res/raw-uk/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-uk/help_changelog.html
@@ -3,10 +3,14 @@
<body>
<h2>2.3</h2>
<ul>
+<li>видалений непотрібний експорт публічного ключа при експорті секретного ключа (завдяки Ash Hughes)</li>
+<li>виправлено налаштування дат дії ключів (завдяки Ash Hughes)</li>
+<li>більше внутрішніх виправлень при редагуванні ключів (завдяки Ash Hughes)</li>
+<li>сервери запитаного ключа безпосередньо з екрану імпорту</li>
+<li>виправлено стиль розмітки і діалогу у Андроїд 2.2-3.0</li>
<li>виправлено збої, коли ключ мав порожній ідентифікатор користувача</li>
<li>виправлено збої та порожні списки при поверненні з екрану реєстрації</li>
<li>Bouncy Castle (криптографічна бібліотека) оновлена з версії 1.47 до 1.50 та зібрана з коду</li>
-<li>видалений непотрібний експорт публічного ключа при експорті секретного ключа (завдяки Ash Hughes)</li>
<li>виправлено завантаження ключа з вікна реєстрації</li>
</ul>
<h2>2.2</h2>
diff --git a/OpenPGP-Keychain/src/main/res/raw-uk/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-uk/help_nfc_beam.html
index 4a91477d4..dc34048d3 100644
--- a/OpenPGP-Keychain/src/main/res/raw-uk/help_nfc_beam.html
+++ b/OpenPGP-Keychain/src/main/res/raw-uk/help_nfc_beam.html
@@ -3,7 +3,7 @@
<body>
<h2>Як обмінятися ключами</h2>
<ol>
-<li>Натисніть і утримуйте ключ, який ви хочете передати.</li>
+<li>Перейдіть до контактних даних ваших партнерів і відкрийте контакт, який ви хочете надіслати.</li>
<li>Піднесіть обидва пристрої впритул зворотними сторонами (до повного торкання). Ви відчуєте невелику вібрацію.</li>
<li>Після вібрації пристроїв на екрані з'явиться картка з передаваним вмістом.</li>
<li>Натисніть на картку, що б передати дані з одного пристрою на інший.</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 c2b5a8019..520224815 100644
--- a/OpenPGP-Keychain/src/main/res/raw-uk/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-uk/help_start.html
@@ -1,22 +1,19 @@
<html>
<head></head>
<body>
-<h2>ЕКСПЕРИМЕНТАЛЬНА програма</h2>
-<p>Це ЕКСПЕРИМЕНТАЛЬНА версія. Використовуйте на свій страх і ризик!</p>
-
<h2>Приступаючи до роботи</h2>
-<p>Для початку Вам будуть потрібні ключі. Скористайтеся функцією Імпорт в меню розділу "Мої ключі".<!--<p>Install K-9 Mail for the best integration, it will supports OpenPGP Keychain for PGP/INLINE and lets you directly encrypt/decrypt emails.-->
-<br>Для зручності вибору файлів рекомендується встановити OI File Manager. Він буде викликатися при натисканні кнопки вибору файлу.</p>
+<p>Спершу вам потрібна персональна в'язка ключів. Створіть одну через меню параметрів у "Мої Ключі" або імпортуйте наявні в'язки ключів через "Імпорт ключів". Після цього ви зможете завантажувати ключі ваших друзів чи обміняти їх через штрих-коди або NFC.</p>
-<h2>Над чим ще ведеться робота</h2>
-<ul>
-<li>Інтеграція з поштовою програмою K9 Mail.</li>
-<li>При імпорті існуючих ключів губляться підписи</li>
-<li>Відсутня підтримка PGP/MIME K9 Mail</li>
-</ul>
-<p>Якщо ви ходите взяти участь у доопрацюванні та розвитку проекту, відвідайте сторінку проекту на Github: <a href="https://github.com/dschuermann/openpgp-keychain">https://github.com/dschuermann/openpgp-брелок</a></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>
<h2>Я знайшов помилку в OpenPGP Keychain!</h2>
-<p>Будь ласка, повідомте про неї в розділі '<a href="https://github.com/dschuermann/openpgp-keychain/issues">Проблеми з В'язкою ключів OpenPGP</a>'.</p>
+<p>Будь ласка, повідомте про ваду за допомогою <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">відстежувача проблем OpenPGP Keychain</a>.</p>
+
+<h2>Внесок</h2>
+<p>Якщо ви хочете допомогти нам у розробці OpenPGP Keychain через редагування коду програми <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">підпишіться на наш невеличкий посібник у Github</a>.</p>
+
+<h2>Переклади</h2>
+<p>Допоможіть перекласти OpenPGP Keychain! Кожний може взяти участь у <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenPGP Keychain на Transifex</a>.</p>
+
</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 7db2f83de..773d11fa7 100644
--- a/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html
@@ -23,7 +23,9 @@
<h2>Libraries</h2>
<ul>
<li>
-<a href="http://actionbarsherlock.com">ActionBarSherlock</a> (Apache License v2)</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>
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 32ad1a13d..abf660ba8 100644
--- a/OpenPGP-Keychain/src/main/res/raw-zh/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-zh/help_changelog.html
@@ -3,10 +3,14 @@
<body>
<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>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
<li>fix upload of key from signing screen</li>
</ul>
<h2>2.2</h2>
@@ -42,7 +46,7 @@
</ul>
<h2>1.0.8</h2>
<ul>
-<li>basic key server support</li>
+<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>
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 2e7e637e5..88492731c 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
@@ -3,7 +3,7 @@
<body>
<h2>How to receive keys</h2>
<ol>
-<li>Go to your partners 'Manage Public Keys' and long press on the key you want to share.</li>
+<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>
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 1386e8cc1..198dfe64f 100644
--- a/OpenPGP-Keychain/src/main/res/raw-zh/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-zh/help_start.html
@@ -1,22 +1,19 @@
<html>
<head></head>
<body>
-<h2>EXPERIMENTAL software</h2>
-<p>This is EXPERIMENTAL software. Use at your own risk!</p>
-
<h2>Getting started</h2>
-<p>First you need some keys. Import or create them via the option menus in "My Secret Keys".<!--<p>Install K-9 Mail for the best integration, it will supports OpenPGP Keychain for PGP/INLINE and lets you directly encrypt/decrypt emails.-->
-<br>It is recommended that you install OI File Manager to be able to use the browse button for file selection in OpenPGP Keychain.</p>
+<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>Big ToDos</h2>
-<ul>
-<li>K9 Mail integration not published</li>
-<li>Importing existing keys will be stripped of certificates right now</li>
-<li>PGP/MIME in K9 Mail is missing</li>
-</ul>
-<p>If you want to contribute, fork it and do a pull request on Github: <a href="https://github.com/dschuermann/openpgp-keychain">https://github.com/dschuermann/openpgp-keychain</a></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 OpenPGP Keychain!</h2>
-<p>Please report it in the <a href="https://github.com/dschuermann/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
+
+<h2>Contribute</h2>
+<p>If you want to help us developing OpenPGP Keychain 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 OpenPGP Keychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenPGP Keychain on Transifex</a>.</p>
+
</body>
</html>
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 453d435e3..083e055c7 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
@@ -3,8 +3,8 @@
<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>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>
diff --git a/OpenPGP-Keychain/src/main/res/raw/help_about.html b/OpenPGP-Keychain/src/main/res/raw/help_about.html
index 85130965c..51e3f1325 100644
--- a/OpenPGP-Keychain/src/main/res/raw/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw/help_about.html
@@ -5,11 +5,11 @@ And don't add newlines before or after p tags because of transifex -->
<head>
</head>
<body>
-<p><a href="http://sufficientlysecure.org/keychain">http://sufficientlysecure.org/keychain</a></p>
-<p><a href="http://sufficientlysecure.org/keychain">OpenPGP Keychain</a> is an OpenPGP implementation for Android. The development began as a fork of Android Privacy Guard (APG).</p>
+<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 OpenPGP Keychain</h2>
+<h2>Developers OpenKeychain</h2>
<ul>
<li>Dominik Schürmann (Lead developer)</li>
<li>Ash Hughes (crypto patches)</li>
@@ -34,8 +34,9 @@ And don't add newlines before or after p tags because of transifex -->
<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>
<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> \ No newline at end of file
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw/help_changelog.html
index 7bb558a24..17ad853de 100644
--- a/OpenPGP-Keychain/src/main/res/raw/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw/help_changelog.html
@@ -10,7 +10,7 @@ And don't add newlines before or after p tags because of transifex -->
<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 key servers directly from the import screen</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>
@@ -55,7 +55,7 @@ And don't add newlines before or after p tags because of transifex -->
<h2>1.0.8</h2>
<ul>
-<li>basic key server support</li>
+<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>
diff --git a/OpenPGP-Keychain/src/main/res/raw/help_start.html b/OpenPGP-Keychain/src/main/res/raw/help_start.html
index 097e22ba8..6c8c49846 100644
--- a/OpenPGP-Keychain/src/main/res/raw/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw/help_start.html
@@ -5,23 +5,19 @@ And don't add newlines before or after p tags because of transifex -->
<head>
</head>
<body>
-<h2>EXPERIMENTAL software</h2>
-<p>This is EXPERIMENTAL software. Use at your own risk!</p>
-
<h2>Getting started</h2>
-<p>First you need some keys. Import or create them via the option menus in "My Secret Keys".
-<!--<p>Install K-9 Mail for the best integration, it will supports OpenPGP Keychain for PGP/INLINE and lets you directly encrypt/decrypt emails.-->
-<br/>It is recommended that you install OI File Manager to be able to use the browse button for file selection in OpenPGP Keychain.</p>
+<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>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>Big ToDos</h2>
-<ul>
-<li>K9 Mail integration not published</li>
-<li>Importing existing keys will be stripped of certificates right now</li>
-<li>PGP/MIME in K9 Mail is missing</li>
-</ul>
-<p>If you want to contribute, fork it and do a pull request on Github: <a href="https://github.com/dschuermann/openpgp-keychain">https://github.com/dschuermann/openpgp-keychain</a></p>
+<h2>Translations</h2>
+<p>Help translating OpenKeychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenPGP Keychain on Transifex</a>.</p>
-<h2>I found a bug in OpenPGP Keychain!</h2>
-<p>Please report it in the <a href="https://github.com/dschuermann/openpgp-keychain/issues">issue tracker of OpenPGP Keychain</a>.</p>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/values-de/strings.xml b/OpenPGP-Keychain/src/main/res/values-de/strings.xml
index ebf303f8a..d421fd141 100644
--- a/OpenPGP-Keychain/src/main/res/values-de/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-de/strings.xml
@@ -12,7 +12,7 @@
<string name="title_edit_key">Schlüssel bearbeiten</string>
<string name="title_preferences">Einstellungen</string>
<string name="title_api_registered_apps">Registrierte Anwendungen</string>
- <string name="title_key_server_preference">Einstellung der Schlüsselserver</string>
+ <string name="title_key_server_preference">Schlüsselserver</string>
<string name="title_change_pass_phrase">Passwort ändern</string>
<string name="title_set_passphrase">Passwort setzen</string>
<string name="title_send_email">E-Mail senden...</string>
@@ -22,10 +22,10 @@
<string name="title_export_key">Schlüssel exportieren</string>
<string name="title_export_keys">Schlüssel exportieren</string>
<string name="title_key_not_found">Schlüssel nicht gefunden</string>
- <string name="title_key_server_query">Schlüssel-Server abfragen</string>
+ <string name="title_key_server_query">Schlüsselserver abfragen</string>
<string name="title_send_key">Auf Schlüsselserver hochladen</string>
<string name="title_unknown_signature_key">Unbekannter Signaturschlüssel</string>
- <string name="title_sign_key">Schlüssel signieren</string>
+ <string name="title_certify_key">Schlüssel beglaubigen</string>
<string name="title_key_details">Schlüsseldetails</string>
<string name="title_help">Hilfe</string>
<!--section-->
@@ -37,17 +37,16 @@
<string name="section_master_key">Hauptschlüssel</string>
<string name="section_master_user_id">Hauptbenutzer-ID</string>
<string name="section_actions">Aktionen</string>
- <string name="section_signing_key">Dein Signaturschlüssel</string>
+ <string name="section_certification_key">Mit diesem Schlüssel beglaubigen</string>
<string name="section_upload_key">Schlüssel hochladen</string>
<string name="section_key_server">Schlüsselserver</string>
+ <string name="section_encrypt_and_or_sign">Verschlüsseln und/oder Signieren</string>
+ <string name="section_decrypt_verify">Entschlüsseln und Verifizieren</string>
<!--button-->
- <string name="btn_sign_to_clipboard">Signieren (Zwischenablage)</string>
- <string name="btn_encrypt_to_clipboard">In die Zwischenablage verschlüsseln</string>
- <string name="btn_encrypt_and_send">Verschlüsseln und senden...</string>
- <string name="btn_sign_and_send">Signieren und senden...</string>
<string name="btn_sign">Signieren</string>
+ <string name="btn_certify">Beglaubigen</string>
<string name="btn_decrypt">Entschlüsseln</string>
- <string name="btn_verify">Signatur prüfen</string>
+ <string name="btn_decrypt_verify">Entschlüsseln und Verifizieren</string>
<string name="btn_select_encrypt_keys">Empfänger auswählen</string>
<string name="btn_encrypt_file">Datei verschlüsseln</string>
<string name="btn_save">Speichern</string>
@@ -61,6 +60,9 @@
<string name="btn_export_to_server">Auf Schlüsselserver hochladen</string>
<string name="btn_next">Weiter</string>
<string name="btn_back">Zurück</string>
+ <string name="btn_clipboard">Zwischenablage</string>
+ <string name="btn_share">Teilen mit…</string>
+ <string name="btn_lookup_key">Schlüssel nachschlagen</string>
<!--menu-->
<string name="menu_preferences">Einstellungen</string>
<string name="menu_help">Hilfe</string>
@@ -99,6 +101,7 @@
<string name="label_passphrase_again">Wiederholen</string>
<string name="label_algorithm">Algorithmus</string>
<string name="label_ascii_armor">ASCII-Armor</string>
+ <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_encryption_algorithm">Verschlüsselungsalgorithmus</string>
@@ -108,7 +111,6 @@
<string name="label_passphrase_cache_ttl">Passwort-Cache</string>
<string name="label_message_compression">Nachrichten-Komprimierung</string>
<string name="label_file_compression">Datei-Komprimierung</string>
- <string name="label_force_v3_signature">OpenPGPv3-Signaturen erzwingen</string>
<string name="label_key_servers">Schlüsselserver</string>
<string name="label_key_id">Schlüssel-ID</string>
<string name="label_creation">Erstellungsdatum</string>
@@ -119,19 +121,21 @@
<string name="label_name">Name</string>
<string name="label_comment">Kommentar</string>
<string name="label_email">E-Mail</string>
- <string name="label_send_key">Lade Schlüssel nach dem Signieren auf den ausgewählten Schlüsselserver hoch</string>
+ <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>
<plurals name="select_keys_button">
<item quantity="one">%d ausgewählt</item>
<item quantity="other">%d ausgewählt</item>
</plurals>
+ <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="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>
<plurals name="n_key_servers">
<item quantity="one">%d Schlüsselserver</item>
<item quantity="other">%d Schlüsselserver</item>
@@ -213,9 +217,12 @@
<item quantity="one">%d Schlüssel gefunden.</item>
<item quantity="other">%d Schlüssel gefunden.</item>
</plurals>
- <string name="unknown_signature_key_touch_to_look_up">Unbekannte Unterschrift, zum Suchen berühren. </string>
- <string name="lookup_unknown_key">Unbekannter Schlüssel %s, soll dieser auf einem Schlüsselserver gesucht werden?</string>
- <string name="key_send_success">Schlüssel erfolgreich zum Server geschickt.</string>
+ <string name="unknown_signature">Unbekannte Signatur. Benutze den Button um den fehlenden Schlüssel nachzuschlagen.</string>
+ <plurals name="bad_keys_encountered">
+ <item quantity="one">%d schlechter privater Schlüssel ignoriert. Evtl. wurde er mit folgender Option exportiert:\n --export-secret-subkeys\nUnbedingt mit der Option \n --export-secret-keys\nexportieren.</item>
+ <item quantity="other">%d schlechte private Schlüssel ignoriert. Evtl. wurden sie mit folgender Option exportiert:\n --export-secret-subkeys\nUnbedingt mit der Option \n --export-secret-keys\nexportieren.</item>
+ </plurals>
+ <string name="key_send_success">Schlüssel wurde erfolgreich hochgeladen.</string>
<string name="key_sign_success">Schlüssel erfolgreich signiert</string>
<string name="list_empty">Diese Liste ist leer!</string>
<string name="nfc_successfull">Schlüssel erfolgreich mit NFC Beam gesendet!</string>
@@ -254,19 +261,22 @@
<string name="error_jelly_bean_needed">Android 4.1 alias Jelly Bean wird benötigt um Androids NFC-Beam nutzen zu können!</string>
<string name="error_nfc_needed">NFC steht auf diesem Gerät nicht zur Verfügung!</string>
<string name="error_nothing_import">Nichts zu importieren!</string>
+ <string name="error_expiry_must_come_after_creation">Ablaufdatum muss später sein als das Erstellungsdatum</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>
<!--progress dialogs, usually ending in '…'-->
<string name="progress_done">fertig.</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 eine Weile 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_importing_secret_keys">importiere private Schlüssel...</string>
<plurals name="progress_exporting_key">
<item quantity="one">Schlüssel wird exportiert…</item>
<item quantity="other">Schlüssel werden exportiert…</item>
@@ -327,17 +337,15 @@
<string name="intent_send_encrypt">OpenPGP: Verschlüsseln</string>
<string name="intent_send_decrypt">OpenPGP: Entschlüsseln</string>
<!--Remote API-->
- <string name="api_no_apps">Keine registrierten Anwendungen vorhanden!</string>
<string name="api_settings_show_advanced">Erweiterte Einstellungen anzeigen</string>
<string name="api_settings_hide_advanced">Erweiterte Einstellungen ausblenden</string>
<string name="api_settings_no_key">Kein Schlüssel ausgewählt</string>
<string name="api_settings_select_key">Schlüssel auswählen</string>
<string name="api_settings_save">Speichern</string>
<string name="api_settings_cancel">Abbrechen</string>
- <string name="api_settings_revoke">Zugang widerufen</string>
+ <string name="api_settings_revoke">Zugriff widerufen</string>
<string name="api_settings_package_name">Paketname</string>
<string name="api_settings_package_signature">SHA-256 der Paketsignatur</string>
- <string name="api_register_text">Folgende Anwendung beantragt Zugriff zur API von OpenPGP Keychain.\n\nZugriff dauerhaft erlauben?</string>
<string name="api_register_allow">Zugriff erlauben</string>
<string name="api_register_disallow">Zugriff verbieten</string>
<string name="api_register_error_select_key">Bitte einen Schlüssel auswählen!</string>
@@ -363,6 +371,7 @@
<string name="key_list_empty_button_import">existierende Schlüssel importierst.</string>
<!--Key view-->
<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>
<!--Navigation Drawer-->
<string name="nav_contacts">Kontakte</string>
<string name="nav_encrypt">Verschlüsseln</string>
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 8f508a885..6efe9548d 100644
--- a/OpenPGP-Keychain/src/main/res/values-es-rCO/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-es-rCO/strings.xml
@@ -10,7 +10,6 @@
<string name="title_edit_key">Editar clave</string>
<string name="title_preferences">Preferencias</string>
<string name="title_api_registered_apps">Aplicaciones registradas</string>
- <string name="title_key_server_preference">Preferencias del servidor de claves</string>
<string name="title_change_pass_phrase">Cambiar contraseña</string>
<string name="title_set_passphrase">Establecer contraseña</string>
<string name="title_send_email">Enviar correo electrónico...</string>
@@ -20,9 +19,7 @@
<string name="title_export_key">Exportar clave</string>
<string name="title_export_keys">Exportar claves</string>
<string name="title_key_not_found">Clave no encontrada</string>
- <string name="title_key_server_query">Solicitar al servidor de claves</string>
<string name="title_unknown_signature_key">Clave de firma desconocida</string>
- <string name="title_sign_key">Clave de firma</string>
<string name="title_help">Ayuda</string>
<!--section-->
<string name="section_user_ids">IDs de usuario</string>
@@ -31,13 +28,8 @@
<string name="section_defaults">Por defecto</string>
<string name="section_advanced">Avanzado</string>
<!--button-->
- <string name="btn_sign_to_clipboard">Firmar (Portapapeles)</string>
- <string name="btn_encrypt_to_clipboard">Cifrar a portapapeles</string>
- <string name="btn_encrypt_and_send">Cifrar y enviar</string>
- <string name="btn_sign_and_send">Firmar y enviar</string>
<string name="btn_sign">Firmar</string>
<string name="btn_decrypt">Descifrar</string>
- <string name="btn_verify">Verificar</string>
<string name="btn_select_encrypt_keys">Escoger destinatarios</string>
<string name="btn_encrypt_file">Cifrar archivo</string>
<string name="btn_save">Guardar</string>
@@ -61,7 +53,6 @@
<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_sign_key">Clave de firma</string>
<!--label-->
<string name="label_sign">Firmar</string>
@@ -80,8 +71,6 @@
<string name="label_symmetric">Contraseña</string>
<string name="label_message_compression">Compresión de mensaje</string>
<string name="label_file_compression">Compresión de archivo</string>
- <string name="label_force_v3_signature">Forzar firmas V3</string>
- <string name="label_key_servers">Servidores de claves</string>
<string name="label_key_id">ID de clave</string>
<string name="label_creation">Creación</string>
<string name="label_expiry">Expiración</string>
diff --git a/OpenPGP-Keychain/src/main/res/values-es/strings.xml b/OpenPGP-Keychain/src/main/res/values-es/strings.xml
new file mode 100644
index 000000000..d6b0f9562
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/values-es/strings.xml
@@ -0,0 +1,393 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <!--title-->
+ <string name="title_manage_public_keys">Contactos</string>
+ <string name="title_manage_secret_keys">Claves secretas</string>
+ <string name="title_select_recipients">Seleccionar la clave pública</string>
+ <string name="title_select_secret_key">Seleccionar la clave secreta</string>
+ <string name="title_encrypt">Cifrar</string>
+ <string name="title_decrypt">Descifrar</string>
+ <string name="title_authentication">Frase de contraseña</string>
+ <string name="title_create_key">Crear clave</string>
+ <string name="title_edit_key"> Editar clave</string>
+ <string name="title_preferences"> Preferencias</string>
+ <string name="title_api_registered_apps">Aplicaciones registradas</string>
+ <string name="title_key_server_preference">Prioridad del servidor de claves</string>
+ <string name="title_change_pass_phrase">Cambiar la 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_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>
+ <string name="title_export_key">Exportar clave</string>
+ <string name="title_export_keys">Exportar claves</string>
+ <string name="title_key_not_found">Clave no encontrada</string>
+ <string name="title_key_server_query">Consultar servidor de claves</string>
+ <string name="title_send_key">Cargar al servidor de claves</string>
+ <string name="title_unknown_signature_key">Clave de firma desconocida</string>
+ <string name="title_certify_key">Certificar clave</string>
+ <string name="title_key_details">Detalles de la clave</string>
+ <string name="title_help">Ayuda</string>
+ <!--section-->
+ <string name="section_user_ids">IDs de usuario</string>
+ <string name="section_keys">Claves</string>
+ <string name="section_general">General</string>
+ <string name="section_defaults">Predeterminados</string>
+ <string name="section_advanced">Avanzado</string>
+ <string name="section_master_key">Clave maestra</string>
+ <string name="section_master_user_id">ID del usuario principal</string>
+ <string name="section_actions">Acciones</string>
+ <string name="section_certification_key">Tu clave usada para las certificaciones</string>
+ <string name="section_upload_key">Cargar clave</string>
+ <string name="section_key_server">Servidor de claves</string>
+ <string name="section_encrypt_and_or_sign">Cifrar y/o firmar</string>
+ <string name="section_decrypt_verify">Descifrar y verificar</string>
+ <!--button-->
+ <string name="btn_sign">Firmar</string>
+ <string name="btn_certify">Certificar</string>
+ <string name="btn_decrypt">Descifrar</string>
+ <string name="btn_decrypt_verify">Descifrar y verificar</string>
+ <string name="btn_select_encrypt_keys">Seleccionar destinatarios</string>
+ <string name="btn_encrypt_file">Cifrar archivo</string>
+ <string name="btn_save">Guardar</string>
+ <string name="btn_do_not_save">Cancelar</string>
+ <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_search">Buscar</string>
+ <string name="btn_export_to_server">Cargar al servidor de claves</string>
+ <string name="btn_next">Siguiente</string>
+ <string name="btn_back">Volver</string>
+ <string name="btn_clipboard">Portapapeles</string>
+ <string name="btn_share">Compartir con...</string>
+ <string name="btn_lookup_key">Buscar clave</string>
+ <!--menu-->
+ <string name="menu_preferences">Ajustes</string>
+ <string name="menu_help">Ayuda</string>
+ <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">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_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_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_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>
+ <string name="menu_share_default">con...</string>
+ <string name="menu_share_qr_code">con código QR</string>
+ <string name="menu_share_qr_code_fingerprint">con código QR</string>
+ <string name="menu_share_nfc">con NFC</string>
+ <string name="menu_copy_to_clipboard">Copiar al portapapeles</string>
+ <string name="menu_sign_key"> Clave de firma</string>
+ <string name="menu_beam_preferences">Ajustes de Beam</string>
+ <string name="menu_key_edit_cancel">Cancelar</string>
+ <string name="menu_encrypt_to">Cifrar hacia...</string>
+ <!--label-->
+ <string name="label_sign">Firmar</string>
+ <string name="label_message">Mensaje</string>
+ <string name="label_file">Archivo</string>
+ <string name="label_no_passphrase">No hay frase de contraseña</string>
+ <string name="label_passphrase">Frase de contraseña</string>
+ <string name="label_passphrase_again">De nuevo</string>
+ <string name="label_algorithm">Algoritmo</string>
+ <string name="label_ascii_armor">Armadura ASCII</string>
+ <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_encryption_algorithm">Algoritmo de cifrado</string>
+ <string name="label_hash_algorithm">Algoritmo de Hash</string>
+ <string name="label_asymmetric">Clave pública</string>
+ <string name="label_symmetric">Frase de contraseña</string>
+ <string name="label_passphrase_cache_ttl">Caché de frase de contraseña</string>
+ <string name="label_message_compression">Compresión de mensaje</string>
+ <string name="label_file_compression">Compresión de archivo</string>
+ <string name="label_force_v3_signature">Forzar firmas OpenPGPv3 antiguas</string>
+ <string name="label_key_servers">Servidores de claves</string>
+ <string name="label_key_id">ID de clave</string>
+ <string name="label_creation">Creación</string>
+ <string name="label_expiry">Caducidad</string>
+ <string name="label_usage">Uso</string>
+ <string name="label_key_size">Tamaño de clave</string>
+ <string name="label_main_user_id">ID del usuario principal</string>
+ <string name="label_name">Nombre</string>
+ <string name="label_comment">Comentario</string>
+ <string name="label_email">Email</string>
+ <string name="label_send_key">Cargar clave al servidor de claves seleccionado después de la certificación</string>
+ <string name="label_fingerprint">Huella digital</string>
+ <string name="select_keys_button_default">Seleccionar</string>
+ <plurals name="select_keys_button">
+ <item quantity="one">%d seleccionado</item>
+ <item quantity="other">%d seleccionados</item>
+ </plurals>
+ <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="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>
+ <plurals name="n_key_servers">
+ <item quantity="one">%d servidor de claves</item>
+ <item quantity="other">%d servidores de claves</item>
+ </plurals>
+ <string name="fingerprint">Huella digital:</string>
+ <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>
+ <string name="choice_5mins">5 mins</string>
+ <string name="choice_10mins">10 mins</string>
+ <string name="choice_20mins">20 mins</string>
+ <string name="choice_40mins">40 mins</string>
+ <string name="choice_1hour">1 hora</string>
+ <string name="choice_2hours">2 horas</string>
+ <string name="choice_4hours">4 horas</string>
+ <string name="choice_8hours">8 horas</string>
+ <string name="choice_forever">para siempre</string>
+ <string name="dsa">DSA</string>
+ <string name="elgamal">ElGamal</string>
+ <string name="rsa">RSA</string>
+ <string name="filemanager_title_open">Abrir...</string>
+ <string name="warning">Advertencia</string>
+ <string name="error">Error</string>
+ <string name="error_message">Error: %s</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_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="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>
+ <string name="specify_file_to_encrypt_to">Por favor, especifica hacia qué archivo quieres cifrar.\nADVERTENCIA: El archivo se sobreescribirá si ya existiese.</string>
+ <string name="specify_file_to_decrypt_to">Por favor, especifica hacia qué archivo quieres descifrar.\nADVERTENCIA: El archivo se sobreescribirá si ya existiese.</string>
+ <string name="specify_file_to_export_to">Por favor, especifica hacia qué archivo quieres exportar.\nADVERTENCIA: El archivo se sobreescribirá si ya existiese.</string>
+ <string name="specify_file_to_export_secret_keys_to">Por favor, especifica hacia qué archivo quieres exportar.\nADVERTENCIA: Estás a punto de exportar claves SECRETAS.\nADVERTENCIA: El archivo se sobreescribirá si ya existiese.</string>
+ <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>
+ <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>
+ </plurals>
+ <plurals name="keys_added_and_updated_2">
+ <item quantity="one">y actualizada %d clave.</item>
+ <item quantity="other">y actualizadas %d claves.</item>
+ </plurals>
+ <plurals name="keys_added">
+ <item quantity="one">%d clave añadida satisfactoriamente.</item>
+ <item quantity="other">%d claves añadidas satisfactoriamente.</item>
+ </plurals>
+ <plurals name="keys_updated">
+ <item quantity="one">%d clave actualizada satisfactoriamente.</item>
+ <item quantity="other">%d claves actualizadas satisfactoriamente.</item>
+ </plurals>
+ <string name="no_keys_added_or_updated">No se han añadido o actualizado claves.</string>
+ <string name="key_exported">Se ha exportado 1 clave satisfactoriamente.</string>
+ <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_not_found">No se puede encontrar la clave %08X.</string>
+ <plurals name="keys_found">
+ <item quantity="one">Se ha encontrado %d clave.</item>
+ <item quantity="other">Se han encontrado %d claves.</item>
+ </plurals>
+ <string name="unknown_signature">Firma desconocida, pulsa el botón para buscar la clave extraviada.</string>
+ <plurals name="bad_keys_encountered">
+ <item quantity="one">%d mala clave secreta ignorada. Quizás hayas exportado con la opción\n--export-secret-subkeys\nAsegúrate de que exportas con\n--export-secret-keys\nen su lugar.</item>
+ <item quantity="other">%d malas claves secretas ignoradas. Quizás hayas exportado con la opción\n--export-secret-subkeys\nAsegúrate de que exportas con\n--export-secret-keys\nen su lugar.</item>
+ </plurals>
+ <string name="key_send_success">Clave cargada al servidor satisfactoriamente</string>
+ <string name="key_sign_success">Clave firmada satisfactoriamente</string>
+ <string name="list_empty">¡Esta lista está vacía!</string>
+ <string name="nfc_successfull">¡Clave enviada satisfactoriamente con NFC Beam!</string>
+ <string name="key_copied_to_clipboard">¡La clave ha sido copiada al portapapeles!</string>
+ <string name="key_has_already_been_signed">¡La clave ya ha sido firmada!</string>
+ <string name="select_key_to_sign">¡Por favor, selecciona la clave que se usará para firmar!</string>
+ <string name="key_too_big_for_sharing">¡La clave es demasiado grande para ser compartida de esta forma!</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">ha fallado el borrado de \'%s\'</string>
+ <string name="error_file_not_found">archivo no encontrado</string>
+ <string name="error_no_secret_key_found">no se ha encontrado una clave secreta adecuada</string>
+ <string name="error_no_known_encryption_found">se ha encontrado un tipo de cifrado no conocido</string>
+ <string name="error_external_storage_not_ready">el almacenamiento externo no está preparado</string>
+ <string name="error_invalid_email">email incorrecto \'%s\'</string>
+ <string name="error_key_size_minimum512bit">el tamaño de clave debe ser de al menos 512bit</string>
+ <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_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>
+ <string name="error_key_needs_master_key">necesitas al menos una clave maestra</string>
+ <string name="error_no_encryption_keys_or_passphrase">no has proporcionado ninguna clave de cifrado o frase de contraseña</string>
+ <string name="error_signature_failed">la firma ha fallado</string>
+ <string name="error_no_signature_passphrase">no has proporcionado una frase de contraseña</string>
+ <string name="error_no_signature_key">no has proporcionado una clave de firma</string>
+ <string name="error_invalid_data">cifrado de datos no válido</string>
+ <string name="error_corrupt_data">datos corrompidos</string>
+ <string name="error_integrity_check_failed">¡ha fallado la comprobación de integridad! ¡Los datos han sido modificados!</string>
+ <string name="error_no_symmetric_encryption_packet">no se ha podido encontrar un paquete con cifrado simétrico</string>
+ <string name="error_wrong_passphrase">frase de contraseña incorrecta</string>
+ <string name="error_saving_keys">error al guardar algunas claves</string>
+ <string name="error_could_not_extract_private_key">no se puede extraer la clave privada</string>
+ <string name="error_only_files_are_supported">Dirigir datos binarios sin un archivo real en el sistema de archivos es incompatible. Esto solo es compatible con ACTION_ENCRYPT_STREAM_AND_RETURN.</string>
+ <string name="error_jelly_bean_needed">!Necesitas Android 4.1 alias Jelly Bean para poder usar la característica NFC Beam!</string>
+ <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_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>
+ <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>
+ <!--progress dialogs, usually ending in '…'-->
+ <string name="progress_done">hecho.</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>
+ <string name="progress_building_master_key">construyendo el anillo maestro...</string>
+ <string name="progress_adding_sub_keys">añadiendo las subclaves...</string>
+ <string name="progress_saving_key_ring">guardando claves...</string>
+ <plurals name="progress_exporting_key">
+ <item quantity="one">exportando clave...</item>
+ <item quantity="other">exportando claves...</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>
+ <string name="progress_encrypting">cifrando los datos...</string>
+ <string name="progress_decrypting">descifrando los datos...</string>
+ <string name="progress_preparing_signature">preparando la firma...</string>
+ <string name="progress_generating_signature">generando la firma...</string>
+ <string name="progress_processing_signature">procesando la firma...</string>
+ <string name="progress_verifying_signature">verificando la firma...</string>
+ <string name="progress_signing">firmando...</string>
+ <string name="progress_reading_data">leyendo los datos...</string>
+ <string name="progress_finding_key">localizando la clave...</string>
+ <string name="progress_decompressing_data">descomprimiendo los datos...</string>
+ <string name="progress_verifying_integrity">verificando la integridad...</string>
+ <string name="progress_deleting_securely">borrando \'%s\' de forma segura…</string>
+ <string name="progress_querying">consultando...</string>
+ <!--action strings-->
+ <string name="hint_public_keys">Buscar claves públicas</string>
+ <string name="hint_secret_keys">Buscar claves secretas</string>
+ <string name="action_share_key_with">Compartir la clave con...</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">rápido</string>
+ <string name="compression_very_slow">muy lento</string>
+ <!--Help-->
+ <string name="help_tab_start">Comenzar</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>
+ <string name="help_about_version">Versión:</string>
+ <!--Import-->
+ <string name="import_import">Importar las claves seleccionadas</string>
+ <string name="import_sign_and_upload">Importar, firmar y cargar las claves seleccionadas</string>
+ <string name="import_from_clipboard">Importar desde el portapapeles</string>
+ <plurals name="import_qr_code_missing">
+ <item quantity="one">El código QR con ID %s se ha extraviado</item>
+ <item quantity="other">Los códigos QR con IDs %s se han extraviado</item>
+ </plurals>
+ <string name="import_qr_code_start_with_one">Por favor, comienza con el código QR de ID 1</string>
+ <string name="import_qr_code_wrong">¡El código QR está deformado! ¡Por favor, prueba de nuevo!</string>
+ <string name="import_qr_code_finished">¡El escaneo del código QR ha finalizado!</string>
+ <string name="import_qr_code_too_short_fingerprint">La huella digital contenida en este código QR es demasiado corta (&lt; 16 caracteres)</string>
+ <string name="import_qr_scan_button">Escanea el código QR con \'Barcode Scanner\'</string>
+ <string name="import_nfc_text">Para recibir las claves a través de NFC, el dispositivo tiene que estar desbloqueado.</string>
+ <string name="import_nfc_help_button">Ayuda</string>
+ <string name="import_clipboard_button">Tomar la clave desde el portapapeles</string>
+ <!--Intent labels-->
+ <string name="intent_decrypt_file">OpenPGP: Descifrar archivo</string>
+ <string name="intent_import_key">OpenPGP: Importar clave</string>
+ <string name="intent_send_encrypt">OpenPGP: Cifrar</string>
+ <string name="intent_send_decrypt">OpenPGP: Descifrar</string>
+ <!--Remote API-->
+ <string name="api_no_apps">¡No hay aplicaciones registradas¡\n\nLas aplicaciones de terceros pueden pedir acceso a OpenPGP Keychain. Después de recibir permiso, se mostrarán aquí.</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_save">Guardar</string>
+ <string name="api_settings_cancel">Cancelar</string>
+ <string name="api_settings_revoke">Revocar acceso</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_register_text">La aplicación mostrada pide acceso a OpenPGP Keychain.\n¿Permitir acceso?\n\nAVISO: Si no sabes por qué ha aparecido esta pantalla, ¡deniega el acceso! Puedes revocar el acceso más tarde usando la ventana \'Aplicaciones Registradas\'.</string>
+ <string name="api_register_allow">Permitir el acceso</string>
+ <string name="api_register_disallow">Denegar el acceso</string>
+ <string name="api_register_error_select_key">¡Por favor, selecciona una clave!</string>
+ <string name="api_select_pub_keys_missing_text">No se han encontrado claves públicas para estas IDs de usuario:</string>
+ <string name="api_select_pub_keys_dublicates_text">Existe más de una clave pública para estos IDs de usuario:</string>
+ <string name="api_select_pub_keys_text">¡Por favor, revisa la lista de destinatarios!</string>
+ <string name="api_error_wrong_signature">¡La comprobación de la firma ha fallado! ¿Has instalado esta app desde una fuente distinta? Si estás seguro de que esto no es un ataque, revoca el registro de esta app en OpenPGP Keychain y regístrala de nuevo.</string>
+ <!--Share-->
+ <string name="share_qr_code_dialog_title">Compartir con código QR</string>
+ <string name="share_qr_code_dialog_start">Pasa por todos los códigos QR usando \'Siguiente\', y escanéalos de uno en uno.</string>
+ <string name="share_qr_code_dialog_fingerprint_text">Huella digital:</string>
+ <string name="share_qr_code_dialog_progress">Código QR con ID %1$d de %2$d</string>
+ <string name="share_nfc_dialog">Compartir con NFC</string>
+ <!--Key list-->
+ <plurals name="key_list_selected_keys">
+ <item quantity="one">1 clave seleccionada.</item>
+ <item quantity="other">%d claves seleccionadas.</item>
+ </plurals>
+ <string name="key_list_empty_text1">Aún no hay claves disponibles...</string>
+ <string name="key_list_empty_text2">Puedes empezar por</string>
+ <string name="key_list_empty_text3">o</string>
+ <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_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_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>
+</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-fr/strings.xml b/OpenPGP-Keychain/src/main/res/values-fr/strings.xml
index d427403dd..016090d61 100644
--- a/OpenPGP-Keychain/src/main/res/values-fr/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-fr/strings.xml
@@ -12,7 +12,7 @@
<string name="title_edit_key">Modifier une clef</string>
<string name="title_preferences">Préférences</string>
<string name="title_api_registered_apps">Applications enregistrées</string>
- <string name="title_key_server_preference">Serveurs de clefs préférés</string>
+ <string name="title_key_server_preference">Préférences du serveur de clefs</string>
<string name="title_change_pass_phrase">Changer la phrase de passe</string>
<string name="title_set_passphrase">Définir la phrase de passe</string>
<string name="title_send_email">Envoyer un courriel...</string>
@@ -25,7 +25,7 @@
<string name="title_key_server_query">Interroger le serveur de clefs</string>
<string name="title_send_key">Téléverser vers le serveur de clefs</string>
<string name="title_unknown_signature_key">Clef de signature inconnue</string>
- <string name="title_sign_key">Signer la clef</string>
+ <string name="title_certify_key">Certifier la clef</string>
<string name="title_key_details">Détails sur la clef</string>
<string name="title_help">Aide</string>
<!--section-->
@@ -37,17 +37,16 @@
<string name="section_master_key">Clef maîtresse</string>
<string name="section_master_user_id">ID utilisateur maître</string>
<string name="section_actions">Actions</string>
- <string name="section_signing_key">Votre clef utilisée pour la signature</string>
+ <string name="section_certification_key">Votre clef utilisée pour la certification</string>
<string name="section_upload_key">Téléverser la clef</string>
<string name="section_key_server">Serveur de clefs</string>
+ <string name="section_encrypt_and_or_sign">Chiffrer et/ou signer</string>
+ <string name="section_decrypt_verify">Déchiffrer et vérifier</string>
<!--button-->
- <string name="btn_sign_to_clipboard">Signer (presse-papiers)</string>
- <string name="btn_encrypt_to_clipboard">Chiffrer vers le presse-papiers</string>
- <string name="btn_encrypt_and_send">Chiffrer et envoyer...</string>
- <string name="btn_sign_and_send">Signer et envoyer...</string>
<string name="btn_sign">Signer</string>
+ <string name="btn_certify">Certifier</string>
<string name="btn_decrypt">Déchiffrer</string>
- <string name="btn_verify">Vérifier</string>
+ <string name="btn_decrypt_verify">Déchiffrer et vérifier</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>
@@ -61,6 +60,9 @@
<string name="btn_export_to_server">Téléverser vers le serveur de clefs</string>
<string name="btn_next">Suivant</string>
<string name="btn_back">Retour</string>
+ <string name="btn_clipboard">Presse-papiers</string>
+ <string name="btn_share">Partager avec...</string>
+ <string name="btn_lookup_key">Rechercher la clef</string>
<!--menu-->
<string name="menu_preferences">Paramètres</string>
<string name="menu_help">Aide</string>
@@ -109,7 +111,6 @@
<string name="label_passphrase_cache_ttl">Cache de la phrase de passe</string>
<string name="label_message_compression">Compression des messages</string>
<string name="label_file_compression">Compression des fichiers</string>
- <string name="label_force_v3_signature">Forcer les signatures V3</string>
<string name="label_key_servers">Serveurs de clefs</string>
<string name="label_key_id">ID de le clef</string>
<string name="label_creation">Création</string>
@@ -120,7 +121,7 @@
<string name="label_name">Nom</string>
<string name="label_comment">Commentaire</string>
<string name="label_email">Courriel</string>
- <string name="label_send_key">Téléverser la clef vers le serveur de clefs choisi après signature</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>
<plurals name="select_keys_button">
@@ -134,9 +135,10 @@
<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>
<plurals name="n_key_servers">
- <item quantity="one">%d serveur de clef</item>
- <item quantity="other">%d serveurs de clef</item>
+ <item quantity="one">%d serveur de clefs</item>
+ <item quantity="other">%d serveurs de clefs</item>
</plurals>
<string name="fingerprint">Empreinte :</string>
<string name="secret_key">Clef secrète :</string>
@@ -215,13 +217,12 @@
<item quantity="one">%d clef trouvée.</item>
<item quantity="other">%d clefs trouvées.</item>
</plurals>
- <string name="unknown_signature_key_touch_to_look_up">Signature inconnue. Toucher pour rechercher la clef.</string>
+ <string name="unknown_signature">Signature inconnue. Cliquer sur le bouton pour rechercher la clef manquante.</string>
<plurals name="bad_keys_encountered">
<item quantity="one">%d mauvaise clef ignorée. Vous avez peut-être exporté avec l\'option\n --export-secret-subkeys\nAssurez-vous d\'exporter plutôt avec\n --export-secret-keys.</item>
<item quantity="other">%d mauvaises clefs ignorées. Vous avez peut-être exporté avec l\'option\n --export-secret-subkeys\nAssurez-vous d\'exporter plutôt avec\n --export-secret-keys.</item>
</plurals>
- <string name="lookup_unknown_key">Clef %s inconnue, voulez-vous essayer de la trouver sur un serveur de clefs ?</string>
- <string name="key_send_success">Clef envoyée vers le serveur avec succès</string>
+ <string name="key_send_success">Clef téléversée vers le serveur avec succès</string>
<string name="key_sign_success">Clef signée avec succès</string>
<string name="list_empty">Cette liste est vide !</string>
<string name="nfc_successfull">Clef envoyée par NFC BEAM avec succès !</string>
@@ -234,7 +235,7 @@
they will be put after "error_message", e.g. "Error: file not found"-->
<string name="error_file_delete_failed">échec lors de la suppression de « %s »</string>
<string name="error_file_not_found">fichier introuvable</string>
- <string name="error_no_secret_key_found">aucune clé secrète adéquate n\'a été trouvée</string>
+ <string name="error_no_secret_key_found">aucune clefs secrète adéquate n\'a été trouvée</string>
<string name="error_no_known_encryption_found">aucune sorte de chiffrement connu n\'a été trouvé</string>
<string name="error_external_storage_not_ready">le stockage externe n\'est pas prêt</string>
<string name="error_invalid_email">courriel « %s » invalide</string>
@@ -261,19 +262,24 @@
<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_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>
+ <plurals name="error_can_not_delete_info">
+ <item quantity="one">Veuillez le supprimer depuis l\'écran « Mes Clefs »!</item>
+ <item quantity="other">Veuillez les supprimer depuis l\'écran « Mes Clefs »!</item>
+ </plurals>
<!--progress dialogs, usually ending in '…'-->
<string name="progress_done">fait.</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 un moment...</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>
<string name="progress_building_master_key">assemblage du trousseau maître...</string>
<string name="progress_adding_sub_keys">ajout des sous-clefs...</string>
<string name="progress_saving_key_ring">sauvegarde de la clef...</string>
- <string name="progress_importing_secret_keys">Importation des clefs secrètes...</string>
<plurals name="progress_exporting_key">
<item quantity="one">exportation de la clef...</item>
<item quantity="other">exportation des clefs...</item>
@@ -334,7 +340,7 @@
<string name="intent_send_encrypt">OpenPGP : chiffrer</string>
<string name="intent_send_decrypt">OpenPGP : déchiffrer</string>
<!--Remote API-->
- <string name="api_no_apps">Aucune application enregistrée !</string>
+ <string name="api_no_apps">Aucune application enregistrée !\n\nLes applications tierces peuvent demander l\'accès au Porte-clefs OpenPGP. Après avoir autoriser l\'accès, elles seront listées ici.</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>
@@ -344,7 +350,7 @@
<string name="api_settings_revoke">Révoquer l\'accès</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_register_text">L\'application suivante demande l\'accès à l\'API du Porte-clefs OpenPGP.\n\nPermettre l\'accès permanent ?</string>
+ <string name="api_register_text">L\'application affichée demande l\'accès au Porte-clefs OpenPGP.\nPermettre l\'accès ?\n\nAvertissement : si vous ne savez pas pourquoi cet écran est apparu, refusé 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>
<string name="api_register_error_select_key">Veuillez choisir une clef !</string>
@@ -370,6 +376,9 @@
<string name="key_list_empty_button_import">Importer des clefs.</string>
<!--Key view-->
<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>
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 44424561b..e404addfa 100644
--- a/OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml
@@ -15,7 +15,6 @@
<string name="title_export_key">Esportare Chiave</string>
<string name="title_export_keys">Esportare Chiavi</string>
<string name="title_key_not_found">Chiave Non Trovata</string>
- <string name="title_sign_key">Firma Chiave</string>
<string name="title_help">Aiuto</string>
<!--section-->
<string name="section_user_ids">ID Utente</string>
@@ -23,10 +22,8 @@
<string name="section_general">Generale</string>
<string name="section_advanced">Avanzato</string>
<!--button-->
- <string name="btn_sign_and_send">Firmare ed inviare...</string>
<string name="btn_sign">Firmare</string>
<string name="btn_decrypt">Decifrare</string>
- <string name="btn_verify">Verificare</string>
<string name="btn_encrypt_file">Cifrare File</string>
<string name="btn_save">Salva</string>
<string name="btn_do_not_save">Cancella</string>
diff --git a/OpenPGP-Keychain/src/main/res/values-ja/strings.xml b/OpenPGP-Keychain/src/main/res/values-ja/strings.xml
new file mode 100644
index 000000000..15043c93a
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/values-ja/strings.xml
@@ -0,0 +1,381 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <!--title-->
+ <string name="title_manage_public_keys">連絡先</string>
+ <string name="title_manage_secret_keys">秘密鍵</string>
+ <string name="title_select_recipients">公開鍵の選択</string>
+ <string name="title_select_secret_key">秘密鍵の選択</string>
+ <string name="title_encrypt">暗号化</string>
+ <string name="title_decrypt">復号化</string>
+ <string name="title_authentication">パスフレーズ</string>
+ <string name="title_create_key">鍵の生成</string>
+ <string name="title_edit_key">鍵の編集</string>
+ <string name="title_preferences">設定</string>
+ <string name="title_api_registered_apps">登録済みのアプリケーション</string>
+ <string name="title_key_server_preference">鍵サーバ設定</string>
+ <string name="title_change_pass_phrase">パスフレーズの変更</string>
+ <string name="title_set_passphrase">パスフレーズの設定</string>
+ <string name="title_send_email">メールの送信...</string>
+ <string name="title_encrypt_to_file">暗号化してファイルに</string>
+ <string name="title_decrypt_to_file">復号化してファイルに</string>
+ <string name="title_import_keys">鍵のインポート</string>
+ <string name="title_export_key">鍵のエクスポート</string>
+ <string name="title_export_keys">複数鍵のエクスポート</string>
+ <string name="title_key_not_found">鍵が見当りません</string>
+ <string name="title_key_server_query">鍵サーバへの要求</string>
+ <string name="title_send_key">鍵サーバへアップロード</string>
+ <string name="title_unknown_signature_key">不明な署名の鍵です</string>
+ <string name="title_certify_key">鍵検証</string>
+ <string name="title_key_details">鍵の概要</string>
+ <string name="title_help">ヘルプ</string>
+ <!--section-->
+ <string name="section_user_ids">ユーザID</string>
+ <string name="section_keys">鍵</string>
+ <string name="section_general">一般</string>
+ <string name="section_defaults">デフォルト</string>
+ <string name="section_advanced">拡張</string>
+ <string name="section_master_key">主鍵</string>
+ <string name="section_master_user_id">主ユーザID</string>
+ <string name="section_actions">アクション</string>
+ <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_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_select_encrypt_keys">受信者の選択</string>
+ <string name="btn_encrypt_file">ファイル暗号化</string>
+ <string name="btn_save">保存</string>
+ <string name="btn_do_not_save">キャンセル</string>
+ <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_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_lookup_key">鍵検出</string>
+ <!--menu-->
+ <string name="menu_preferences">設定</string>
+ <string name="menu_help">ヘルプ</string>
+ <string name="menu_import_from_file">ファイルからインポート</string>
+ <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_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_update_key">鍵サーバからの更新</string>
+ <string name="menu_export_key_to_server">鍵サーバへのアップロード</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>
+ <string name="menu_share_default">...(鍵)</string>
+ <string name="menu_share_qr_code">QRコードで共有(鍵)</string>
+ <string name="menu_share_qr_code_fingerprint">QRコードで共有(指紋)</string>
+ <string name="menu_share_nfc">NFCで共有</string>
+ <string name="menu_copy_to_clipboard">クリップボードへコピー</string>
+ <string name="menu_sign_key">鍵を署名</string>
+ <string name="menu_beam_preferences">Beamの設定</string>
+ <string name="menu_key_edit_cancel">キャンセル</string>
+ <string name="menu_encrypt_to">暗号化...</string>
+ <!--label-->
+ <string name="label_sign">署名</string>
+ <string name="label_message">メッセージ</string>
+ <string name="label_file">ファイル</string>
+ <string name="label_no_passphrase">パスフレーズなし</string>
+ <string name="label_passphrase">パスフレーズ</string>
+ <string name="label_passphrase_again">もう一度</string>
+ <string name="label_algorithm">アルゴリズム</string>
+ <string name="label_ascii_armor">アスキー形式</string>
+ <string name="label_select_public_keys">受信者</string>
+ <string name="label_delete_after_encryption">暗号化後に削除</string>
+ <string name="label_delete_after_decryption">復号化後に削除</string>
+ <string name="label_encryption_algorithm">暗号化アルゴリズム</string>
+ <string name="label_hash_algorithm">ハッシュアルゴリズム</string>
+ <string name="label_asymmetric">公開鍵</string>
+ <string name="label_symmetric">パスフレーズ</string>
+ <string name="label_passphrase_cache_ttl">パスフレーズキャッシュ</string>
+ <string name="label_message_compression">メッセージの圧縮</string>
+ <string name="label_file_compression">ファイルの圧縮</string>
+ <string name="label_force_v3_signature">強制的に古いOpenPGPV3形式の署名にする</string>
+ <string name="label_key_servers">鍵サーバ</string>
+ <string name="label_key_id">鍵ID</string>
+ <string name="label_creation">生成</string>
+ <string name="label_expiry">満了</string>
+ <string name="label_usage">使い方</string>
+ <string name="label_key_size">鍵サイズ</string>
+ <string name="label_main_user_id">主ユーザID</string>
+ <string name="label_name">名前</string>
+ <string name="label_comment">コメント</string>
+ <string name="label_email">Eメールアドレス</string>
+ <string name="label_send_key">証明後選択した鍵サーバに鍵をアップロード</string>
+ <string name="label_fingerprint">指紋</string>
+ <string name="select_keys_button_default">選択</string>
+ <plurals name="select_keys_button">
+ <item quantity="other">%d を選択</item>
+ </plurals>
+ <string name="user_id_no_name">&lt;名前なし&gt;</string>
+ <string name="none">&lt;無し&gt;</string>
+ <string name="no_key">&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>
+ <plurals name="n_key_servers">
+ <item quantity="other">%d の鍵サーバ</item>
+ </plurals>
+ <string name="fingerprint">指紋:</string>
+ <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>
+ <string name="choice_5mins">5分</string>
+ <string name="choice_10mins">10分</string>
+ <string name="choice_20mins">20分</string>
+ <string name="choice_40mins">40分</string>
+ <string name="choice_1hour">1時間</string>
+ <string name="choice_2hours">2時間</string>
+ <string name="choice_4hours">4時間</string>
+ <string name="choice_8hours">8時間</string>
+ <string name="choice_forever">永遠</string>
+ <string name="dsa">DSA</string>
+ <string name="elgamal">ElGamal</string>
+ <string name="rsa">RSA</string>
+ <string name="filemanager_title_open">開く...</string>
+ <string name="warning">注意</string>
+ <string name="error">エラー</string>
+ <string name="error_message">エラー: %s</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_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="enter_passphrase_twice">もう一度パスフレーズを入れてください。</string>
+ <string name="select_encryption_key">少なくとも1つの暗号化鍵を選択して下さい。</string>
+ <string name="select_encryption_or_signature_key">少なくとも1つの暗号化鍵か署名鍵を選択して下さい。</string>
+ <string name="specify_file_to_encrypt_to">どのファイルを暗号化するか決めてください。\n注意: 既存のファイルがあると上書きされます。</string>
+ <string name="specify_file_to_decrypt_to">どのファイルを復号化するか決めてください。\n注意: 既存のファイルがあると上書きされます。</string>
+ <string name="specify_file_to_export_to">どのファイルをエクスポートするか決めてください。\n注意: 既存のファイルがあると上書きされます。</string>
+ <string name="specify_file_to_export_secret_keys_to">どのファイルをエクスポートするか決めてください。\n注意: 秘密鍵をエクスポートしています。\n注意: 既存のファイルがあると上書きされます。</string>
+ <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>
+ <plurals name="keys_added_and_updated_1">
+ <item quantity="other">%d の鍵を追加しました</item>
+ </plurals>
+ <plurals name="keys_added_and_updated_2">
+ <item quantity="other">そして %d の鍵をアップロードしました。</item>
+ </plurals>
+ <plurals name="keys_added">
+ <item quantity="other">%d の鍵を追加しました。</item>
+ </plurals>
+ <plurals name="keys_updated">
+ <item quantity="other">%d の鍵をアップロードしました。</item>
+ </plurals>
+ <string name="no_keys_added_or_updated">鍵の追加もしくは更新はありませんでした。</string>
+ <string name="key_exported">1つの鍵をエクスポートしました。</string>
+ <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_not_found">鍵 %08X は見付かりませんでした。</string>
+ <plurals name="keys_found">
+ <item quantity="other">%d の鍵を発見。</item>
+ </plurals>
+ <string name="unknown_signature">不明な署名、ボタンを押して見付からない鍵を検出してください。</string>
+ <plurals name="bad_keys_encountered">
+ <item quantity="other">%d の問題ある鍵を無視しました。 おそらく次のオプションでエクスポートしています\n --export-secret-subkeys\n代りに次のオプションでエクスポートしてください。\n --export-secret-keys</item>
+ </plurals>
+ <string name="key_send_success">鍵を鍵サーバにアップロードしました</string>
+ <string name="key_sign_success">鍵に署名しました。</string>
+ <string name="list_empty">このリストは空です!</string>
+ <string name="nfc_successfull">NFCビームで鍵を送信しました!</string>
+ <string name="key_copied_to_clipboard">鍵はクリプボードにコピーされました!</string>
+ <string name="key_has_already_been_signed">鍵はすでに署名されています!</string>
+ <string name="select_key_to_sign">署名に使う鍵を選択して下さい!</string>
+ <string name="key_too_big_for_sharing">この共有方法では鍵が大きすぎます!</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">\'%s\' の削除に失敗</string>
+ <string name="error_file_not_found">ファイルが見付かりません</string>
+ <string name="error_no_secret_key_found">組になっている秘密鍵が見付かりません</string>
+ <string name="error_no_known_encryption_found">暗号化法が既知の種類内から見付かりません</string>
+ <string name="error_external_storage_not_ready">外部ストレージが準備できていません</string>
+ <string name="error_invalid_email">\'%s\' は不正なEメールアドレスです</string>
+ <string name="error_key_size_minimum512bit">鍵サイズは最低でも512bit必要です</string>
+ <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_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>
+ <string name="error_key_needs_master_key">主鍵が最低でも1つ必要です</string>
+ <string name="error_no_encryption_keys_or_passphrase">鍵が暗号化されていないかパスフレーズが与えられていません</string>
+ <string name="error_signature_failed">署名に失敗</string>
+ <string name="error_no_signature_passphrase">パスフレーズが与えられていません</string>
+ <string name="error_no_signature_key">署名鍵を与えられていません</string>
+ <string name="error_invalid_data">暗号化データが不正です</string>
+ <string name="error_corrupt_data">壊れたデータ</string>
+ <string name="error_integrity_check_failed">完全性チェックが失敗しました! データに変更があります!</string>
+ <string name="error_no_symmetric_encryption_packet">対称鍵暗号のパケットが見付かりませんでした</string>
+ <string name="error_wrong_passphrase">正しくないパスフレーズです</string>
+ <string name="error_saving_keys">鍵の保存エラー</string>
+ <string name="error_could_not_extract_private_key">秘密鍵を取り出すことができません</string>
+ <string name="error_only_files_are_supported">ファイルシステムに存在するファイルではないバイナリデータはサポートされません。 ACTION_ENCRYPT_STREAM_AND_RETURN でのみサポートされます。</string>
+ <string name="error_jelly_bean_needed">Android NFC Beam機能を使うにはAndroid 4.1 (Jelly Bean) が必要です!</string>
+ <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_can_not_delete_contact">この連絡先はあなたなので削除できません。</string>
+ <string name="error_can_not_delete_contacts">この連絡先はあなたなので削除できません。:\n%s</string>
+ <plurals name="error_can_not_delete_info">
+ <item quantity="other">\'自分の鍵\'画面から削除してください!</item>
+ </plurals>
+ <!--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>
+ <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>
+ <string name="progress_building_master_key">主鍵輪の構築中...</string>
+ <string name="progress_adding_sub_keys">副鍵の追加中...</string>
+ <string name="progress_saving_key_ring">鍵の保存...</string>
+ <plurals name="progress_exporting_key">
+ <item quantity="other">鍵のエクスポート...</item>
+ </plurals>
+ <string name="progress_extracting_signature_key">署名鍵の取り出し中...</string>
+ <string name="progress_extracting_key">鍵の取り出し中...</string>
+ <string name="progress_preparing_streams">ストリームの準備中...</string>
+ <string name="progress_encrypting">データの暗号化中...</string>
+ <string name="progress_decrypting">データの復号化中...</string>
+ <string name="progress_preparing_signature">署名の準備中...</string>
+ <string name="progress_generating_signature">署名の生成中...</string>
+ <string name="progress_processing_signature">署名処理中...</string>
+ <string name="progress_verifying_signature">署名の検証中...</string>
+ <string name="progress_signing">署名中...</string>
+ <string name="progress_reading_data">データ読み込み中...</string>
+ <string name="progress_finding_key">鍵検索中...</string>
+ <string name="progress_decompressing_data">データの展開中...</string>
+ <string name="progress_verifying_integrity">完全性の検証中...</string>
+ <string name="progress_deleting_securely">\'%s\' を完全に削除中…</string>
+ <string name="progress_querying">要求中...</string>
+ <!--action strings-->
+ <string name="hint_public_keys">公開鍵の検索</string>
+ <string name="hint_secret_keys">秘密鍵の検索</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>
+ <string name="key_size_2048">2048</string>
+ <string name="key_size_4096">4096</string>
+ <!--compression-->
+ <string name="compression_fast">早い</string>
+ <string name="compression_very_slow">とても遅い</string>
+ <!--Help-->
+ <string name="help_tab_start">開始</string>
+ <string name="help_tab_nfc_beam">NFC Beam</string>
+ <string name="help_tab_changelog">Changelog</string>
+ <string name="help_tab_about">これについて</string>
+ <string name="help_about_version">バージョン:</string>
+ <!--Import-->
+ <string name="import_import">選択した鍵のインポート</string>
+ <string name="import_sign_and_upload">選択した鍵のインポート、署名、そしてアップロード</string>
+ <string name="import_from_clipboard">クリップボードからインポート</string>
+ <plurals name="import_qr_code_missing">
+ <item quantity="other">ID %s のQRコードがありません</item>
+ </plurals>
+ <string name="import_qr_code_start_with_one">QRコードをID 1で始めてください</string>
+ <string name="import_qr_code_wrong">不適QRコード! もう一度!</string>
+ <string name="import_qr_code_finished">QRコードの読み取り完了!</string>
+ <string name="import_qr_code_too_short_fingerprint">QRコードに含まれる指紋が短かすぎます (&lt; 16 文字)</string>
+ <string name="import_qr_scan_button">\'バーコードスキャナー\'でQRコードをスキャンする</string>
+ <string name="import_nfc_text">NFCで鍵を受信しました、デバイスのロックを解除する必要があります。</string>
+ <string name="import_nfc_help_button">ヘルプ</string>
+ <string name="import_clipboard_button">クリップボードから鍵を取得</string>
+ <!--Intent labels-->
+ <string name="intent_decrypt_file">OpenPGP: ファイル復号</string>
+ <string name="intent_import_key">OpenPGP: 鍵のインポート</string>
+ <string name="intent_send_encrypt">OpenPGP: 暗号化</string>
+ <string name="intent_send_decrypt">OpenPGP: 復号化</string>
+ <!--Remote API-->
+ <string name="api_no_apps">登録されていないアプリケーション!\n\nサードパーティアプリケーションはOpenPGP Keychainにアクセスを要求できます。アクセスを与えた後、それらはここにリストされます。</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_save">保存</string>
+ <string name="api_settings_cancel">キャンセル</string>
+ <string name="api_settings_revoke">破棄されたアクセス</string>
+ <string name="api_settings_package_name">パッケージ名</string>
+ <string name="api_settings_package_signature">パッケージの署名 SHA-256</string>
+ <string name="api_register_text">表示されているアプリケーションはOpenPGP Keychainへのアクセスを要求しています。\nアクセスを許可しますか?\n\n注意: もしなぜスクリーンに表れたかわからないなら、アクセスを許可しないでください! あなたは\'登録済みアプリケーション\'スクリーンを使って、以降のアクセスを破棄するこもできます。</string>
+ <string name="api_register_allow">許可されたアクセス</string>
+ <string name="api_register_disallow">許可されないアクセス</string>
+ <string name="api_register_error_select_key">鍵を選択してください!</string>
+ <string name="api_select_pub_keys_missing_text">このユーザIDについて公開鍵が見付かりません:</string>
+ <string name="api_select_pub_keys_dublicates_text">このユーザIDについて1つ以上の公開鍵が存在します:</string>
+ <string name="api_select_pub_keys_text">受信者リストを確認してください!</string>
+ <string name="api_error_wrong_signature">署名チェックが失敗! 違うところからこのアプリをインストールしましたか? もし攻撃されてでなくそうであるなら、OpenPGP Keychainにあるこのアプリの登録を破棄し、再度アプリを登録してください。</string>
+ <!--Share-->
+ <string name="share_qr_code_dialog_title">QRコードで共有</string>
+ <string name="share_qr_code_dialog_start">すべてのQRコードを見る場合、\'次\' を押して一つ一つスキャンしてください。</string>
+ <string name="share_qr_code_dialog_fingerprint_text">指紋:</string>
+ <string name="share_qr_code_dialog_progress">%2$d の ID %1$d のQRコード</string>
+ <string name="share_nfc_dialog">NFCで共有</string>
+ <!--Key list-->
+ <plurals name="key_list_selected_keys">
+ <item quantity="other">%d の鍵を選択。</item>
+ </plurals>
+ <string name="key_list_empty_text1">すでにその鍵は存在しません...</string>
+ <string name="key_list_empty_text2">で始める</string>
+ <string name="key_list_empty_text3">もしくは</string>
+ <string name="key_list_empty_button_create">あなた所有の鍵を作る</string>
+ <string name="key_list_empty_button_import">鍵のインポート。</string>
+ <!--Key view-->
+ <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_import">鍵のインポート</string>
+ <string name="nav_secret_keys">自分の鍵</string>
+ <string name="nav_apps">登録済みのアプリ</string>
+ <string name="drawer_open">ナビゲーションドロワーを開く</string>
+ <string name="drawer_close">ナビゲーションドロワーを閉める</string>
+</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 b0d792708..72a8fdea8 100644
--- a/OpenPGP-Keychain/src/main/res/values-nl-rNL/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-nl-rNL/strings.xml
@@ -10,7 +10,6 @@
<string name="title_edit_key">Sleutel bewerken</string>
<string name="title_preferences">Instellingen</string>
<string name="title_api_registered_apps">Geregistreerde apps</string>
- <string name="title_key_server_preference">Instellingen sleutelserver</string>
<string name="title_change_pass_phrase">Wachtwoord wijzigen</string>
<string name="title_set_passphrase">Wachtwoord instellen</string>
<string name="title_send_email">E-mail verzenden...</string>
@@ -20,9 +19,7 @@
<string name="title_export_key">Sleutels exporteren</string>
<string name="title_export_keys">Sleutels exporteren</string>
<string name="title_key_not_found">Sleutel niet gevonden</string>
- <string name="title_key_server_query">Sleutelserver verzoek zenden</string>
<string name="title_unknown_signature_key">Onbekende handtekeningssleutel</string>
- <string name="title_sign_key">Sleutel ondertekenen</string>
<string name="title_help">Help</string>
<!--section-->
<string name="section_user_ids">Gebruikers-id\'s</string>
@@ -31,13 +28,8 @@
<string name="section_defaults">Standaard</string>
<string name="section_advanced">Geavanceerd</string>
<!--button-->
- <string name="btn_sign_to_clipboard">Ondertekenen (klembord)</string>
- <string name="btn_encrypt_to_clipboard">Versleutelen naar klembord</string>
- <string name="btn_encrypt_and_send">Versleutelen en verzenden...</string>
- <string name="btn_sign_and_send">Ondertekenen en verzenden...</string>
<string name="btn_sign">Ondertekenen</string>
<string name="btn_decrypt">Ontsleutelen</string>
- <string name="btn_verify">Verifiëren</string>
<string name="btn_select_encrypt_keys">Ontvangers selecteren</string>
<string name="btn_encrypt_file">Bestand versleutelen</string>
<string name="btn_save">Opslaan</string>
@@ -61,7 +53,6 @@
<string name="menu_create_key">Sleutel aanmaken</string>
<string name="menu_create_key_expert">Sleutel aanmaken (expert)</string>
<string name="menu_search">Zoeken</string>
- <string name="menu_key_server">Importeren uit sleutelserver</string>
<string name="menu_sign_key">Sleutel ondertekenen</string>
<string name="menu_beam_preferences">Beam-instellingen</string>
<!--label-->
@@ -82,8 +73,6 @@
<string name="label_passphrase_cache_ttl">Wachtwoordcache</string>
<string name="label_message_compression">Berichtcompressie</string>
<string name="label_file_compression">Bestandscompressie</string>
- <string name="label_force_v3_signature">V3-handtekeningen afdwingen</string>
- <string name="label_key_servers">Sleutelservers</string>
<string name="label_key_id">Sleutel-id</string>
<string name="label_creation">Aanmaak</string>
<string name="label_expiry">Verlopen</string>
@@ -149,9 +138,6 @@
<string name="no_keys_exported">Geen sleutels geëxporteerd.</string>
<string name="key_creation_el_gamal_info">Opmerking: alleen sub-sleutels ondersteunen ElGamal, en voor ElGamal wordt de dichtstbijzijnde sleutelgrootte van 1536, 2048, 4096 of 8192 gebruikt.</string>
<string name="key_not_found">Kan de sleutel %08X niet vinden.</string>
- <string name="unknown_signature_key_touch_to_look_up">Onbekende handtekening, tik om sleutel op te zoeken.</string>
- <string name="lookup_unknown_key">Onbekende sleutel %s, wilt u het bij een sleutelserver opvragen?</string>
- <string name="key_send_success">Sleutel succesvol verzonden naar server</string>
<string name="key_sign_success">Sleutel succesvol ondertekend</string>
<string name="list_empty">Lijst is leeg</string>
<string name="nfc_successfull">Sleutel succesvol verzonden met Beam</string>
@@ -189,13 +175,11 @@
<string name="progress_saving">opslaan...</string>
<string name="progress_importing">importeren...</string>
<string name="progress_exporting">exporteren...</string>
- <string name="progress_generating">sleutel genereren, een ogenblik geduld...</string>
<string name="progress_building_key">sleutel maken...</string>
<string name="progress_preparing_master_key">hoofdsleutel voorbereiden...</string>
<string name="progress_certifying_master_key">hoofdsleutel certificeren...</string>
<string name="progress_building_master_key">hoofdsleutelbos maken...</string>
<string name="progress_adding_sub_keys">sub-sleutels toevoegen...</string>
- <string name="progress_importing_secret_keys">privésleutels importeren...</string>
<string name="progress_extracting_signature_key">ondertekeningssleutel uitpakken...</string>
<string name="progress_extracting_key">sleutel uitpakken...</string>
<string name="progress_preparing_streams">streams voorbereiden...</string>
@@ -241,13 +225,11 @@
<string name="intent_send_encrypt">OpenPGP: versleutelen</string>
<string name="intent_send_decrypt">OpenPGP: ontsleutelen</string>
<!--Remote API-->
- <string name="api_no_apps">Geen geregistreerde apps</string>
<string name="api_settings_no_key">Geen sleutel geselecteerd</string>
<string name="api_settings_select_key">Sleutel selecteren</string>
<string name="api_settings_save">Opslaan</string>
<string name="api_settings_cancel">Annuleren</string>
<string name="api_settings_revoke">Toegang herroepen</string>
- <string name="api_register_text">De volgende app vraagt toegang to de KeyChain-API van OpenPGP\n\nAltijd toestaan?</string>
<string name="api_register_allow">Toegang toestaan</string>
<string name="api_register_disallow">Toegang weigeren</string>
<string name="api_register_error_select_key">Selecteert u a.u.b. een sleutel</string>
diff --git a/OpenPGP-Keychain/src/main/res/values-ru/strings.xml b/OpenPGP-Keychain/src/main/res/values-ru/strings.xml
index 91ec2d1af..cbc394766 100644
--- a/OpenPGP-Keychain/src/main/res/values-ru/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-ru/strings.xml
@@ -25,7 +25,7 @@
<string name="title_key_server_query">Запросить сервер ключей</string>
<string name="title_send_key">Загрузить на сервер ключей</string>
<string name="title_unknown_signature_key">Неизвестная подпись</string>
- <string name="title_sign_key">Ключ для подписи</string>
+ <string name="title_certify_key">Сертифицировать ключ</string>
<string name="title_key_details">Сведения о ключе</string>
<string name="title_help">Помощь</string>
<!--section-->
@@ -37,17 +37,16 @@
<string name="section_master_key">Основной ключ</string>
<string name="section_master_user_id">Владелец</string>
<string name="section_actions">Действия</string>
- <string name="section_signing_key">Ваш ключ, используемый для подписания</string>
+ <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_decrypt_verify">Расшифровать и проверить</string>
<!--button-->
- <string name="btn_sign_to_clipboard">Подписать (Буфер обмена)</string>
- <string name="btn_encrypt_to_clipboard">Зашифровать в Буфер обмена</string>
- <string name="btn_encrypt_and_send">Зашифровать и отправить...</string>
- <string name="btn_sign_and_send">Подписать и отправить...</string>
<string name="btn_sign">Подписать</string>
+ <string name="btn_certify">Сертифицировать</string>
<string name="btn_decrypt">Расшифровать</string>
- <string name="btn_verify">Проверить</string>
+ <string name="btn_decrypt_verify">Расшифровать и проверить</string>
<string name="btn_select_encrypt_keys">Выбрать получателей</string>
<string name="btn_encrypt_file">Зашифровать файл</string>
<string name="btn_save">Сохранить</string>
@@ -61,6 +60,9 @@
<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_lookup_key">Найти ключ</string>
<!--menu-->
<string name="menu_preferences">Настройки</string>
<string name="menu_help">Помощь</string>
@@ -109,7 +111,6 @@
<string name="label_passphrase_cache_ttl">Помнить пароль</string>
<string name="label_message_compression">Сжатие сообщения</string>
<string name="label_file_compression">Сжатие файла</string>
- <string name="label_force_v3_signature">Использовать V3 подписи</string>
<string name="label_key_servers">Серверы ключей</string>
<string name="label_key_id">ID ключа</string>
<string name="label_creation">Создан</string>
@@ -120,7 +121,7 @@
<string name="label_name">Имя</string>
<string name="label_comment">Комментарий</string>
<string name="label_email">Email</string>
- <string name="label_send_key">Загрузить подписанный ключ на сервер</string>
+ <string name="label_send_key">После сертификации загрузить ключ на сервер</string>
<string name="label_fingerprint">Отпечаток</string>
<string name="select_keys_button_default">Выбрать</string>
<plurals name="select_keys_button">
@@ -134,7 +135,8 @@
<string name="unknown_status"></string>
<string name="can_encrypt">шифрование</string>
<string name="can_sign">подпись</string>
- <string name="expired">годен до</string>
+ <string name="expired">просрочен</string>
+ <string name="revoked">отозван</string>
<plurals name="n_key_servers">
<item quantity="one">%d сервер ключей</item>
<item quantity="few">%d серверов ключей</item>
@@ -222,14 +224,13 @@
<item quantity="few">Найдено %d ключей.</item>
<item quantity="other">Найдено %d ключей.</item>
</plurals>
- <string name="unknown_signature_key_touch_to_look_up">Неизвестная подпись. Нажмите для выбора ключа.</string>
+ <string name="unknown_signature">Неизвестная подпись. Нажмите кнопку, что бы найти ключ.</string>
<plurals name="bad_keys_encountered">
<item quantity="one">%d плохой секретный ключ проигнорирован. Возможно, вы экспортируете с параметром\n--export-secret-subkeys\nВместо этого используйте\n--export-secret-keys</item>
<item quantity="few">%d плохих секретных ключей проигнорировано. Возможно, вы экспортируете с параметром\n--export-secret-subkeys\nВместо этого используйте\n--export-secret-keys\n</item>
<item quantity="other">%d плохих секретных ключей проигнорировано. Возможно, вы экспортируете с параметром\n--export-secret-subkeys\nВместо этого используйте\n--export-secret-keys\n</item>
</plurals>
- <string name="lookup_unknown_key">Неизвестный ключ %s. Хотите искать на сервере ключей?</string>
- <string name="key_send_success">Ключ успешно отправлен на сервер</string>
+ <string name="key_send_success">Ключ успешно загружен на сервер</string>
<string name="key_sign_success">Ключ успешно подписан</string>
<string name="list_empty">Список пуст!</string>
<string name="nfc_successfull">Ключ успешно передан через NFC!</string>
@@ -269,19 +270,25 @@
<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_can_not_delete_contact">нельзя удалить свой собственный контакт. Пожалуйста, удалите его в разделе \'Мои ключи\'!</string>
+ <string name="error_can_not_delete_contacts">это ваши собственные контакты, их нельзя удалить:\n%s</string>
+ <plurals name="error_can_not_delete_info">
+ <item quantity="one">Пожалуйста, удалите его в разделе \'Мои ключи\'!</item>
+ <item quantity="few">Пожалуйста, удалите их в разделе \'Мои ключи\'!</item>
+ <item quantity="other">Пожалуйста, удалите их в разделе \'Мои ключи\'!</item>
+ </plurals>
<!--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>
- <string name="progress_generating">создание ключа... на это нужно время...</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>
<string name="progress_building_master_key">создание основной связки...</string>
<string name="progress_adding_sub_keys">добавление доп. ключей...</string>
<string name="progress_saving_key_ring">сохранение ключа...</string>
- <string name="progress_importing_secret_keys">импорт секретных ключей...</string>
<plurals name="progress_exporting_key">
<item quantity="one">экспорт ключа...</item>
<item quantity="few">экспорт ключей...</item>
@@ -344,7 +351,7 @@
<string name="intent_send_encrypt">OpenPGP: Зашифровать</string>
<string name="intent_send_decrypt">OpenPGP: Расшифровать</string>
<!--Remote API-->
- <string name="api_no_apps">Нет связанных приложений!</string>
+ <string name="api_no_apps">Нет связанных программ!\n\nСторонние программы могут запросить доступ к OpenPGP Keychain, после чего они будут отражаться здесь.</string>
<string name="api_settings_show_advanced">Показать расширенные настройки</string>
<string name="api_settings_hide_advanced">Скрыть расширенные настройки</string>
<string name="api_settings_no_key">Ключ не выбран</string>
@@ -354,7 +361,7 @@
<string name="api_settings_revoke">Отозвать доступ</string>
<string name="api_settings_package_name">Наименование пакета</string>
<string name="api_settings_package_signature">SHA-256 подписи пакета</string>
- <string name="api_register_text">Приложение запрашивает доступ к OpenPGP Keychain API.\nРазрешить доступ?</string>
+ <string name="api_register_text">Данное приложение запрашивает доступ к OpenPGP Keychain.\nРазрешить доступ?\n\nВНИМАНИЕ: Если вы не знаете почему возник этот запрос, откажите в доступе!\nПозже вы можете отозвать право доступа в разделе \"Зарегистрированные программы\".</string>
<string name="api_register_allow">Разрешить доступ</string>
<string name="api_register_disallow">Запретить доступ</string>
<string name="api_register_error_select_key">Пожалуйста, выберите ключ!</string>
@@ -381,6 +388,9 @@
<string name="key_list_empty_button_import">Импортировать ключи</string>
<!--Key view-->
<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>
diff --git a/OpenPGP-Keychain/src/main/res/values-tr/strings.xml b/OpenPGP-Keychain/src/main/res/values-tr/strings.xml
index d9fe8713e..5bb5225b5 100644
--- a/OpenPGP-Keychain/src/main/res/values-tr/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-tr/strings.xml
@@ -7,14 +7,11 @@
<string name="title_create_key">Anahtar oluştur</string>
<string name="title_edit_key">Anahtarı düzenle</string>
<string name="title_preferences">Seçenekler</string>
- <string name="title_key_server_preference">Anahtar Sunucusu Seçenekleri</string>
<string name="title_import_keys">Anahtarları Al</string>
<string name="title_export_key">Anahtarı Ver</string>
<string name="title_export_keys">Anahtarları Ver</string>
<string name="title_key_not_found">Anahtar Bulunamadı</string>
- <string name="title_key_server_query">Anahtar Sunucusunu Sorgula</string>
<string name="title_unknown_signature_key">Bilinmeyen İmza Anahtarı</string>
- <string name="title_sign_key">Anahtarı İmzala</string>
<string name="title_key_details">Anahtar Detayları</string>
<string name="title_help">Yardım</string>
<!--section-->
@@ -24,21 +21,14 @@
<string name="section_defaults">Varsayılanlar</string>
<string name="section_advanced">Gelişmiş</string>
<string name="section_upload_key">Anahtar Yükle</string>
- <string name="section_key_server">Anahtar Sunucusu</string>
<!--button-->
- <string name="btn_sign_to_clipboard">İmzala (Pano)</string>
- <string name="btn_encrypt_to_clipboard">Panoya Şifrele</string>
- <string name="btn_encrypt_and_send">Şifrele ve gönder...</string>
- <string name="btn_sign_and_send">İmzala ve gönder...</string>
<string name="btn_sign">İmzala</string>
- <string name="btn_verify">Doğrula</string>
<string name="btn_select_encrypt_keys">Alıcıları Seç</string>
<string name="btn_save">Kaydet</string>
<string name="btn_do_not_save">İptal</string>
<string name="btn_delete">Sil</string>
<string name="btn_okay">Tamam</string>
<string name="btn_search">Ara</string>
- <string name="btn_export_to_server">Sunucuya Anahtar Yükle</string>
<string name="btn_next">İleri</string>
<string name="btn_back">Geri</string>
<!--menu-->
@@ -53,7 +43,6 @@
<string name="menu_create_key">Anahtar oluştur</string>
<string name="menu_create_key_expert">Anahtar oluştur (uzman)</string>
<string name="menu_search">Ara</string>
- <string name="menu_key_server">Anahtar sunucusundan al</string>
<string name="menu_copy_to_clipboard">Panoya kopyala</string>
<string name="menu_sign_key">Anahtarı imzala</string>
<string name="menu_key_edit_cancel">İptal</string>
@@ -64,7 +53,6 @@
<string name="label_passphrase_again">Tekrar</string>
<string name="label_algorithm">Algoritma</string>
<string name="label_asymmetric">Açık Anahtar</string>
- <string name="label_key_servers">Anahtar Sunucuları</string>
<string name="label_creation">Oluşturma</string>
<string name="label_usage">Kullanım</string>
<string name="label_key_size">Anahtar Boyutu</string>
@@ -97,7 +85,6 @@
<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_send_success">Anahtar sunucuya başarıyla gönderildi</string>
<string name="key_sign_success">Anahtar başarıyla imzalandı</string>
<string name="list_empty">Liste boş!</string>
<!--errors
@@ -112,7 +99,6 @@
<string name="progress_saving">kaydediliyor...</string>
<string name="progress_importing">alıyor...</string>
<string name="progress_exporting">veriyor...</string>
- <string name="progress_generating">anahtar üretiliyor, bu biraz süre alabilir...</string>
<string name="progress_building_key">anahtar oluşturuluyor...</string>
<string name="progress_preparing_signature">imza hazırlanıyor...</string>
<string name="progress_generating_signature">imza oluşturuluyor...</string>
diff --git a/OpenPGP-Keychain/src/main/res/values-uk/strings.xml b/OpenPGP-Keychain/src/main/res/values-uk/strings.xml
index ac40311b0..47cbb38b3 100644
--- a/OpenPGP-Keychain/src/main/res/values-uk/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-uk/strings.xml
@@ -22,10 +22,10 @@
<string name="title_export_key">Експортувати ключ</string>
<string name="title_export_keys">Експортувати ключі</string>
<string name="title_key_not_found">Ключ не знайдено</string>
- <string name="title_key_server_query">Сервер запиту ключа</string>
+ <string name="title_key_server_query">Сервер ключа запиту</string>
<string name="title_send_key">Завантажити на сервер ключів</string>
<string name="title_unknown_signature_key">Невідомий підпис ключа</string>
- <string name="title_sign_key">Підписати ключ</string>
+ <string name="title_certify_key">Сертифікувати ключ</string>
<string name="title_key_details">Подробиці про ключ</string>
<string name="title_help">Довідка</string>
<!--section-->
@@ -37,17 +37,16 @@
<string name="section_master_key">Основний ключ</string>
<string name="section_master_user_id">ІД основного ключа</string>
<string name="section_actions">Дії</string>
- <string name="section_signing_key">Ваш ключ, використаний для підпису</string>
+ <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_decrypt_verify">Розшифрувати і Перевірити</string>
<!--button-->
- <string name="btn_sign_to_clipboard">Підпис (буфер обміну)</string>
- <string name="btn_encrypt_to_clipboard">Зашифрувати у буфер обміну</string>
- <string name="btn_encrypt_and_send">Шифрувати і надіслати…</string>
- <string name="btn_sign_and_send">Підписати і надіслати…</string>
<string name="btn_sign">Підписати</string>
+ <string name="btn_certify">Сертифікувати</string>
<string name="btn_decrypt">Розшифрувати</string>
- <string name="btn_verify">Перевірити</string>
+ <string name="btn_decrypt_verify">Розшифрувати і Перевірити</string>
<string name="btn_select_encrypt_keys">Вибрати одержувачів</string>
<string name="btn_encrypt_file">Шифрувати файл</string>
<string name="btn_save">Зберегти</string>
@@ -61,6 +60,9 @@
<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_lookup_key">Шукати ключ</string>
<!--menu-->
<string name="menu_preferences">Параметри</string>
<string name="menu_help">Довідка</string>
@@ -109,7 +111,7 @@
<string name="label_passphrase_cache_ttl">Кеш парольної фрази</string>
<string name="label_message_compression">Стиснення повідомлення</string>
<string name="label_file_compression">Стиснення файлу</string>
- <string name="label_force_v3_signature">Примусові підписи V3</string>
+ <string name="label_force_v3_signature">Примусово старі підписи OpenPGPv3</string>
<string name="label_key_servers">Сервери ключів</string>
<string name="label_key_id">ІД ключа</string>
<string name="label_creation">Створення</string>
@@ -120,7 +122,7 @@
<string name="label_name">Назва</string>
<string name="label_comment">Коментар</string>
<string name="label_email">Ел. пошта</string>
- <string name="label_send_key">Завантажити ключ на вибраний сервер ключів після підписування</string>
+ <string name="label_send_key">Завантажити ключ до вибраного сервера ключів після сертифікації</string>
<string name="label_fingerprint">Відбиток</string>
<string name="select_keys_button_default">Вибрати</string>
<plurals name="select_keys_button">
@@ -135,6 +137,7 @@
<string name="can_encrypt">можна зашифрувати</string>
<string name="can_sign">можна підписати</string>
<string name="expired">закінчився</string>
+ <string name="revoked">скасовано</string>
<plurals name="n_key_servers">
<item quantity="one">%d сервер ключів</item>
<item quantity="few">%d сервери ключів</item>
@@ -222,14 +225,13 @@
<item quantity="few">Знайдено %d ключі.</item>
<item quantity="other">Знайдено %d ключів.</item>
</plurals>
- <string name="unknown_signature_key_touch_to_look_up">Невідомий підпис, натисніть, щоб шукати ключ.</string>
+ <string name="unknown_signature">Невідомий підпис, натисніть кнопку для пошуку втраченого ключа.</string>
<plurals name="bad_keys_encountered">
<item quantity="one">%d поганий секретний ключ проігнорований. Можливо ви експортували з параметром\n --export-secret-subkeys\nЗробіть ваш експорт з \n --export-secret-keys\nнатомість.</item>
<item quantity="few">%d погані секретні ключі проігноровані. Можливо ви експортували з параметром\n --export-secret-subkeys\nЗробіть ваш експорт з \n --export-secret-keys\nнатомість.</item>
<item quantity="other">%d поганих секретних ключів проігноровано. Можливо ви експортували з параметром\n --export-secret-subkeys\nЗробіть ваш експорт з \n --export-secret-keys\nнатомість.</item>
</plurals>
- <string name="lookup_unknown_key">Невідомий ключ %s, ви хочете віднайти його на сервері ключів?</string>
- <string name="key_send_success">Успішно надіслано ключ на сервер</string>
+ <string name="key_send_success">Успішно завантажено ключ на сервер</string>
<string name="key_sign_success">Успішно підписаний ключ</string>
<string name="list_empty">Цей список - порожній!</string>
<string name="nfc_successfull">Успішно відправлений ключ з NFC променем!</string>
@@ -260,6 +262,7 @@
<string name="error_no_signature_key">не подано ключ підпису</string>
<string name="error_invalid_data">недійсні дані шифрування</string>
<string name="error_corrupt_data">пошкодити дані</string>
+ <string name="error_integrity_check_failed">Невдала перевірка цілісності! Дані вже змінено!</string>
<string name="error_no_symmetric_encryption_packet">не знайдено пакунок з симетричним шифруванням</string>
<string name="error_wrong_passphrase">помилкова парольна фраза</string>
<string name="error_saving_keys">помилка збереження деяких ключів</string>
@@ -269,19 +272,25 @@
<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_can_not_delete_contact">ви не можете вилучити цей контакт, тому що він ваш власний.</string>
+ <string name="error_can_not_delete_contacts">ви не можете вилучити наступні контакти, тому що вони - ваші власні:\n%s</string>
+ <plurals name="error_can_not_delete_info">
+ <item quantity="one">Будь ласка, вилучіть його з екрану „Мої ключі“!</item>
+ <item quantity="few">Будь ласка, вилучіть їх з екрану „Мої ключі“!</item>
+ <item quantity="other">Будь ласка, вилучіть їх з екрану „Мої ключі“!</item>
+ </plurals>
<!--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>
- <string name="progress_generating">генерується ключ, зачекайте…</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>
<string name="progress_building_master_key">побудова основного кільця…</string>
<string name="progress_adding_sub_keys">додавання підключів…</string>
<string name="progress_saving_key_ring">зберігається ключ…</string>
- <string name="progress_importing_secret_keys">імпортуються секретні ключі…</string>
<plurals name="progress_exporting_key">
<item quantity="one">експортується ключ…</item>
<item quantity="few">експортуються ключі…</item>
@@ -344,7 +353,7 @@
<string name="intent_send_encrypt">OpenPGP: зашифрувати</string>
<string name="intent_send_decrypt">OpenPGP: розшифрувати</string>
<!--Remote API-->
- <string name="api_no_apps">Немає зареєстрованих програм!</string>
+ <string name="api_no_apps">Нема зареєстрованих програм!\n\nСтороні програми можуть вимагати доступ до OpenPGP Keychain. Після надання доступу вони будуть наведені тут.</string>
<string name="api_settings_show_advanced">Показати додаткові налаштування</string>
<string name="api_settings_hide_advanced">Приховати додаткові налаштування</string>
<string name="api_settings_no_key">Не вибрано ключа</string>
@@ -354,7 +363,7 @@
<string name="api_settings_revoke">Відкликати доступ</string>
<string name="api_settings_package_name">Назва пакунку</string>
<string name="api_settings_package_signature">SHA-256 підписку пакунку</string>
- <string name="api_register_text">Наступна програм запитала доступ до OpenPGP Keychain API.\n\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>
<string name="api_register_error_select_key">Будь ласка, виберіть ключ!</string>
@@ -381,6 +390,9 @@
<string name="key_list_empty_button_import">імпортуюся ключі.</string>
<!--Key view-->
<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>
diff --git a/OpenPGP-Keychain/src/main/res/values-zh/strings.xml b/OpenPGP-Keychain/src/main/res/values-zh/strings.xml
index 6bb115049..30d6a3518 100644
--- a/OpenPGP-Keychain/src/main/res/values-zh/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-zh/strings.xml
@@ -1,26 +1,164 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<!--title-->
+ <string name="title_encrypt">加密</string>
+ <string name="title_decrypt">解密</string>
+ <string name="title_key_server_preference">密钥服务器偏好</string>
+ <string name="title_key_server_query">查询密钥服务器</string>
+ <string name="title_send_key">上传到密钥服务器</string>
<!--section-->
+ <string name="section_master_key">主密钥</string>
+ <string name="section_key_server">密钥服务器</string>
+ <string name="section_decrypt_verify">解密并验证</string>
<!--button-->
+ <string name="btn_decrypt_verify">解密并验证</string>
+ <string name="btn_clipboard">剪贴板</string>
<!--menu-->
+ <string name="menu_help">帮助</string>
+ <string name="menu_import">导入</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_share">分享</string>
+ <string name="menu_copy_to_clipboard">复制到剪贴板</string>
+ <string name="menu_sign_key">签署密钥</string>
+ <string name="menu_key_edit_cancel">取消</string>
+ <string name="menu_encrypt_to">加密到...</string>
<!--label-->
+ <string name="label_sign">签署</string>
+ <string name="label_message">讯息</string>
+ <string name="label_file">文件</string>
+ <string name="label_no_passphrase">没有密语</string>
+ <string name="label_passphrase">密语</string>
+ <string name="label_algorithm">算法</string>
+ <string name="label_select_public_keys">收件人</string>
+ <string name="label_delete_after_encryption">加密后删除</string>
+ <string name="label_delete_after_decryption">解密后删除</string>
+ <string name="label_encryption_algorithm">加密算法</string>
+ <string name="label_hash_algorithm">哈希算法</string>
+ <string name="label_asymmetric">公共密钥</string>
+ <string name="label_symmetric">密语</string>
+ <string name="label_passphrase_cache_ttl">密语缓存</string>
+ <string name="label_comment">注解</string>
+ <string name="label_email">电子邮件</string>
+ <string name="label_fingerprint">指纹</string>
<string name="unknown_status"></string>
+ <string name="can_encrypt">可以加密</string>
+ <string name="can_sign">可以签署</string>
+ <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>
+ <string name="choice_5mins">5分钟</string>
+ <string name="choice_10mins">10分钟</string>
+ <string name="choice_20mins">20分钟</string>
+ <string name="choice_40mins">40分钟</string>
+ <string name="choice_1hour">1小时</string>
+ <string name="choice_2hours">2小时</string>
+ <string name="choice_4hours">4小时</string>
+ <string name="choice_8hours">8小时</string>
+ <string name="choice_forever">永远</string>
+ <string name="filemanager_title_open">打开...</string>
+ <string name="warning">警告</string>
+ <string name="error">错误</string>
<!--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>
+ <string name="key_exported">成功地导出了1个密钥</string>
+ <string name="no_keys_exported">没有密钥被导出</string>
+ <string name="key_send_success">成功地上传了密钥到服务器</string>
+ <string name="list_empty">这个列表是空的!</string>
<!--errors
no punctuation, all lowercase,
they will be put after "error_message", e.g. "Error: file not found"-->
+ <string name="error_file_not_found">没有找到文件</string>
+ <string name="error_external_storage_not_ready">外置存储没有准备好</string>
+ <string name="error_invalid_email">无效的email \'%s\'</string>
+ <string name="error_key_size_minimum512bit">密钥的大小必须至少512位</string>
+ <string name="error_unknown_algorithm_choice">位置的算法选择</string>
+ <string name="error_user_id_needs_a_name">你需要指定一个名字</string>
+ <string name="error_user_id_needs_an_email_address">你需要指定一个电子邮件地址</string>
+ <string name="error_key_needs_a_user_id">需要至少一个用户id</string>
+ <string name="error_main_user_id_must_not_be_empty">主用户id不能是空的</string>
+ <string name="error_key_needs_master_key">需要至少一个主密钥</string>
+ <string name="error_signature_failed">签名失败</string>
+ <string name="error_no_signature_passphrase">没有提供密语</string>
+ <string name="error_no_signature_key">没有提供密钥</string>
+ <string name="error_invalid_data">不是有效的加密数据</string>
+ <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>
+ <string name="progress_building_key">建立密钥</string>
+ <string name="progress_preparing_master_key">正在准备主密钥</string>
+ <string name="progress_verifying_signature">正在验证签名...</string>
+ <string name="progress_signing">正在签名...</string>
+ <string name="progress_reading_data">正在读取数据</string>
+ <string name="progress_finding_key">正在查找密钥</string>
+ <string name="progress_querying">正在查询</string>
<!--action strings-->
<!--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">快</string>
+ <string name="compression_very_slow">非常慢</string>
<!--Help-->
+ <string name="help_tab_start">开始</string>
+ <string name="help_tab_changelog">更新日志</string>
+ <string name="help_tab_about">关于</string>
+ <string name="help_about_version">版本:</string>
<!--Import-->
+ <string name="import_from_clipboard">从剪贴板导入</string>
+ <string name="import_qr_code_finished">二维码扫描完成!</string>
+ <string name="import_nfc_help_button">帮助</string>
<!--Intent labels-->
+ <string name="intent_decrypt_file">OpenPGP: 解密文件</string>
+ <string name="intent_import_key">OpenPGP: 导入密钥</string>
+ <string name="intent_send_encrypt">OpenPGP: 加密</string>
+ <string name="intent_send_decrypt">OpenPGP: 解密</string>
<!--Remote API-->
+ <string name="api_settings_show_advanced">显示高级设置</string>
+ <string name="api_settings_hide_advanced">隐藏高级设置</string>
+ <string name="api_settings_select_key">选择密钥</string>
+ <string name="api_settings_save">保存</string>
+ <string name="api_settings_cancel">取消</string>
+ <string name="api_settings_revoke">撤销访问</string>
+ <string name="api_register_allow">允许访问</string>
+ <string name="api_register_disallow">不允许访问</string>
+ <string name="api_register_error_select_key">请选择一个密钥</string>
+ <string name="api_select_pub_keys_text">请重审收件人列表</string>
<!--Share-->
+ <string name="share_qr_code_dialog_fingerprint_text">指纹:</string>
+ <string name="share_nfc_dialog">使用NFC分享</string>
<!--Key list-->
+ <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>
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values/static_strings.xml b/OpenPGP-Keychain/src/main/res/values/static_strings.xml
index 3c9cf6673..faf1e687a 100644
--- a/OpenPGP-Keychain/src/main/res/values/static_strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values/static_strings.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="app_name" translatable="false">OpenPGP Keychain</string>
+ <string name="app_name" translatable="false">OpenKeychain</string>
</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 64c9be32d..861a50eaa 100644
--- a/OpenPGP-Keychain/src/main/res/values/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values/strings.xml
@@ -13,7 +13,7 @@
<string name="title_edit_key">Edit Key</string>
<string name="title_preferences">Preferences</string>
<string name="title_api_registered_apps">Registered Applications</string>
- <string name="title_key_server_preference">Key Server Preference</string>
+ <string name="title_key_server_preference">Keyserver Preference</string>
<string name="title_change_pass_phrase">Change Passphrase</string>
<string name="title_set_passphrase">Set Passphrase</string>
<string name="title_send_email">"Send Mail…"</string>
@@ -23,10 +23,10 @@
<string name="title_export_key">Export Key</string>
<string name="title_export_keys">Export Keys</string>
<string name="title_key_not_found">Key Not Found</string>
- <string name="title_key_server_query">Query Key Server</string>
- <string name="title_send_key">Upload to Key Server</string>
+ <string name="title_key_server_query">Query Keyserver</string>
+ <string name="title_send_key">Upload to Keyserver</string>
<string name="title_unknown_signature_key">Unknown Signature Key</string>
- <string name="title_sign_key">Sign Key</string>
+ <string name="title_certify_key">Certify Key</string>
<string name="title_key_details">Key Details</string>
<string name="title_help">Help</string>
@@ -39,14 +39,15 @@
<string name="section_master_key">Master Key</string>
<string name="section_master_user_id">Master User ID</string>
<string name="section_actions">Actions</string>
- <string name="section_signing_key">Your Key used for Signing</string>
+ <string name="section_certification_key">Your Key used for certification</string>
<string name="section_upload_key">Upload Key</string>
- <string name="section_key_server">Key Server</string>
+ <string name="section_key_server">Keyserver</string>
<string name="section_encrypt_and_or_sign">Encrypt and/or Sign</string>
<string name="section_decrypt_verify">Decrypt and Verify</string>
<!-- button -->
<string name="btn_sign">Sign</string>
+ <string name="btn_certify">Certify</string>
<string name="btn_decrypt">Decrypt</string>
<string name="btn_decrypt_verify">Decrypt and Verify</string>
<string name="btn_select_encrypt_keys">Select Recipients</string>
@@ -59,12 +60,12 @@
<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 Key Server</string>
+ <string name="btn_export_to_server">Upload To Keyserver</string>
<string name="btn_next">Next</string>
<string name="btn_back">Back</string>
<string name="btn_clipboard">Clipboard</string>
<string name="btn_share">Share with…</string>
-
+ <string name="btn_lookup_key">Lookup key</string>
<!-- menu -->
<string name="menu_preferences">Settings</string>
@@ -79,8 +80,8 @@
<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 key server</string>
- <string name="menu_update_key">Update from key server</string>
+ <string name="menu_key_server">Import from keyserver</string>
+ <string name="menu_update_key">Update from keyserver</string>
<string name="menu_export_key_to_server">Upload to key server</string>
<string name="menu_share">Share</string>
<string name="menu_share_title_fingerprint">Share fingerprint…</string>
@@ -115,8 +116,8 @@
<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>
- <string name="label_force_v3_signature">Force V3 Signatures</string>
- <string name="label_key_servers">Key Servers</string>
+ <string name="label_force_v3_signature">Force old OpenPGPv3 Signatures</string>
+ <string name="label_key_servers">Keyservers</string>
<string name="label_key_id">Key ID</string>
<string name="label_creation">Creation</string>
<string name="label_expiry">Expiry</string>
@@ -126,7 +127,7 @@
<string name="label_name">Name</string>
<string name="label_comment">Comment</string>
<string name="label_email">Email</string>
- <string name="label_send_key">Upload key to selected key server after signing</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>
@@ -142,10 +143,11 @@
<string name="can_encrypt">can encrypt</string>
<string name="can_sign">can sign</string>
<string name="expired">expired</string>
+ <string name="revoked">revoked</string>
<plurals name="n_key_servers">
- <item quantity="one">%d key server</item>
- <item quantity="other">%d key servers</item>
+ <item quantity="one">%d keyserver</item>
+ <item quantity="other">%d keyservers</item>
</plurals>
<string name="fingerprint">Fingerprint:</string>
@@ -235,15 +237,14 @@
<item quantity="other">Found %d keys.</item>
</plurals>
- <string name="unknown_signature_key_touch_to_look_up">Unknown signature, touch to look up key.</string>
+ <string name="unknown_signature">Unknown signature, click button to lookup the missing key.</string>
<plurals name="bad_keys_encountered">
<item quantity="one">%d bad secret key ignored. Perhaps you exported with the option\n --export-secret-subkeys\nMake sure you export with\n --export-secret-keys\ninstead.</item>
<item quantity="other">%d bad secret keys ignored. Perhaps you exported with the option\n --export-secret-subkeys\nMake sure you export with\n --export-secret-keys\ninstead.</item>
</plurals>
- <string name="lookup_unknown_key">Unknown key %s, do you want to try finding it on a keyserver?</string>
- <string name="key_send_success">Successfully sent key to server</string>
+ <string name="key_send_success">Successfully uploaded key to server</string>
<string name="key_sign_success">Successfully signed key</string>
<string name="list_empty">This list is empty!</string>
<string name="nfc_successfull">Successfully sent key with NFC Beam!</string>
@@ -277,6 +278,7 @@
<string name="error_no_signature_key">no signature key given</string>
<string name="error_invalid_data">not valid encryption data</string>
<string name="error_corrupt_data">corrupt data</string>
+ <string name="error_integrity_check_failed">integrity check failed! Data has been modified!</string>
<string name="error_no_symmetric_encryption_packet">couldn\'t find a packet with symmetric encryption</string>
<string name="error_wrong_passphrase">wrong passphrase</string>
<string name="error_saving_keys">error saving some keys</string>
@@ -287,13 +289,19 @@
<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>
+ <plurals name="error_can_not_delete_info">
+ <item quantity="one">Please delete it from the \'My Keys\' screen!</item>
+ <item quantity="other">Please delete them from the \'My Keys\' screen!</item>
+ </plurals>
<!-- progress dialogs, usually ending in '…' -->
<string name="progress_done">done.</string>
<string name="progress_saving">saving…</string>
<string name="progress_importing">importing…</string>
<string name="progress_exporting">exporting…</string>
- <string name="progress_generating">generating key, this can take a while…</string>
+ <string name="progress_generating">generating key, this can take up to 3 minutes…</string>
<string name="progress_building_key">building key…</string>
<string name="progress_preparing_master_key">preparing master key…</string>
<string name="progress_certifying_master_key">certifying master key…</string>
@@ -365,13 +373,13 @@
<string name="import_clipboard_button">Get key from clipboard</string>
<!-- Intent labels -->
- <string name="intent_decrypt_file">OpenPGP: Decrypt File</string>
- <string name="intent_import_key">OpenPGP: Import Key</string>
- <string name="intent_send_encrypt">OpenPGP: Encrypt</string>
- <string name="intent_send_decrypt">OpenPGP: Decrypt</string>
+ <string name="intent_decrypt_file">OpenKeychain: Decrypt File</string>
+ <string name="intent_import_key">OpenKeychain: Import Key</string>
+ <string name="intent_send_encrypt">OpenKeychain: Encrypt</string>
+ <string name="intent_send_decrypt">OpenKeychain: Decrypt</string>
<!-- Remote API -->
- <string name="api_no_apps">No registered applications!</string>
+ <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_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>
@@ -381,14 +389,14 @@
<string name="api_settings_revoke">Revoke access</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_register_text">The following application requests access to OpenPGP Keychain\'s API.\n\nAllow permanent access?</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>
<string name="api_register_error_select_key">Please select a key!</string>
<string name="api_select_pub_keys_missing_text">No public keys were found for these user ids:</string>
<string name="api_select_pub_keys_dublicates_text">More than one public key exist for these user ids:</string>
<string name="api_select_pub_keys_text">Please review the list of recipients!</string>
- <string name="api_error_wrong_signature">Signature check failed! Have you installed this app from a different source? If you are sure that this is not an attack, revoke this app\'s registration in OpenPGP Keychain and then register the app again.</string>
+ <string name="api_error_wrong_signature">Signature check failed! Have you installed this app from a different source? If you are sure that this is not an attack, revoke this app\'s registration in OpenKeychain and then register the app again.</string>
<!-- Share -->
<string name="share_qr_code_dialog_title">Share with QR Code</string>
@@ -411,6 +419,9 @@
<!-- Key view -->
<string name="key_view_action_encrypt">Encrypt to this contact</string>
+ <string name="key_view_action_certify">Certify this contact\'s key</string>
+ <string name="key_view_tab_main">Info</string>
+ <string name="key_view_tab_certs">Certifications</string>
<!-- Navigation Drawer -->
<string name="nav_contacts">Contacts</string>
diff --git a/README.md b/README.md
index c9894ce49..3a8c81b74 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# OpenPGP Keychain (for Android)
+# OpenKeychain (for Android)
-OpenPGP Keychain is an OpenPGP implementation for Android.
+OpenKeychain is an OpenPGP implementation for Android.
The development began as a fork of Android Privacy Guard (APG).
see http://sufficientlysecure.org/keychain
@@ -17,10 +17,10 @@ Translations are managed at Transifex, please contribute there at https://www.tr
2. Lookout for interesting issues on our issue page at Github: https://github.com/openpgp-keychain/openpgp-keychain/issues
3. Tell us about your plans on the mailinglist
4. Read this README, especially the notes about coding style
-5. Fork OpenPGP Keychain and contribute code (the best part ;) )
+5. Fork OpenKeychain and contribute code (the best part ;) )
6. Open a pull request on Github. I will help with occuring problems and merge your changes back into the main project.
-I am happy about every code contribution and appreciate your effort to help us developing OpenPGP Keychain!
+I am happy about every code contribution and appreciate your effort to help us developing OpenKeychain!
## Development
@@ -40,7 +40,7 @@ Select everything for the newest SDK
### Build API Demo with Gradle
1. Follow 1-3 from above
-2. Change to API Demo directory ``cd OpenPGP-Keychain-API-Demo``
+2. Change to API Demo directory ``cd OpenPGP-Keychain-API``
3. Execute ``./gradlew build``
### Development with Android Studio
@@ -48,7 +48,9 @@ Select everything for the newest SDK
I am using the newest [Android Studio](http://developer.android.com/sdk/installing/studio.html) for development. Development with Eclipse is currently not possible because I am using the new [project structure](http://developer.android.com/sdk/installing/studio-tips.html).
1. Clone the project from github
-2. From Android Studio: File -> Import Project -> Select the cloned top folder
+2. From Android Studio: File -> Import Project -> ...
+ * Select the cloned top folder if you want to develop on the main project
+ * Select the "OpenPGP-Keychain-API" folder if you want to develop on the API example
3. Import project from external model -> choose Gradle
## Keychain API
@@ -62,7 +64,7 @@ To do automatic encryption/decryption/sign/verify use the OpenPGP Remote API.
* ``android.intent.action.VIEW`` connected to .gpg and .asc files: Import Key and Decrypt
* ``android.intent.action.SEND`` connected to all mime types (text/plain and every binary data like files and images): Encrypt and Decrypt
-#### OpenPGP Keychain Intent actions:
+#### OpenKeychain Intent actions:
* ``org.sufficientlysecure.keychain.action.ENCRYPT``
* To encrypt or sign text, use extra ``text`` (type: ``String``)
@@ -74,19 +76,18 @@ To do automatic encryption/decryption/sign/verify use the OpenPGP Remote API.
* ``org.sufficientlysecure.keychain.action.IMPORT_KEY``
* Extras: ``key_bytes`` (type: ``byte[]``)
* or set data ``Uri`` (``intent.setData()``) pointing to a file
-* ``org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_KEY_SERVER``
+* ``org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_KEYSERVER``
* Extras: ``query`` (type: ``String``)
* or ``fingerprint`` (type: ``String``)
* ``org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE``
* without extras, starts Barcode Scanner to get QR Code
-#### OpenPGP Keychain special registered Intents:
-* ``android.intent.action.VIEW`` with URIs following the ``openpgp4fpr`` schema. For example: ``openpgp4fpr:718C070100012282``. This is used in QR Codes, but could also be embedded into your website. (compatible with Monkeysphere's and Guardian Project's QR Codes)
+#### OpenKeychain special registered Intents:
+* ``android.intent.action.VIEW`` with URIs following the ``openpgp4fpr`` schema. For example: ``openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282``. This is used in QR Codes, but could also be embedded into your website. (compatible with Monkeysphere's and Guardian Project's QR Codes)
* NFC (``android.nfc.action.NDEF_DISCOVERED``) on mime type ``application/pgp-keys`` (as specified in http://tools.ietf.org/html/rfc3156, section 7)
### OpenPGP Remote API
-To do asyncronous fast encryption/decryption/sign/verify operations bind to the OpenPGP remote service.
-The API Demo contains all required AIDL files and a demo activity.
+To do fast encryption/decryption/sign/verify operations without user interaction bind to the OpenPGP remote service.
#### Try out the API
Keychain: https://play.google.com/store/apps/details?id=org.sufficientlysecure.keychain
@@ -97,45 +98,25 @@ All apps wanting to use this generic API
just need to include the AIDL files and connect to the service. Other
OpenPGP apps can implement a service based on this AIDL definition.
-The API is designed to be as easy as possible to use by apps like
-K-9 Mail. The service definition defines
-sign/encrypt/signAndEncrypt/decryptAndVerify [1].
+The API is designed to be as easy as possible to use by apps like K-9 Mail.
+The service definition defines sign, encrypt, signAndEncrypt, decryptAndVerify, and getKeyIds.
-As can be seen the apps themselves never need handle key ids directly.
-Only user ids (emails) are used to define recipients. If more than one
-pub key exists for an email, OpenPGP Keychain will handle the problem by
-showing a selection screen.
+As can be seen in the API Demo, the apps themselves never need to handle key ids directly.
+You can use user ids (emails) to define recipients.
+If more than one public key exists for an email, OpenKeychain will handle the problem by showing a selection screen. Additionally, it is also possible to use key ids.
-Also app devs never need to fiddle with private keys. On first
-operation, OpenPGP Keychain shows an activity to allow or disallow
-access, while also allowing to choose the private key used for this app.
-Please try the Demo app out to see how it works [4].
+Also app devs never need to fiddle with private keys.
+On first operation, OpenKeychain shows an activity to allow or disallow access, while also allowing to choose the private key used for this app.
+Please try the Demo app out to see how it works.
#### Integration
-The API is defined as AIDL interfaces in org.openintents.openpgp packge
-[2]. All files from [2] needs to be included in the project.
+Copy the api library from "libraries/keychain-api-library" to your project and add it as an dependency to your gradle build.
+Inspect the ode found in "OpenPGP-Keychain-API" to understand how to use the API.
-Using the OpenPgpServiceConnection.java [3] you can choose to which
-OpenPGP provider you want to connect (other pgp apps can implement the
-interfaces). They can be queried as shown in the demo app (see [3] how
-to query). If other OpenPGP apps implement the service, no additional
-code is required in k9mail per provider. See [3] for a complete example
-for integration.
-
-[1] https://github.com/openpgp-keychain/openpgp-keychain/blob/master/OpenPGP-Keychain-API-Demo/src/org/openintents/openpgp/IOpenPgpService.aidl
-[2] https://github.com/openpgp-keychain/openpgp-keychain/tree/master/OpenPGP-Keychain-API-Demo/src/org/openintents/openpgp
-[3] https://github.com/openpgp-keychain/openpgp-keychain/blob/master/OpenPGP-Keychain-API-Demo/src/org/openintents/openpgp/OpenPgpServiceConnection.java
-[3] https://github.com/openpgp-keychain/openpgp-keychain/blob/master/OpenPGP-Keychain-API-Demo/src/org/sufficientlysecure/keychain/demo/OpenPgpProviderActivity.java
-[4] https://play.google.com/store/apps/details?id=org.sufficientlysecure.keychain.demo
-
-## Extended Remote API
-
-TODO
## Libraries
-
### ZXing Barcode Scanner Android Integration
Classes can be found under "libraries/zxing-android-integration/".
@@ -146,14 +127,14 @@ Classes can be found under "libraries/zxing-android-integration/".
### ZXing
Classes can be found under "libraries/zxing/".
-ZXing classes were extracted from the ZXing library (http://code.google.com/p/zxing/).
+ZXing classes were extracted from the ZXing library (https://github.com/zxing/zxing).
Only classes related to QR Code generation are utilized.
### Bouncy Castle
#### Spongy Castle
-Spongy Castle is the stock Bouncy Castle libraries with a couple of small changes to make it work on Android. OpenPGP Keychain uses a forked version with some small changes. These changes will been sent to Bouncy Castle, and Spongy Castle will be used again when they have filtered down.
+Spongy Castle is the stock Bouncy Castle libraries with a couple of small changes to make it work on Android. OpenKeychain uses a forked version with some small changes. These changes will been sent to Bouncy Castle, and Spongy Castle will be used again when they have filtered down.
see
* Fork: https://github.com/openpgp-keychain/spongycastle
@@ -167,6 +148,7 @@ see
#### Documentation
* Documentation project at http://www.cryptoworkshop.com/guide/
* Tests in https://github.com/bcgit/bc-java/tree/master/pg/src/test/java/org/bouncycastle/openpgp/test
+* Examples in https://github.com/bcgit/bc-java/tree/master/pg/src/main/java/org/bouncycastle/openpgp/examples
* Mailinglist Archive at http://bouncy-castle.1462172.n4.nabble.com/Bouncy-Castle-Dev-f1462173.html
@@ -174,13 +156,12 @@ see
### Gradle Build System
-We try to make our builds as [reproducible/deterministic](https://blog.torproject.org/blog/deterministic-builds-part-one-cyberwar-and-global-compromise) as possible.
-This is also a key requirement to be part of F-Droid.
+We try to make our builds as [reproducible/deterministic](https://blog.torproject.org/blog/deterministic-builds-part-one-cyberwar-and-global-compromise) as possible.
When changing build files or dependencies, respect the following requirements:
-- No precompiled libraries. All libraries should be provided as sourcecode in "libraries" folder
-- No dependencies from Maven
-- Always use a fixed Android Gradle plugin version not a dynamic one, e.g. ``0.7.3`` instead of ``0.7.+``
-- Commit the corresponding gradle wrapper version to the repository
+* No precompiled libraries. All libraries should be provided as sourcecode in "libraries" folder (you never know what pre-compiled jar files really contain! The library files are currently directly commited, because git submodules/git subtree are too much of a hassle for new contributors. This could change in the future!)
+* No dependencies from Maven (also a soft requirement for inclusion in [F-Droid](https://f-droid.org))
+* Always use a fixed Android Gradle plugin version not a dynamic one, e.g. ``0.7.3`` instead of ``0.7.+`` (allows offline builds without lookups for new versions, also some minor Android plugin versions had serious issues, i.e. [0.7.2 and 0.8.1](http://tools.android.com/tech-docs/new-build-system))
+* Commit the corresponding [Gradle wrapper](http://www.gradle.org/docs/current/userguide/gradle_wrapper.html) to the repository (allows easy building for new contributors without the need to install the required Gradle version using a package manager)
### Translations
@@ -248,7 +229,7 @@ Some parts (older parts and some libraries are Apache License v2, MIT X11 Licens
Apache License v2
* ZXing
- http://code.google.com/p/zxing/
+ https://github.com/zxing/zxing
Apache License v2
* StickyListHeaders
@@ -259,6 +240,9 @@ Some parts (older parts and some libraries are Apache License v2, MIT X11 Licens
https://github.com/Bearded-Hen/Android-Bootstrap
MIT License
+* Android AppMsg
+ https://github.com/johnkil/Android-AppMsg
+ Apache License v2
### Images
* icon.svg
diff --git a/OpenPGP-Keychain/src/main/aidl/org/sufficientlysecure/keychain/service/remote/IExtendedApiCallback.aidl b/Resources/old extended service/src/main/aidl/org/sufficientlysecure/keychain/service/remote/IExtendedApiCallback.aidl
index f69f66fd7..f69f66fd7 100644
--- a/OpenPGP-Keychain/src/main/aidl/org/sufficientlysecure/keychain/service/remote/IExtendedApiCallback.aidl
+++ b/Resources/old extended service/src/main/aidl/org/sufficientlysecure/keychain/service/remote/IExtendedApiCallback.aidl
diff --git a/OpenPGP-Keychain/src/main/aidl/org/sufficientlysecure/keychain/service/remote/IExtendedApiService.aidl b/Resources/old extended service/src/main/aidl/org/sufficientlysecure/keychain/service/remote/IExtendedApiService.aidl
index 669bd31b5..669bd31b5 100644
--- a/OpenPGP-Keychain/src/main/aidl/org/sufficientlysecure/keychain/service/remote/IExtendedApiService.aidl
+++ b/Resources/old extended service/src/main/aidl/org/sufficientlysecure/keychain/service/remote/IExtendedApiService.aidl
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/ExtendedApiService.java b/Resources/old extended service/src/main/java/ExtendedApiService.java
index 427e6bb8f..427e6bb8f 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/ExtendedApiService.java
+++ b/Resources/old extended service/src/main/java/ExtendedApiService.java
diff --git a/build.gradle b/build.gradle
index c5ca596fc..86c40fa3a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,7 +4,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:0.8.0'
+ classpath 'com.android.tools.build:gradle:0.8.3'
}
}
@@ -16,4 +16,4 @@ allprojects {
task wrapper(type: Wrapper) {
gradleVersion = '1.10'
-}
+} \ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ddc691c4f..9559bfd8b 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Jan 29 01:43:20 CET 2014
+#Sun Feb 09 18:34:27 CET 2014
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
diff --git a/libraries/Android-AppMsg/.gitignore b/libraries/Android-AppMsg/.gitignore
new file mode 100644
index 000000000..bdbc634c1
--- /dev/null
+++ b/libraries/Android-AppMsg/.gitignore
@@ -0,0 +1,36 @@
+#Android generated
+bin
+gen
+
+#Eclipse
+.project
+.classpath
+.settings
+
+#IntelliJ IDEA
+.idea
+*.iml
+*.ipr
+*.iws
+out
+
+#Maven
+target
+release.properties
+pom.xml.*
+
+#Ant
+build.xml
+local.properties
+proguard.cfg
+proguard-project.txt
+
+#Gradle
+.gradle
+build
+
+#OSX
+.DS_Store
+
+#Personal Files
+signing.properties
diff --git a/libraries/Android-AppMsg/LICENSE.txt b/libraries/Android-AppMsg/LICENSE.txt
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/libraries/Android-AppMsg/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/libraries/Android-AppMsg/README.md b/libraries/Android-AppMsg/README.md
new file mode 100644
index 000000000..7f8006998
--- /dev/null
+++ b/libraries/Android-AppMsg/README.md
@@ -0,0 +1,117 @@
+Android AppMsg (Crouton) Library
+================================
+
+Implementation of in-layout notifications. Based on [Toast](http://developer.android.com/reference/android/widget/Toast.html) notifications and article [The making of Prixing #4: in-layout notifications](http://android.cyrilmottier.com/?p=773) by [Cyril Mottier](http://www.cyrilmottier.com/).
+
+
+Description
+-----------
+
+Toast is far from being perfect and I am not entirely satisfied with it.
+Toast can be un-accurate in some cases. Indeed, Toast has one major drawback: it completely breaks contexts.
+This issue can be reproduced effortless. Let’s say a user is currently in an app firing a Toast and wants to switch to another application using the dedicated “multitask” button.
+The Toast will remain on screen even if the brought-to-front application has nothing do to with the previously shown app as described on the following figure:
+![Example Image][1]
+
+As you can easily notice, the problem with Toasts is they are persistent.
+Once a Toast has been fired, it is displayed on top of any screen and remains visible for the duration specified at its creation.
+
+In order to bypass the Toast persistence problem and ensure information is displayed in the correct context, we decided to create a new notification system:
+Activity-bound notifications. This is what it looks like in the current version of Prixing:
+![Example Image][2]
+
+Crouton overcomes the main issue of having a Toast being shown while the menu is open.
+It sticks to the current screen sliding with it and leaving the menu completely free of any information that would have not been related to it.
+
+<b>Copyright (C) by Cyril Mottier</b>
+
+Sample
+------
+
+A sample application is available on Google Play:
+
+<a href="http://play.google.com/store/apps/details?id=com.devspark.appmsg.sample">
+ <img alt="Get it on Google Play"
+ src="http://www.android.com/images/brand/get_it_on_play_logo_small.png" />
+</a>
+
+![Example Image][3]
+
+The source code is available in this repository.
+
+Compatibility
+-------------
+
+This library is compatible from API 4 (Android 1.6).
+
+Installation
+------------
+
+The sample project requires:
+
+* The library project
+* [ActionBarSherlock](https://github.com/JakeWharton/ActionBarSherlock)
+
+Usage
+-----
+
+Android AppMsg is presented as an [Android library project](http://developer.android.com/guide/developing/projects/projects-eclipse.html).
+You can include this project by [referencing it as a library project](http://developer.android.com/guide/developing/projects/projects-eclipse.html#ReferencingLibraryProject) in Eclipse or ant.
+
+To display the item you need the following code:
+
+* Show AppMsg:
+
+``` java
+AppMsg.makeText(/*Activity*/, /*CharSequence*/, /*AppMsg.Style*/).show();
+```
+
+Gradle
+------
+
+Android-AppMsg Library is now pushed to Maven Central as a AAR, so you just need to add the following dependency to your build.gradle.
+
+``` xml
+dependencies {
+ compile 'com.github.johnkil.android-appmsg:appmsg:1.2.0'
+}
+```
+
+Example Gradle project using Android-AppMsg:
+
+* [Android-AppMsg-Gradle-Sample](https://github.com/johnkil/Android-AppMsg-Gradle-Sample)
+
+
+Contribution
+------------
+
+Please fork [dev](https://github.com/johnkil/Android-AppMsg/tree/dev) repository and contribute back using [pull requests](https://github.com/johnkil/Android-AppMsg/pulls).
+
+Contributors are recommended to follow the Android [Code Style Guidelines](http://source.android.com/source/code-style.html).
+
+Any contributions, large or small, major features, bug fixes, additional language translations, unit/integration tests are welcomed and appreciated but will be thoroughly reviewed and discussed.
+
+Developed By
+------------
+* Evgeny Shishkin - <johnkil78@gmail.com>
+
+License
+-------
+
+ Copyright 2012 Evgeny Shishkin
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+[1]: http://cyrilmottier.com/media/2012/07/the-making-of-prixing-4-activity-tied-notifications/toast_user_flow_fail.png
+[2]: http://cyrilmottier.com/media/2012/07/the-making-of-prixing-4-activity-tied-notifications/in_layout_notification.png
+[3]: http://i46.tinypic.com/21kywit.png
diff --git a/libraries/Android-AppMsg/library/AndroidManifest.xml b/libraries/Android-AppMsg/library/AndroidManifest.xml
new file mode 100644
index 000000000..c03f66559
--- /dev/null
+++ b/libraries/Android-AppMsg/library/AndroidManifest.xml
@@ -0,0 +1,12 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.devspark.appmsg"
+ android:versionCode="5"
+ android:versionName="1.2.0">
+
+ <uses-sdk
+ android:minSdkVersion="4"
+ android:targetSdkVersion="19"/>
+
+ <application/>
+
+</manifest> \ No newline at end of file
diff --git a/libraries/Android-AppMsg/library/build.gradle b/libraries/Android-AppMsg/library/build.gradle
new file mode 100644
index 000000000..934cf1cb1
--- /dev/null
+++ b/libraries/Android-AppMsg/library/build.gradle
@@ -0,0 +1,22 @@
+apply plugin: 'android-library'
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion '19.0.1'
+
+ defaultConfig {
+ minSdkVersion 4
+ }
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ res.srcDirs = ['res']
+ }
+ }
+}
+
+if (project.hasProperty('nexusUsername')) {
+ // Used to push in maven
+ apply from: '../maven_push.gradle'
+} \ No newline at end of file
diff --git a/libraries/Android-AppMsg/library/pom.xml b/libraries/Android-AppMsg/library/pom.xml
new file mode 100644
index 000000000..149557370
--- /dev/null
+++ b/libraries/Android-AppMsg/library/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.devspark</groupId>
+ <artifactId>appmsg</artifactId>
+ <version>1.0.1</version>
+ <packaging>apklib</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.android</groupId>
+ <artifactId>android</artifactId>
+ <version>4.1.1.4</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <sourceDirectory>src</sourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pmd-plugin</artifactId>
+ <version>2.7.1</version>
+ <configuration>
+ <linkXref>true</linkXref>
+ <sourceEncoding>utf-8</sourceEncoding>
+ <minimumTokens>100</minimumTokens>
+ <targetJdk>1.6</targetJdk>
+ <excludes>
+ <exclude>**/*Bean.java</exclude>
+ <exclude>**/generated/*.java</exclude>
+ </excludes>
+ <excludeRoots>
+ <excludeRoot>target/generated-sources/stubs</excludeRoot>
+ </excludeRoots>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>com.jayway.maven.plugins.android.generation2</groupId>
+ <artifactId>android-maven-plugin</artifactId>
+ <version>3.6.0</version>
+ <configuration>
+ <androidManifestFile>${project.basedir}/AndroidManifest.xml</androidManifestFile>
+ <assetsDirectory>${project.basedir}/assets</assetsDirectory>
+ <resourceDirectory>${project.basedir}/res</resourceDirectory>
+ <nativeLibrariesDirectory>${project.basedir}/src/main/native</nativeLibrariesDirectory>
+ <sdk>
+ <platform>16</platform>
+ </sdk>
+ <undeployBeforeDeploy>true</undeployBeforeDeploy>
+ </configuration>
+ <extensions>true</extensions>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.2</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/libraries/Android-AppMsg/library/project.properties b/libraries/Android-AppMsg/library/project.properties
new file mode 100644
index 000000000..91d2b0246
--- /dev/null
+++ b/libraries/Android-AppMsg/library/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
+android.library=true
diff --git a/libraries/Android-AppMsg/library/res/layout/app_msg.xml b/libraries/Android-AppMsg/library/res/layout/app_msg.xml
new file mode 100644
index 000000000..235fe47b9
--- /dev/null
+++ b/libraries/Android-AppMsg/library/res/layout/app_msg.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:minHeight="48dp"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@android:id/message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:padding="8dp"
+ android:textColor="?android:textColorPrimaryInverse"
+ android:textIsSelectable="false"
+ android:textSize="14sp"
+ android:textStyle="bold" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/libraries/Android-AppMsg/library/res/values/colors.xml b/libraries/Android-AppMsg/library/res/values/colors.xml
new file mode 100644
index 000000000..2bfd9ecb4
--- /dev/null
+++ b/libraries/Android-AppMsg/library/res/values/colors.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <color name="alert">#CC0000</color>
+ <color name="confirm">#FF8800</color>
+ <color name="info">#669900</color>
+
+</resources> \ No newline at end of file
diff --git a/libraries/Android-AppMsg/library/src/com/devspark/appmsg/AppMsg.java b/libraries/Android-AppMsg/library/src/com/devspark/appmsg/AppMsg.java
new file mode 100644
index 000000000..e86273762
--- /dev/null
+++ b/libraries/Android-AppMsg/library/src/com/devspark/appmsg/AppMsg.java
@@ -0,0 +1,587 @@
+/*
+ * Copyright 2012 Evgeny Shishkin
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 com.devspark.appmsg;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+/**
+ * In-layout notifications. Based on {@link android.widget.Toast} notifications
+ * and article by Cyril Mottier (http://android.cyrilmottier.com/?p=773).
+ *
+ * @author e.shishkin
+ */
+public class AppMsg {
+
+ /**
+ * Show the view or text notification for a short period of time. This time
+ * could be user-definable. This is the default.
+ *
+ * @see #setDuration
+ */
+ public static final int LENGTH_SHORT = 3000;
+
+ /**
+ * Show the view or text notification for a long period of time. This time
+ * could be user-definable.
+ *
+ * @see #setDuration
+ */
+ public static final int LENGTH_LONG = 5000;
+
+ /**
+ * <p>Show the view or text notification for an undefined amount of time
+ * -Usually until an invocation of {@link #cancel()}, {@link #cancelAll(android.app.Activity)},
+ * {@link #cancelAll()} or {@link android.app.Activity#onDestroy()}-,
+ * stacking on top of any other {@link com.devspark.appmsg.AppMsg} with this duration.</p>
+ *
+ * <p><b>Note</b>: You are responsible
+ * for calling {@link #cancel()} on such {@link com.devspark.appmsg.AppMsg}.</p>
+ *
+ * @see #setDuration
+ */
+ public static final int LENGTH_STICKY = -1;
+
+ /**
+ * Lowest priority, messages with this priority will be showed after all messages with priority
+ * {@link #PRIORITY_HIGH} and {@link #PRIORITY_NORMAL} have been shown.
+ *
+ * @see #setPriority(int)
+ */
+ public static final int PRIORITY_LOW = Integer.MIN_VALUE;
+ /**
+ * Normal priority, messages with this priority will be showed after all messages with priority
+ * {@link #PRIORITY_HIGH} but before {@link #PRIORITY_LOW} have been shown.
+ *
+ * @see #setPriority(int)
+ */
+ public static final int PRIORITY_NORMAL = 0;
+ /**
+ * Highest priority, messages with this priority will be showed before any other message.
+ *
+ * @see #setPriority(int)
+ */
+ public static final int PRIORITY_HIGH = Integer.MAX_VALUE;
+
+ /**
+ * Show the text notification for a long period of time with a negative style.
+ */
+ public static final Style STYLE_ALERT = new Style(LENGTH_LONG, R.color.alert);
+
+ /**
+ * Show the text notification for a short period of time with a positive style.
+ */
+ public static final Style STYLE_CONFIRM = new Style(LENGTH_SHORT, R.color.confirm);
+
+ /**
+ * Show the text notification for a short period of time with a neutral style.
+ */
+ public static final Style STYLE_INFO = new Style(LENGTH_SHORT, R.color.info);
+
+ private final Activity mActivity;
+ private int mDuration = LENGTH_SHORT;
+ private View mView;
+ private ViewGroup mParent;
+ private LayoutParams mLayoutParams;
+ private boolean mFloating;
+ Animation mInAnimation, mOutAnimation;
+ int mPriority = PRIORITY_NORMAL;
+
+ /**
+ * Construct an empty AppMsg object. You must call {@link #setView} before
+ * you can call {@link #show}.
+ *
+ * @param activity {@link android.app.Activity} to use.
+ */
+ public AppMsg(Activity activity) {
+ mActivity = activity;
+ }
+
+ /**
+ * Make a {@link AppMsg} that just contains a text view.
+ *
+ * @param context The context to use. Usually your
+ * {@link android.app.Activity} object.
+ * @param text The text to show. Can be formatted text.
+ * @param style The style with a background and a duration.
+ */
+ public static AppMsg makeText(Activity context, CharSequence text, Style style) {
+ return makeText(context, text, style, R.layout.app_msg);
+ }
+
+ /**
+ * @author mengguoqiang 扩展支持设置字体大小
+ * Make a {@link AppMsg} that just contains a text view.
+ *
+ * @param context The context to use. Usually your
+ * {@link android.app.Activity} object.
+ * @param text The text to show. Can be formatted text.
+ * @param style The style with a background and a duration.
+ */
+ public static AppMsg makeText(Activity context, CharSequence text, Style style, float textSize) {
+ return makeText(context, text, style, R.layout.app_msg, textSize);
+ }
+
+ /**
+ * Make a {@link AppMsg} with a custom layout. The layout must have a {@link TextView} com id {@link android.R.id.message}
+ *
+ * @param context The context to use. Usually your
+ * {@link android.app.Activity} object.
+ * @param text The text to show. Can be formatted text.
+ * @param style The style with a background and a duration.
+ */
+ public static AppMsg makeText(Activity context, CharSequence text, Style style, int layoutId) {
+ LayoutInflater inflate = (LayoutInflater)
+ context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View v = inflate.inflate(layoutId, null);
+
+ return makeText(context, text, style, v, true);
+ }
+
+ /**
+ * @author mengguoqiang 扩展支持字体大小
+ * Make a {@link AppMsg} with a custom layout. The layout must have a {@link TextView} com id {@link android.R.id.message}
+ *
+ * @param context The context to use. Usually your
+ * {@link android.app.Activity} object.
+ * @param text The text to show. Can be formatted text.
+ * @param style The style with a background and a duration.
+ */
+ public static AppMsg makeText(Activity context, CharSequence text, Style style, int layoutId, float textSize) {
+ LayoutInflater inflate = (LayoutInflater)
+ context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View v = inflate.inflate(layoutId, null);
+
+ return makeText(context, text, style, v, true, textSize);
+ }
+
+ /**
+ * Make a non-floating {@link AppMsg} with a custom view presented inside the layout.
+ * It can be used to create non-floating notifications if floating is false.
+ *
+ * @param context The context to use. Usually your
+ * {@link android.app.Activity} object.
+ * @param customView
+ * View to be used.
+ * @param text The text to show. Can be formatted text.
+ * @param style The style with a background and a duration.
+ */
+ public static AppMsg makeText(Activity context, CharSequence text, Style style, View customView) {
+ return makeText(context, text, style, customView, false);
+ }
+
+ /**
+ * Make a {@link AppMsg} with a custom view. It can be used to create non-floating notifications if floating is false.
+ *
+ * @param context The context to use. Usually your
+ * {@link android.app.Activity} object.
+ * @param view
+ * View to be used.
+ * @param text The text to show. Can be formatted text.
+ * @param style The style with a background and a duration.
+ * @param floating true if it'll float.
+ */
+ private static AppMsg makeText(Activity context, CharSequence text, Style style, View view, boolean floating) {
+ return makeText(context, text, style, view, floating, 0);
+ }
+
+ /**
+ *
+ * @author mengguoqiang 扩展支持设置字体大小
+ * Make a {@link AppMsg} with a custom view. It can be used to create non-floating notifications if floating is false.
+ *
+ * @param context The context to use. Usually your
+ * {@link android.app.Activity} object.
+ * @param view
+ * View to be used.
+ * @param text The text to show. Can be formatted text.
+ * @param style The style with a background and a duration.
+ * @param floating true if it'll float.
+ */
+ private static AppMsg makeText(Activity context, CharSequence text, Style style, View view, boolean floating, float textSize) {
+ AppMsg result = new AppMsg(context);
+
+ view.setBackgroundResource(style.background);
+
+ TextView tv = (TextView) view.findViewById(android.R.id.message);
+ if(textSize > 0) tv.setTextSize(textSize);
+ tv.setText(text);
+
+ result.mView = view;
+ result.mDuration = style.duration;
+ result.mFloating = floating;
+
+ return result;
+ }
+
+ /**
+ * Make a {@link AppMsg} with a custom view. It can be used to create non-floating notifications if floating is false.
+ *
+ * @param context The context to use. Usually your
+ * {@link android.app.Activity} object.
+ * @param resId The resource id of the string resource to use. Can be
+ * formatted text.
+ * @param style The style with a background and a duration.
+ * @param floating true if it'll float.
+ */
+ public static AppMsg makeText(Activity context, int resId, Style style, View customView, boolean floating) {
+ return makeText(context, context.getResources().getText(resId), style, customView, floating);
+ }
+
+ /**
+ * Make a {@link AppMsg} that just contains a text view with the text from a
+ * resource.
+ *
+ * @param context The context to use. Usually your
+ * {@link android.app.Activity} object.
+ * @param resId The resource id of the string resource to use. Can be
+ * formatted text.
+ * @param style The style with a background and a duration.
+ * @throws Resources.NotFoundException if the resource can't be found.
+ */
+ public static AppMsg makeText(Activity context, int resId, Style style)
+ throws Resources.NotFoundException {
+ return makeText(context, context.getResources().getText(resId), style);
+ }
+
+ /**
+ * Make a {@link AppMsg} with a custom layout using the text from a
+ * resource. The layout must have a {@link TextView} com id {@link android.R.id.message}
+ *
+ * @param context The context to use. Usually your
+ * {@link android.app.Activity} object.
+ * @param resId The resource id of the string resource to use. Can be
+ * formatted text.
+ * @param style The style with a background and a duration.
+ * @throws Resources.NotFoundException if the resource can't be found.
+ */
+ public static AppMsg makeText(Activity context, int resId, Style style, int layoutId)
+ throws Resources.NotFoundException {
+ return makeText(context, context.getResources().getText(resId), style, layoutId);
+ }
+
+ /**
+ * Show the view for the specified duration.
+ */
+ public void show() {
+ MsgManager manager = MsgManager.obtain(mActivity);
+ manager.add(this);
+ }
+
+ /**
+ * @return <code>true</code> if the {@link AppMsg} is being displayed, else <code>false</code>.
+ */
+ public boolean isShowing() {
+ if (mFloating) {
+ return mView != null && mView.getParent() != null;
+ } else {
+ return mView.getVisibility() == View.VISIBLE;
+ }
+ }
+
+ /**
+ * Close the view if it's showing, or don't show it if it isn't showing yet.
+ * You do not normally have to call this. Normally view will disappear on its own
+ * after the appropriate duration.
+ */
+ public void cancel() {
+ MsgManager.obtain(mActivity).clearMsg(this);
+
+ }
+
+ /**
+ * Cancels all queued {@link AppMsg}s, in all Activities. If there is a {@link AppMsg}
+ * displayed currently, it will be the last one displayed.
+ */
+ public static void cancelAll() {
+ MsgManager.clearAll();
+ }
+
+ /**
+ * Cancels all queued {@link AppMsg}s, in given {@link android.app.Activity}.
+ * If there is a {@link AppMsg} displayed currently, it will be the last one displayed.
+ * @param activity
+ */
+ public static void cancelAll(Activity activity) {
+ MsgManager.release(activity);
+ }
+
+ /**
+ * Return the activity.
+ */
+ public Activity getActivity() {
+ return mActivity;
+ }
+
+ /**
+ * Set the view to show.
+ *
+ * @see #getView
+ */
+ public void setView(View view) {
+ mView = view;
+ }
+
+ /**
+ * Return the view.
+ *
+ * @see #setView
+ */
+ public View getView() {
+ return mView;
+ }
+
+ /**
+ * Set how long to show the view for.
+ *
+ * @see #LENGTH_SHORT
+ * @see #LENGTH_LONG
+ */
+ public void setDuration(int duration) {
+ mDuration = duration;
+ }
+
+ /**
+ * Return the duration.
+ *
+ * @see #setDuration
+ */
+ public int getDuration() {
+ return mDuration;
+ }
+
+ /**
+ * Update the text in a AppMsg that was previously created using one of the makeText() methods.
+ *
+ * @param resId The new text for the AppMsg.
+ */
+ public void setText(int resId) {
+ setText(mActivity.getText(resId));
+ }
+
+ /**
+ * Update the text in a AppMsg that was previously created using one of the makeText() methods.
+ *
+ * @param s The new text for the AppMsg.
+ */
+ public void setText(CharSequence s) {
+ if (mView == null) {
+ throw new RuntimeException("This AppMsg was not created with AppMsg.makeText()");
+ }
+ TextView tv = (TextView) mView.findViewById(android.R.id.message);
+ if (tv == null) {
+ throw new RuntimeException("This AppMsg was not created with AppMsg.makeText()");
+ }
+ tv.setText(s);
+ }
+
+ /**
+ * Gets the crouton's layout parameters, constructing a default if necessary.
+ *
+ * @return the layout parameters
+ */
+ public LayoutParams getLayoutParams() {
+ if (mLayoutParams == null) {
+ mLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ }
+ return mLayoutParams;
+ }
+
+ /**
+ * Sets the layout parameters which will be used to display the crouton.
+ *
+ * @param layoutParams The layout parameters to use.
+ * @return <code>this</code>, for chaining.
+ */
+ public AppMsg setLayoutParams(LayoutParams layoutParams) {
+ mLayoutParams = layoutParams;
+ return this;
+ }
+
+ /**
+ * Constructs and sets the layout parameters to have some gravity.
+ *
+ * @param gravity the gravity of the Crouton
+ * @return <code>this</code>, for chaining.
+ * @see android.view.Gravity
+ */
+ public AppMsg setLayoutGravity(int gravity) {
+ mLayoutParams = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, gravity);
+ return this;
+ }
+
+ /**
+ * Return the value of floating.
+ *
+ * @see #setFloating(boolean)
+ */
+ public boolean isFloating() {
+ return mFloating;
+ }
+
+ /**
+ * Sets the value of floating.
+ *
+ * @param mFloating
+ */
+ public void setFloating(boolean mFloating) {
+ this.mFloating = mFloating;
+ }
+
+ /**
+ * Sets the Animations to be used when displaying/removing the Crouton.
+ * @param inAnimation the Animation resource ID to be used when displaying.
+ * @param outAnimation the Animation resource ID to be used when removing.
+ */
+ public AppMsg setAnimation(int inAnimation, int outAnimation) {
+ return setAnimation(AnimationUtils.loadAnimation(mActivity, inAnimation),
+ AnimationUtils.loadAnimation(mActivity, outAnimation));
+ }
+
+ /**
+ * Sets the Animations to be used when displaying/removing the Crouton.
+ * @param inAnimation the Animation to be used when displaying.
+ * @param outAnimation the Animation to be used when removing.
+ */
+ public AppMsg setAnimation(Animation inAnimation, Animation outAnimation) {
+ mInAnimation = inAnimation;
+ mOutAnimation = outAnimation;
+ return this;
+ }
+
+ /**
+ * @return
+ * Current priority
+ *
+ * @see #PRIORITY_HIGH
+ * @see #PRIORITY_NORMAL
+ * @see #PRIORITY_LOW
+ */
+ public int getPriority() {
+ return mPriority;
+ }
+
+ /**
+ * <p>Set priority for this message</p>
+ * <p><b>Note</b>: This only affects the order in which the messages get shown,
+ * not the stacking order of the views.</p>
+ *
+ * <p>Example: In the queue there are 3 messages [A, B, C],
+ * all of them with priority {@link #PRIORITY_NORMAL}, currently message A is being shown
+ * so we add a new message D with priority {@link #PRIORITY_HIGH}, after A goes away, given that
+ * D has a higher priority than B an the reset, D will be shown, then once that D is gone,
+ * B will be shown, and then finally C.</p>
+ *
+ * @param priority
+ * A value indicating priority, although you can use any integer value, usage of already
+ * defined is highly encouraged.
+ *
+ * @see #PRIORITY_HIGH
+ * @see #PRIORITY_NORMAL
+ * @see #PRIORITY_LOW
+ */
+ public void setPriority(int priority) {
+ mPriority = priority;
+ }
+
+ /**
+ * @return
+ * Provided parent to add {@link #getView()} to using {@link #getLayoutParams()}.
+ */
+ public ViewGroup getParent() {
+ return mParent;
+ }
+
+ /**
+ * Provide a different parent than Activity decor view
+ * @param parent
+ * Provided parent to add {@link #getView()} to using {@link #getLayoutParams()}.
+ *
+ */
+ public void setParent(ViewGroup parent) {
+ mParent = parent;
+ }
+
+ /**
+ * Provide a different parent than Activity decor view
+ *
+ * @param parentId
+ * Provided parent id to add {@link #getView()} to using {@link #getLayoutParams()}.
+ *
+ */
+ public void setParent(int parentId) {
+ setParent((ViewGroup) mActivity.findViewById(parentId));
+ }
+
+ /**
+ * The style for a {@link AppMsg}.
+ *
+ * @author e.shishkin
+ */
+ public static class Style {
+
+ private final int duration;
+ private final int background;
+
+ /**
+ * Construct an {@link AppMsg.Style} object.
+ *
+ * @param duration How long to display the message. Either
+ * {@link #LENGTH_SHORT} or {@link #LENGTH_LONG}
+ * @param resId resource for AppMsg background
+ */
+ public Style(int duration, int resId) {
+ this.duration = duration;
+ this.background = resId;
+ }
+
+ /**
+ * Return the duration in milliseconds.
+ */
+ public int getDuration() {
+ return duration;
+ }
+
+ /**
+ * Return the resource id of background.
+ */
+ public int getBackground() {
+ return background;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof AppMsg.Style)) {
+ return false;
+ }
+ Style style = (Style) o;
+ return style.duration == duration
+ && style.background == background;
+ }
+
+ }
+
+}
diff --git a/libraries/Android-AppMsg/library/src/com/devspark/appmsg/MsgManager.java b/libraries/Android-AppMsg/library/src/com/devspark/appmsg/MsgManager.java
new file mode 100644
index 000000000..962648566
--- /dev/null
+++ b/libraries/Android-AppMsg/library/src/com/devspark/appmsg/MsgManager.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright 2012 Evgeny Shishkin
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 com.devspark.appmsg;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.Application;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+
+import java.lang.ref.WeakReference;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.PriorityQueue;
+import java.util.Queue;
+import java.util.WeakHashMap;
+
+import static android.app.Application.ActivityLifecycleCallbacks;
+import static android.os.Build.VERSION.SDK_INT;
+import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
+import static com.devspark.appmsg.AppMsg.LENGTH_STICKY;
+
+/**
+ * @author Evgeny Shishkin
+ */
+class MsgManager extends Handler implements Comparator<AppMsg> {
+
+ private static final int MESSAGE_DISPLAY = 0xc2007;
+ private static final int MESSAGE_ADD_VIEW = 0xc20074dd;
+ private static final int MESSAGE_REMOVE = 0xc2007de1;
+
+ private static WeakHashMap<Activity, MsgManager> sManagers;
+ private static ReleaseCallbacks sReleaseCallbacks;
+
+ private final Queue<AppMsg> msgQueue;
+ private final Queue<AppMsg> stickyQueue;
+
+ private MsgManager() {
+ msgQueue = new PriorityQueue<AppMsg>(1, this);
+ stickyQueue = new LinkedList<AppMsg>();
+ }
+
+ /**
+ * @return A {@link MsgManager} instance to be used for given {@link android.app.Activity}.
+ */
+ static synchronized MsgManager obtain(Activity activity) {
+ if (sManagers == null) {
+ sManagers = new WeakHashMap<Activity, MsgManager>(1);
+ }
+ MsgManager manager = sManagers.get(activity);
+ if (manager == null) {
+ manager = new MsgManager();
+ ensureReleaseOnDestroy(activity);
+ sManagers.put(activity, manager);
+ }
+
+ return manager;
+ }
+
+ static void ensureReleaseOnDestroy(Activity activity) {
+ if (SDK_INT < ICE_CREAM_SANDWICH) {
+ return;
+ }
+ if (sReleaseCallbacks == null) {
+ sReleaseCallbacks = new ReleaseCallbacksIcs();
+ }
+ sReleaseCallbacks.register(activity.getApplication());
+ }
+
+
+ static synchronized void release(Activity activity) {
+ if (sManagers != null) {
+ final MsgManager manager = sManagers.remove(activity);
+ if (manager != null) {
+ manager.clearAllMsg();
+ }
+ }
+ }
+
+ static synchronized void clearAll() {
+ if (sManagers != null) {
+ final Iterator<MsgManager> iterator = sManagers.values().iterator();
+ while (iterator.hasNext()) {
+ final MsgManager manager = iterator.next();
+ if (manager != null) {
+ manager.clearAllMsg();
+ }
+ iterator.remove();
+ }
+ sManagers.clear();
+ }
+ }
+
+ /**
+ * Inserts a {@link AppMsg} to be displayed.
+ *
+ * @param appMsg
+ */
+ void add(AppMsg appMsg) {
+ msgQueue.add(appMsg);
+ if (appMsg.mInAnimation == null) {
+ appMsg.mInAnimation = AnimationUtils.loadAnimation(appMsg.getActivity(),
+ android.R.anim.fade_in);
+ }
+ if (appMsg.mOutAnimation == null) {
+ appMsg.mOutAnimation = AnimationUtils.loadAnimation(appMsg.getActivity(),
+ android.R.anim.fade_out);
+ }
+ displayMsg();
+ }
+
+ /**
+ * Removes all {@link AppMsg} from the queue.
+ */
+ void clearMsg(AppMsg appMsg) {
+ if(msgQueue.contains(appMsg) || stickyQueue.contains(appMsg)){
+ // Avoid the message from being removed twice.
+ removeMessages(MESSAGE_DISPLAY, appMsg);
+ removeMessages(MESSAGE_ADD_VIEW, appMsg);
+ removeMessages(MESSAGE_REMOVE, appMsg);
+ msgQueue.remove(appMsg);
+ stickyQueue.remove(appMsg);
+ removeMsg(appMsg);
+ }
+ }
+
+ /**
+ * Removes all {@link AppMsg} from the queue.
+ */
+ void clearAllMsg() {
+ removeMessages(MESSAGE_DISPLAY);
+ removeMessages(MESSAGE_ADD_VIEW);
+ removeMessages(MESSAGE_REMOVE);
+ clearShowing();
+ msgQueue.clear();
+ stickyQueue.clear();
+ }
+
+ void clearShowing() {
+ final Collection<AppMsg> showing = new HashSet<AppMsg>();
+ obtainShowing(msgQueue, showing);
+ obtainShowing(stickyQueue, showing);
+ for (AppMsg msg : showing) {
+ clearMsg(msg);
+ }
+ }
+
+ static void obtainShowing(Collection<AppMsg> from, Collection<AppMsg> appendTo) {
+ for (AppMsg msg : from) {
+ if (msg.isShowing()) {
+ appendTo.add(msg);
+ }
+ }
+ }
+
+ /**
+ * Displays the next {@link AppMsg} within the queue.
+ */
+ private void displayMsg() {
+ if (msgQueue.isEmpty()) {
+ return;
+ }
+ // First peek whether the AppMsg is being displayed.
+ final AppMsg appMsg = msgQueue.peek();
+ final Message msg;
+ if (!appMsg.isShowing()) {
+ // Display the AppMsg
+ msg = obtainMessage(MESSAGE_ADD_VIEW);
+ msg.obj = appMsg;
+ sendMessage(msg);
+ } else if (appMsg.getDuration() != LENGTH_STICKY) {
+ msg = obtainMessage(MESSAGE_DISPLAY);
+ sendMessageDelayed(msg, appMsg.getDuration()
+ + appMsg.mInAnimation.getDuration() + appMsg.mOutAnimation.getDuration());
+ }
+ }
+
+ /**
+ * Removes the {@link AppMsg}'s view after it's display duration.
+ *
+ * @param appMsg The {@link AppMsg} added to a {@link ViewGroup} and should be removed.s
+ */
+ private void removeMsg(final AppMsg appMsg) {
+ clearMsg(appMsg);
+ final View view = appMsg.getView();
+ ViewGroup parent = ((ViewGroup) view.getParent());
+ if (parent != null) {
+ appMsg.mOutAnimation.setAnimationListener(new OutAnimationListener(appMsg));
+ view.clearAnimation();
+ view.startAnimation(appMsg.mOutAnimation);
+ }
+
+ Message msg = obtainMessage(MESSAGE_DISPLAY);
+ sendMessage(msg);
+ }
+
+ private void addMsgToView(AppMsg appMsg) {
+ View view = appMsg.getView();
+ if (view.getParent() == null) { // Not added yet
+ final ViewGroup targetParent = appMsg.getParent();
+ final ViewGroup.LayoutParams params = appMsg.getLayoutParams();
+ if (targetParent != null) {
+ targetParent.addView(view, params);
+ } else {
+ appMsg.getActivity().addContentView(view, params);
+ }
+ }
+ view.clearAnimation();
+ view.startAnimation(appMsg.mInAnimation);
+ if (view.getVisibility() != View.VISIBLE) {
+ view.setVisibility(View.VISIBLE);
+ }
+
+ final int duration = appMsg.getDuration();
+ if (duration != LENGTH_STICKY) {
+ final Message msg = obtainMessage(MESSAGE_REMOVE);
+ msg.obj = appMsg;
+ sendMessageDelayed(msg, duration);
+ } else { // We are sticky, we don't get removed just yet
+ stickyQueue.add(msgQueue.poll());
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ final AppMsg appMsg;
+ switch (msg.what) {
+ case MESSAGE_DISPLAY:
+ displayMsg();
+ break;
+ case MESSAGE_ADD_VIEW:
+ appMsg = (AppMsg) msg.obj;
+ addMsgToView(appMsg);
+ break;
+ case MESSAGE_REMOVE:
+ appMsg = (AppMsg) msg.obj;
+ removeMsg(appMsg);
+ break;
+ default:
+ super.handleMessage(msg);
+ break;
+ }
+ }
+
+ @Override
+ public int compare(AppMsg lhs, AppMsg rhs) {
+ return inverseCompareInt(lhs.mPriority, rhs.mPriority);
+ }
+
+ static int inverseCompareInt(int lhs, int rhs) {
+ return lhs < rhs ? 1 : (lhs == rhs ? 0 : -1);
+ }
+
+ private static class OutAnimationListener implements Animation.AnimationListener {
+
+ private final AppMsg appMsg;
+
+ private OutAnimationListener(AppMsg appMsg) {
+ this.appMsg = appMsg;
+ }
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ final View view = appMsg.getView();
+ if (appMsg.isFloating()) {
+ final ViewGroup parent = ((ViewGroup) view.getParent());
+ if (parent != null) {
+ parent.post(new Runnable() { // One does not simply removeView
+ @Override
+ public void run() {
+ parent.removeView(view);
+ }
+ });
+ }
+ } else {
+ view.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+
+ }
+ }
+
+ interface ReleaseCallbacks {
+ void register(Application application);
+ }
+
+ @TargetApi(ICE_CREAM_SANDWICH)
+ static class ReleaseCallbacksIcs implements ActivityLifecycleCallbacks, ReleaseCallbacks {
+ private WeakReference<Application> mLastApp;
+ public void register(Application app) {
+ if (mLastApp != null && mLastApp.get() == app) {
+ return; // Already registered with this app
+ } else {
+ mLastApp = new WeakReference<Application>(app);
+ }
+ app.registerActivityLifecycleCallbacks(this);
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ release(activity);
+ }
+ @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
+ @Override public void onActivityStarted(Activity activity) {}
+ @Override public void onActivityResumed(Activity activity) {}
+ @Override public void onActivityPaused(Activity activity) {}
+ @Override public void onActivityStopped(Activity activity) {}
+ @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
+ }
+} \ No newline at end of file
diff --git a/libraries/keychain-api-library/.gitignore b/libraries/keychain-api-library/.gitignore
new file mode 100644
index 000000000..aa8bb5760
--- /dev/null
+++ b/libraries/keychain-api-library/.gitignore
@@ -0,0 +1,29 @@
+#Android specific
+bin
+gen
+obj
+lint.xml
+local.properties
+release.properties
+ant.properties
+*.class
+*.apk
+
+#Gradle
+.gradle
+build
+gradle.properties
+
+#Maven
+target
+pom.xml.*
+
+#Eclipse
+.project
+.classpath
+.settings
+.metadata
+
+#IntelliJ IDEA
+.idea
+*.iml
diff --git a/libraries/keychain-api-library/AndroidManifest.xml b/libraries/keychain-api-library/AndroidManifest.xml
new file mode 100644
index 000000000..768922c22
--- /dev/null
+++ b/libraries/keychain-api-library/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.sufficientlysecure.keychain.api"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="9"
+ android:targetSdkVersion="19" />
+
+ <application/>
+
+</manifest> \ No newline at end of file
diff --git a/libraries/keychain-api-library/LICENSE b/libraries/keychain-api-library/LICENSE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/libraries/keychain-api-library/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/libraries/keychain-api-library/build.gradle b/libraries/keychain-api-library/build.gradle
new file mode 100644
index 000000000..1d5911783
--- /dev/null
+++ b/libraries/keychain-api-library/build.gradle
@@ -0,0 +1,35 @@
+// please leave this here, so this library builds on its own
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:0.8.3'
+ }
+}
+
+apply plugin: 'android-library'
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion '19.0.1'
+
+ // NOTE: We are using the old folder structure to also support Eclipse
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ resources.srcDirs = ['src']
+ aidl.srcDirs = ['src']
+ renderscript.srcDirs = ['src']
+ res.srcDirs = ['res']
+ assets.srcDirs = ['assets']
+ }
+ }
+
+ // Do not abort build if lint finds errors
+ lintOptions {
+ abortOnError false
+ }
+}
diff --git a/libraries/keychain-api-library/project.properties b/libraries/keychain-api-library/project.properties
new file mode 100644
index 000000000..91d2b0246
--- /dev/null
+++ b/libraries/keychain-api-library/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
+android.library=true
diff --git a/libraries/keychain-api-library/res/drawable-hdpi/ic_action_cancel_launchersize.png b/libraries/keychain-api-library/res/drawable-hdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 000000000..71b9118dc
--- /dev/null
+++ b/libraries/keychain-api-library/res/drawable-hdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/libraries/keychain-api-library/res/drawable-mdpi/ic_action_cancel_launchersize.png b/libraries/keychain-api-library/res/drawable-mdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 000000000..270abf45f
--- /dev/null
+++ b/libraries/keychain-api-library/res/drawable-mdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/libraries/keychain-api-library/res/drawable-xhdpi/ic_action_cancel_launchersize.png b/libraries/keychain-api-library/res/drawable-xhdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 000000000..1e3571fa5
--- /dev/null
+++ b/libraries/keychain-api-library/res/drawable-xhdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/libraries/keychain-api-library/res/drawable-xxhdpi/ic_action_cancel_launchersize.png b/libraries/keychain-api-library/res/drawable-xxhdpi/ic_action_cancel_launchersize.png
new file mode 100644
index 000000000..52044601e
--- /dev/null
+++ b/libraries/keychain-api-library/res/drawable-xxhdpi/ic_action_cancel_launchersize.png
Binary files differ
diff --git a/libraries/keychain-api-library/res/values/strings.xml b/libraries/keychain-api-library/res/values/strings.xml
new file mode 100644
index 000000000..a198d0b5e
--- /dev/null
+++ b/libraries/keychain-api-library/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="openpgp_list_preference_none">None</string>
+
+</resources> \ No newline at end of file
diff --git a/libraries/keychain-api-library/src/org/openintents/openpgp/IOpenPgpService.aidl b/libraries/keychain-api-library/src/org/openintents/openpgp/IOpenPgpService.aidl
new file mode 100644
index 000000000..578a7d4b5
--- /dev/null
+++ b/libraries/keychain-api-library/src/org/openintents/openpgp/IOpenPgpService.aidl
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp;
+
+interface IOpenPgpService {
+
+ /**
+ * General extras
+ * --------------
+ *
+ * Bundle params:
+ * int api_version (required)
+ * boolean ascii_armor (request ascii armor for ouput)
+ *
+ * returned Bundle:
+ * int result_code (0, 1, or 2 (see OpenPgpConstants))
+ * OpenPgpError error (if result_code == 0)
+ * Intent intent (if result_code == 2)
+ *
+ */
+
+ /**
+ * Sign only
+ *
+ * optional params:
+ * String passphrase (for key passphrase)
+ */
+ Bundle sign(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
+
+ /**
+ * Encrypt
+ *
+ * Bundle params:
+ * long[] key_ids
+ * or
+ * String[] user_ids (= emails of recipients) (if more than one key has this user_id, a PendingIntent is returned)
+ *
+ * optional params:
+ * String passphrase (for key passphrase)
+ */
+ Bundle encrypt(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
+
+ /**
+ * Sign and encrypt
+ *
+ * Bundle params:
+ * same as in encrypt()
+ */
+ Bundle signAndEncrypt(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
+
+ /**
+ * Decrypts and verifies given input bytes. This methods handles encrypted-only, signed-and-encrypted,
+ * and also signed-only input.
+ *
+ * returned Bundle:
+ * OpenPgpSignatureResult signature_result
+ */
+ Bundle decryptAndVerify(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
+
+ /**
+ * Retrieves key ids based on given user ids (=emails)
+ *
+ * Bundle params:
+ * String[] user_ids
+ *
+ * returned Bundle:
+ * long[] key_ids
+ */
+ Bundle getKeyIds(in Bundle params);
+
+} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpError.java b/libraries/keychain-api-library/src/org/openintents/openpgp/OpenPgpError.java
index f108d3169..4dd2cc641 100644
--- a/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpError.java
+++ b/libraries/keychain-api-library/src/org/openintents/openpgp/OpenPgpError.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,10 +20,13 @@ import android.os.Parcel;
import android.os.Parcelable;
public class OpenPgpError implements Parcelable {
+ public static final int CLIENT_SIDE_ERROR = -1;
+
public static final int GENERIC_ERROR = 0;
- public static final int NO_OR_WRONG_PASSPHRASE = 1;
- public static final int NO_USER_IDS = 2;
- public static final int USER_INTERACTION_REQUIRED = 3;
+ public static final int INCOMPATIBLE_API_VERSIONS = 1;
+
+ public static final int NO_OR_WRONG_PASSPHRASE = 2;
+ public static final int NO_USER_IDS = 3;
int errorId;
String message;
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpSignatureResult.java b/libraries/keychain-api-library/src/org/openintents/openpgp/OpenPgpSignatureResult.java
index 829f8f8cf..16c79ca27 100644
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpSignatureResult.java
+++ b/libraries/keychain-api-library/src/org/openintents/openpgp/OpenPgpSignatureResult.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,14 +22,14 @@ import android.os.Parcelable;
public class OpenPgpSignatureResult implements Parcelable {
// generic error on signature verification
public static final int SIGNATURE_ERROR = 0;
- // successfully verified signature, with trusted public key
- public static final int SIGNATURE_SUCCESS_TRUSTED = 1;
+ // successfully verified signature, with certified public key
+ public static final int SIGNATURE_SUCCESS_CERTIFIED = 1;
// no public key was found for this signature verification
// you can retrieve the key with
// getKeys(new String[] {String.valueOf(signatureResult.getKeyId)}, true, callback)
public static final int SIGNATURE_UNKNOWN_PUB_KEY = 2;
- // successfully verified signature, but with untrusted public key
- public static final int SIGNATURE_SUCCESS_UNTRUSTED = 3;
+ // successfully verified signature, but with certified public key
+ public static final int SIGNATURE_SUCCESS_UNCERTIFIED = 3;
int status;
boolean signatureOnly;
diff --git a/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpApi.java b/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpApi.java
new file mode 100644
index 000000000..f121c345d
--- /dev/null
+++ b/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpApi.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp.util;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import org.openintents.openpgp.IOpenPgpService;
+import org.openintents.openpgp.OpenPgpError;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class OpenPgpApi {
+
+ IOpenPgpService mService;
+ Context mContext;
+
+ private static final int OPERATION_SIGN = 0;
+ private static final int OPERATION_ENCRYPT = 1;
+ private static final int OPERATION_SIGN_ENCRYPT = 2;
+ private static final int OPERATION_DECRYPT_VERIFY = 3;
+ private static final int OPERATION_GET_KEY_IDS = 4;
+
+ public OpenPgpApi(Context context, IOpenPgpService service) {
+ this.mContext = context;
+ this.mService = service;
+ }
+
+ public Bundle sign(InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_SIGN, new Bundle(), is, os);
+ }
+
+ public Bundle sign(Bundle params, InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_SIGN, params, is, os);
+ }
+
+ public void sign(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
+ executeApiAsync(OPERATION_SIGN, params, is, os, callback);
+ }
+
+ public Bundle encrypt(InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_ENCRYPT, new Bundle(), is, os);
+ }
+
+ public Bundle encrypt(Bundle params, InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_ENCRYPT, params, is, os);
+ }
+
+ public void encrypt(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
+ executeApiAsync(OPERATION_ENCRYPT, params, is, os, callback);
+ }
+
+ public Bundle signAndEncrypt(InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_SIGN_ENCRYPT, new Bundle(), is, os);
+ }
+
+ public Bundle signAndEncrypt(Bundle params, InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_SIGN_ENCRYPT, params, is, os);
+ }
+
+ public void signAndEncrypt(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
+ executeApiAsync(OPERATION_SIGN_ENCRYPT, params, is, os, callback);
+ }
+
+ public Bundle decryptAndVerify(InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_DECRYPT_VERIFY, new Bundle(), is, os);
+ }
+
+ public Bundle decryptAndVerify(Bundle params, InputStream is, final OutputStream os) {
+ return executeApi(OPERATION_DECRYPT_VERIFY, params, is, os);
+ }
+
+ public void decryptAndVerify(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
+ executeApiAsync(OPERATION_DECRYPT_VERIFY, params, is, os, callback);
+ }
+
+ public Bundle getKeyIds(Bundle params) {
+ return executeApi(OPERATION_GET_KEY_IDS, params, null, null);
+ }
+
+ public interface IOpenPgpCallback {
+ void onReturn(final Bundle result);
+ }
+
+ private class OpenPgpAsyncTask extends AsyncTask<Void, Integer, Bundle> {
+ int operationId;
+ Bundle params;
+ InputStream is;
+ OutputStream os;
+ IOpenPgpCallback callback;
+
+ private OpenPgpAsyncTask(int operationId, Bundle params, InputStream is, OutputStream os, IOpenPgpCallback callback) {
+ this.operationId = operationId;
+ this.params = params;
+ this.is = is;
+ this.os = os;
+ this.callback = callback;
+ }
+
+ @Override
+ protected Bundle doInBackground(Void... unused) {
+ return executeApi(operationId, params, is, os);
+ }
+
+ protected void onPostExecute(Bundle result) {
+ callback.onReturn(result);
+ }
+
+ }
+
+ private void executeApiAsync(int operationId, Bundle params, InputStream is, OutputStream os, IOpenPgpCallback callback) {
+ new OpenPgpAsyncTask(operationId, params, is, os, callback).execute((Void[]) null);
+ }
+
+ private Bundle executeApi(int operationId, Bundle params, InputStream is, OutputStream os) {
+ try {
+ params.putInt(OpenPgpConstants.PARAMS_API_VERSION, OpenPgpConstants.API_VERSION);
+
+ Bundle result = null;
+
+ if (operationId == OPERATION_GET_KEY_IDS) {
+ result = mService.getKeyIds(params);
+ return result;
+ } else {
+ // send the input and output pfds
+ ParcelFileDescriptor input = ParcelFileDescriptorUtil.pipeFrom(is,
+ new ParcelFileDescriptorUtil.IThreadListener() {
+
+ @Override
+ public void onThreadFinished(Thread thread) {
+ Log.d(OpenPgpConstants.TAG, "Copy to service finished");
+ }
+ });
+ ParcelFileDescriptor output = ParcelFileDescriptorUtil.pipeTo(os,
+ new ParcelFileDescriptorUtil.IThreadListener() {
+
+ @Override
+ public void onThreadFinished(Thread thread) {
+ Log.d(OpenPgpConstants.TAG, "Service finished writing!");
+ }
+ });
+
+
+ // blocks until result is ready
+ switch (operationId) {
+ case OPERATION_SIGN:
+ result = mService.sign(params, input, output);
+ break;
+ case OPERATION_ENCRYPT:
+ result = mService.encrypt(params, input, output);
+ break;
+ case OPERATION_SIGN_ENCRYPT:
+ result = mService.signAndEncrypt(params, input, output);
+ break;
+ case OPERATION_DECRYPT_VERIFY:
+ result = mService.decryptAndVerify(params, input, output);
+ break;
+ }
+ // close() is required to halt the TransferThread
+ output.close();
+
+ // set class loader to current context to allow unparcelling
+ // of OpenPgpError and OpenPgpSignatureResult
+ // http://stackoverflow.com/a/3806769
+ result.setClassLoader(mContext.getClassLoader());
+
+ return result;
+ }
+ } catch (Exception e) {
+ Log.e(OpenPgpConstants.TAG, "Exception", e);
+ Bundle result = new Bundle();
+ result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
+ result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
+ new OpenPgpError(OpenPgpError.CLIENT_SIDE_ERROR, e.getMessage()));
+ return result;
+ }
+ }
+
+
+}
diff --git a/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpConstants.java b/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpConstants.java
new file mode 100644
index 000000000..263b42aaa
--- /dev/null
+++ b/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpConstants.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp.util;
+
+public class OpenPgpConstants {
+
+ public static final String TAG = "OpenPgp API";
+
+ public static final int API_VERSION = 1;
+ public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService";
+
+
+ /* Bundle params */
+ public static final String PARAMS_API_VERSION = "api_version";
+ // request ASCII Armor for output
+ // OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
+ public static final String PARAMS_REQUEST_ASCII_ARMOR = "ascii_armor";
+ // (for encrypt method)
+ public static final String PARAMS_USER_IDS = "user_ids";
+ public static final String PARAMS_KEY_IDS = "key_ids";
+ // optional parameter:
+ public static final String PARAMS_PASSPHRASE = "passphrase";
+
+ /* Service Bundle returns */
+ public static final String RESULT_CODE = "result_code";
+ public static final String RESULT_SIGNATURE = "signature";
+ public static final String RESULT_ERRORS = "error";
+ public static final String RESULT_INTENT = "intent";
+
+ // get actual error object from RESULT_ERRORS
+ public static final int RESULT_CODE_ERROR = 0;
+ // success!
+ public static final int RESULT_CODE_SUCCESS = 1;
+ // executeServiceMethod intent and do it again with params from intent
+ public static final int RESULT_CODE_USER_INTERACTION_REQUIRED = 2;
+
+ /* PendingIntent returns */
+ public static final String PI_RESULT_PARAMS = "params";
+
+}
diff --git a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpListPreference.java b/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpListPreference.java
index c8e709df9..034186a3a 100644
--- a/OpenPGP-Keychain-API-Demo/src/main/java/org/openintents/openpgp/OpenPgpListPreference.java
+++ b/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpListPreference.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,20 +14,14 @@
* limitations under the License.
*/
-package org.openintents.openpgp;
-
-import java.util.ArrayList;
-import java.util.List;
+package org.openintents.openpgp.util;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
+import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.preference.DialogPreference;
import android.util.AttributeSet;
@@ -37,33 +31,21 @@ import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.TextView;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.sufficientlysecure.keychain.api.R;
+
+/**
+ * Does not extend ListPreference, but is very similar to it!
+ * http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/preference/ListPreference.java/?v=source
+ */
public class OpenPgpListPreference extends DialogPreference {
- ArrayList<OpenPgpProviderEntry> mProviderList = new ArrayList<OpenPgpProviderEntry>();
+ private ArrayList<OpenPgpProviderEntry> mProviderList = new ArrayList<OpenPgpProviderEntry>();
private String mSelectedPackage;
public OpenPgpListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
-
- List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(
- new Intent(OpenPgpConstants.SERVICE_INTENT), PackageManager.GET_META_DATA);
- if (!resInfo.isEmpty()) {
- for (ResolveInfo resolveInfo : resInfo) {
- if (resolveInfo.serviceInfo == null)
- continue;
-
- String packageName = resolveInfo.serviceInfo.packageName;
- String simpleName = String.valueOf(resolveInfo.serviceInfo.loadLabel(context
- .getPackageManager()));
- Drawable icon = resolveInfo.serviceInfo.loadIcon(context.getPackageManager());
-
- // get api version
- ServiceInfo si = resolveInfo.serviceInfo;
- int apiVersion = si.metaData.getInt("api_version");
-
- mProviderList.add(new OpenPgpProviderEntry(packageName, simpleName, icon,
- apiVersion));
- }
- }
}
public OpenPgpListPreference(Context context) {
@@ -71,20 +53,42 @@ public class OpenPgpListPreference extends DialogPreference {
}
/**
- * Can be used to add "no selection"
- *
+ * Public method to add new entries for legacy applications
+ *
* @param packageName
* @param simpleName
* @param icon
*/
- public void addProvider(int position, String packageName, String simpleName, Drawable icon,
- int apiVersion) {
- mProviderList.add(position, new OpenPgpProviderEntry(packageName, simpleName, icon,
- apiVersion));
+ public void addProvider(int position, String packageName, String simpleName, Drawable icon) {
+ mProviderList.add(position, new OpenPgpProviderEntry(packageName, simpleName, icon));
}
@Override
protected void onPrepareDialogBuilder(Builder builder) {
+
+ // get providers
+ mProviderList.clear();
+ Intent intent = new Intent(OpenPgpConstants.SERVICE_INTENT);
+ List<ResolveInfo> resInfo = getContext().getPackageManager().queryIntentServices(intent, 0);
+ if (!resInfo.isEmpty()) {
+ for (ResolveInfo resolveInfo : resInfo) {
+ if (resolveInfo.serviceInfo == null)
+ continue;
+
+ String packageName = resolveInfo.serviceInfo.packageName;
+ String simpleName = String.valueOf(resolveInfo.serviceInfo.loadLabel(getContext()
+ .getPackageManager()));
+ Drawable icon = resolveInfo.serviceInfo.loadIcon(getContext().getPackageManager());
+
+ mProviderList.add(new OpenPgpProviderEntry(packageName, simpleName, icon));
+ }
+ }
+
+ // add "none"-entry
+ mProviderList.add(0, new OpenPgpProviderEntry("",
+ getContext().getString(R.string.openpgp_list_preference_none),
+ getContext().getResources().getDrawable(R.drawable.ic_action_cancel_launchersize)));
+
// Init ArrayAdapter with OpenPGP Providers
ListAdapter adapter = new ArrayAdapter<OpenPgpProviderEntry>(getContext(),
android.R.layout.select_dialog_singlechoice, android.R.id.text1, mProviderList) {
@@ -101,15 +105,6 @@ public class OpenPgpListPreference extends DialogPreference {
int dp10 = (int) (10 * getContext().getResources().getDisplayMetrics().density + 0.5f);
tv.setCompoundDrawablePadding(dp10);
- // disable if it has the wrong api_version
- if (mProviderList.get(position).apiVersion == OpenPgpConstants.REQUIRED_API_VERSION) {
- tv.setEnabled(true);
- } else {
- tv.setEnabled(false);
- tv.setText(tv.getText() + " (API v" + mProviderList.get(position).apiVersion
- + ", needs v" + OpenPgpConstants.REQUIRED_API_VERSION + ")");
- }
-
return v;
}
};
@@ -171,6 +166,16 @@ public class OpenPgpListPreference extends DialogPreference {
return getEntryByValue(mSelectedPackage);
}
+ @Override
+ protected Object onGetDefaultValue(TypedArray a, int index) {
+ return a.getString(index);
+ }
+
+ @Override
+ protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+ setValue(restoreValue ? getPersistedString(mSelectedPackage) : (String) defaultValue);
+ }
+
public String getEntryByValue(String packageName) {
for (OpenPgpProviderEntry app : mProviderList) {
if (app.packageName.equals(packageName)) {
@@ -185,14 +190,11 @@ public class OpenPgpListPreference extends DialogPreference {
private String packageName;
private String simpleName;
private Drawable icon;
- private int apiVersion;
- public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon,
- int apiVersion) {
+ public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon) {
this.packageName = packageName;
this.simpleName = simpleName;
this.icon = icon;
- this.apiVersion = apiVersion;
}
@Override
diff --git a/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpServiceConnection.java b/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpServiceConnection.java
index f7ba06aaf..c80656c52 100644
--- a/OpenPGP-Keychain/src/main/java/org/openintents/openpgp/OpenPgpServiceConnection.java
+++ b/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpServiceConnection.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.openintents.openpgp;
+package org.openintents.openpgp.util;
import org.openintents.openpgp.IOpenPgpService;
@@ -23,18 +23,17 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
-import android.util.Log;
public class OpenPgpServiceConnection {
private Context mApplicationContext;
- private IOpenPgpService mService;
private boolean mBound;
- private String mCryptoProviderPackageName;
+ private IOpenPgpService mService;
+ private String mProviderPackageName;
- public OpenPgpServiceConnection(Context context, String cryptoProviderPackageName) {
+ public OpenPgpServiceConnection(Context context, String providerPackageName) {
this.mApplicationContext = context.getApplicationContext();
- this.mCryptoProviderPackageName = cryptoProviderPackageName;
+ this.mProviderPackageName = providerPackageName;
}
public IOpenPgpService getService() {
@@ -45,49 +44,45 @@ public class OpenPgpServiceConnection {
return mBound;
}
- private ServiceConnection mCryptoServiceConnection = new ServiceConnection() {
+ private ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IOpenPgpService.Stub.asInterface(service);
- Log.d(OpenPgpConstants.TAG, "connected to service");
mBound = true;
}
public void onServiceDisconnected(ComponentName name) {
mService = null;
- Log.d(OpenPgpConstants.TAG, "disconnected from service");
mBound = false;
}
};
/**
- * If not already bound, bind!
- *
+ * If not already bound, bind to service!
+ *
* @return
*/
public boolean bindToService() {
- if (mService == null && !mBound) { // if not already connected
+ // if not already bound...
+ if (mService == null && !mBound) {
try {
- Log.d(OpenPgpConstants.TAG, "not bound yet");
-
Intent serviceIntent = new Intent();
serviceIntent.setAction(IOpenPgpService.class.getName());
- serviceIntent.setPackage(mCryptoProviderPackageName);
- mApplicationContext.bindService(serviceIntent, mCryptoServiceConnection,
+ // NOTE: setPackage is very important to restrict the intent to this provider only!
+ serviceIntent.setPackage(mProviderPackageName);
+ mApplicationContext.bindService(serviceIntent, mServiceConnection,
Context.BIND_AUTO_CREATE);
return true;
} catch (Exception e) {
- Log.d(OpenPgpConstants.TAG, "Exception on binding", e);
return false;
}
} else {
- Log.d(OpenPgpConstants.TAG, "already bound");
return true;
}
}
public void unbindFromService() {
- mApplicationContext.unbindService(mCryptoServiceConnection);
+ mApplicationContext.unbindService(mServiceConnection);
}
}
diff --git a/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpUtils.java b/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpUtils.java
new file mode 100644
index 000000000..ffecaceba
--- /dev/null
+++ b/libraries/keychain-api-library/src/org/openintents/openpgp/util/OpenPgpUtils.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openintents.openpgp.util;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+
+public class OpenPgpUtils {
+
+ 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 int PARSE_RESULT_NO_PGP = -1;
+ public static final int PARSE_RESULT_MESSAGE = 0;
+ public static final int PARSE_RESULT_SIGNED_MESSAGE = 1;
+
+ public static int parseMessage(String message) {
+ Matcher matcherSigned = PGP_SIGNED_MESSAGE.matcher(message);
+ Matcher matcherMessage = PGP_MESSAGE.matcher(message);
+
+ if (matcherMessage.matches()) {
+ return PARSE_RESULT_MESSAGE;
+ } else if (matcherSigned.matches()) {
+ return PARSE_RESULT_SIGNED_MESSAGE;
+ } else {
+ return PARSE_RESULT_NO_PGP;
+ }
+ }
+
+ public static boolean isAvailable(Context context) {
+ Intent intent = new Intent(OpenPgpConstants.SERVICE_INTENT);
+ List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(intent, 0);
+ if (!resInfo.isEmpty()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+}
diff --git a/libraries/keychain-api-library/src/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java b/libraries/keychain-api-library/src/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java
new file mode 100644
index 000000000..3569caf5b
--- /dev/null
+++ b/libraries/keychain-api-library/src/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * 2013 Flow (http://stackoverflow.com/questions/18212152/transfer-inputstream-to-another-service-across-process-boundaries-with-parcelf)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.openintents.openpgp.util;
+
+import android.os.ParcelFileDescriptor;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class ParcelFileDescriptorUtil {
+
+ public interface IThreadListener {
+ void onThreadFinished(final Thread thread);
+ }
+
+ public static ParcelFileDescriptor pipeFrom(InputStream inputStream, IThreadListener listener)
+ throws IOException {
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ ParcelFileDescriptor readSide = pipe[0];
+ ParcelFileDescriptor writeSide = pipe[1];
+
+ // start the transfer thread
+ new TransferThread(inputStream, new ParcelFileDescriptor.AutoCloseOutputStream(writeSide),
+ listener)
+ .start();
+
+ return readSide;
+ }
+
+ public static ParcelFileDescriptor pipeTo(OutputStream outputStream, IThreadListener listener)
+ throws IOException {
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ ParcelFileDescriptor readSide = pipe[0];
+ ParcelFileDescriptor writeSide = pipe[1];
+
+ // start the transfer thread
+ new TransferThread(new ParcelFileDescriptor.AutoCloseInputStream(readSide), outputStream,
+ listener)
+ .start();
+
+ return writeSide;
+ }
+
+ static class TransferThread extends Thread {
+ final InputStream mIn;
+ final OutputStream mOut;
+ final IThreadListener mListener;
+
+ TransferThread(InputStream in, OutputStream out, IThreadListener listener) {
+ super("ParcelFileDescriptor Transfer Thread");
+ mIn = in;
+ mOut = out;
+ mListener = listener;
+ setDaemon(true);
+ }
+
+ @Override
+ public void run() {
+ byte[] buf = new byte[1024];
+ int len;
+
+ try {
+ while ((len = mIn.read(buf)) > 0) {
+ mOut.write(buf, 0, len);
+ }
+ mOut.flush(); // just to be safe
+ } catch (IOException e) {
+ //Log.e(OpenPgpConstants.TAG, "TransferThread" + getId() + ": writing failed", e);
+ } finally {
+ try {
+ mIn.close();
+ } catch (IOException e) {
+ //Log.e(OpenPgpConstants.TAG, "TransferThread" + getId(), e);
+ }
+ try {
+ mOut.close();
+ } catch (IOException e) {
+ //Log.e(OpenPgpConstants.TAG, "TransferThread" + getId(), e);
+ }
+ }
+ if (mListener != null) {
+ //Log.d(OpenPgpConstants.TAG, "TransferThread " + getId() + " finished!");
+ mListener.onThreadFinished(this);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/libraries/keychain-api-library/src/org/sufficientlysecure/keychain/api/OpenKeychainIntents.java b/libraries/keychain-api-library/src/org/sufficientlysecure/keychain/api/OpenKeychainIntents.java
new file mode 100644
index 000000000..15aceb534
--- /dev/null
+++ b/libraries/keychain-api-library/src/org/sufficientlysecure/keychain/api/OpenKeychainIntents.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.api;
+
+public class OpenKeychainIntents {
+
+ public static final String ENCRYPT = "org.sufficientlysecure.keychain.action.ENCRYPT";
+ public static final String ENCRYPT_EXTRA_TEXT = "text"; // String
+ public static final String ENCRYPT_ASCII_ARMOR = "ascii_armor"; // boolean
+
+ public static final String DECRYPT = "org.sufficientlysecure.keychain.action.DECRYPT";
+ public static final String DECRYPT_EXTRA_TEXT = "text"; // String
+
+ public static final String IMPORT_KEY = "org.sufficientlysecure.keychain.action.IMPORT_KEY";
+ public static final String IMPORT_KEY_EXTRA_KEY_BYTES = "key_bytes"; // byte[]
+
+ public static final String IMPORT_KEY_FROM_KEYSERVER = "org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_KEYSERVER";
+ public static final String IMPORT_KEY_FROM_KEYSERVER_QUERY = "query"; // String
+ public static final String IMPORT_KEY_FROM_KEYSERVER_FINGERPRINT = "fingerprint"; // String
+
+ public static final String IMPORT_KEY_FROM_QR_CODE = "org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE";
+
+}
diff --git a/settings.gradle b/settings.gradle
index 9f4d801a6..f123762aa 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,4 +1,5 @@
include ':OpenPGP-Keychain'
+include ':libraries:keychain-api-library'
include ':libraries:HtmlTextView'
include ':libraries:StickyListHeaders:library'
include ':libraries:AndroidBootstrap'
@@ -8,3 +9,4 @@ include ':libraries:spongycastle:core'
include ':libraries:spongycastle:pg'
include ':libraries:spongycastle:pkix'
include ':libraries:spongycastle:prov'
+include ':libraries:Android-AppMsg:library'