aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitmodules4
-rw-r--r--.travis.yml49
-rw-r--r--.tx/config2
-rw-r--r--CHANGELOG2
-rw-r--r--Graphics/drawables/ic_stat_notify.svg65
-rwxr-xr-xGraphics/get-material-icons.sh11
-rwxr-xr-xGraphics/update-drawables.sh2
-rw-r--r--OpenKeychain-Test/build.gradle121
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java541
-rw-r--r--OpenKeychain/build.gradle164
-rw-r--r--OpenKeychain/src/androidTest/assets/ci.pngbin0 -> 7230 bytes
-rw-r--r--OpenKeychain/src/androidTest/assets/pa.pngbin0 -> 4440 bytes
-rw-r--r--OpenKeychain/src/androidTest/assets/re.pngbin0 -> 7085 bytes
-rw-r--r--OpenKeychain/src/androidTest/assets/valodim.pub.asc1032
-rw-r--r--OpenKeychain/src/androidTest/assets/x.sec.asc156
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/JacocoWorkaroundJUnitRunner.java29
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/TestHelpers.java169
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/actions/CustomActions.java82
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/actions/OrientationChangeAction.java74
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/BitmapMatcher.java63
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/CustomMatchers.java160
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/DrawableMatcher.java128
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/EditTextMatchers.java2
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceTest.java171
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/AsymmetricFileOperationTests.java389
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/AsymmetricTextOperationTests.java185
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/CreateKeyActivityTest.java (renamed from OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/CreateKeyActivityTest.java)16
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/EditKeyTest.java90
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/MiscCryptOperationTests.java293
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/SymmetricTextOperationTests.java188
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareTest.java124
-rw-r--r--OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionViewTest.java91
-rw-r--r--OpenKeychain/src/debug/res/drawable-hdpi/ic_launcher.pngbin0 -> 6863 bytes
-rw-r--r--OpenKeychain/src/debug/res/drawable-mdpi/ic_launcher.pngbin0 -> 4609 bytes
-rw-r--r--OpenKeychain/src/debug/res/drawable-xhdpi/ic_launcher.pngbin0 -> 8887 bytes
-rw-r--r--OpenKeychain/src/debug/res/drawable-xxhdpi/ic_launcher.pngbin0 -> 13422 bytes
-rw-r--r--OpenKeychain/src/debug/res/drawable-xxxhdpi/ic_launcher.pngbin0 -> 16655 bytes
-rw-r--r--OpenKeychain/src/debug/res/values/strings.xml4
-rw-r--r--OpenKeychain/src/main/AndroidManifest.xml114
-rw-r--r--OpenKeychain/src/main/assets/word_confirm_list.txt12032
-rw-r--r--OpenKeychain/src/main/java/android/support/v4/widget/FlingNestedScrollView.java1355
-rw-r--r--OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java76
-rw-r--r--OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPublicKeyDataDecryptorFactoryBuilder.java278
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java60
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java29
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/AppCompatPreferenceActivity.java127
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java82
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/DialogFragmentWorkaround.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java27
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java111
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java21
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java128
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ConsolidateOperation.java48
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java20
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java82
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java385
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java583
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java574
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java177
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java49
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java107
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java85
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java25
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ExportResult.java14
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/GetKeyResult.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ImportKeyResult.java19
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/InputPendingResult.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/KeybaseVerificationResult.java89
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java99
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpEditKeyResult.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/RevokeResult.java123
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java20
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java41
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java78
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java59
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java91
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConstants.java114
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyInputParcel.java162
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java)515
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java69
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java339
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSecurityConstants.java262
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java38
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java74
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java26
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java19
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java52
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java110
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java121
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java115
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java262
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java23
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java16
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java152
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsHeaderFragment.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java24
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java25
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CloudImportService.java384
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ConsolidateInputParcel.java58
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DeleteKeyringParcel.java63
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java111
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java74
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeybaseVerificationParcel.java62
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java752
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java221
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java516
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java159
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java66
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/RevokeKeyringParcel.java66
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java67
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java129
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java83
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java131
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupFragment.java200
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java47
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java165
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java103
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java147
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java33
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java420
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java58
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java16
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyBlankFragment.java90
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyImportFragment.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java)141
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinFragment.java134
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinRepeatFragment.java152
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyWaitFragment.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyWaitFragment.java)4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java214
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java126
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java313
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java228
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java945
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java232
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java234
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java426
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextActivity.java94
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextFragment.java164
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java173
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java103
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java67
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java92
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java624
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java153
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeFragment.java19
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeSymmetricFragment.java86
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java88
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java336
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpMarkdownFragment.java24
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java178
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java60
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java174
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java430
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java43
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java184
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java245
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/OrbotRequiredDialogActivity.java173
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java270
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java575
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/RetryUploadDialogActivity.java88
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java137
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java523
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java194
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java360
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java96
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java779
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java46
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java287
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java)402
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java92
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java29
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java48
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java136
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeySelectableAdapter.java87
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java51
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java47
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java549
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java59
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java172
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java348
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/QueueingCryptoOperationFragment.java93
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddKeyserverDialogFragment.java)181
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEmailDialogFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AdvancedAppSettingsDialogFragment.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java31
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java190
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java29
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java23
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/OrbotStartDialogFragment.java163
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PreferenceInstallDialogFragment.java75
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java78
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SupportInstallDialogFragment.java78
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java30
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java99
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java100
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ExperimentalWordConfirm.java126
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/FormattingUtils.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Highlighter.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/InstallDialogFragmentHelper.java132
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java247
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java61
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ThemeChanger.java83
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/DividerItemDecoration.java106
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperAdapter.java41
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperDragCallback.java92
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperViewHolder.java39
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/RecyclerItemClickListener.java70
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java43
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java28
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java84
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java227
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthView.java30
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java40
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ToolableViewAnimator.java76
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java69
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java124
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java271
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyUpdateHelper.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/OrientationUtils.java85
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableFileCache.java25
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableHashMap.java63
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableProxy.java91
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java205
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareHelper.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/TlsHelper.java51
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java463
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/TorServiceUtils.java156
-rw-r--r--OpenKeychain/src/main/res/anim/fade_in.xml2
-rw-r--r--OpenKeychain/src/main/res/anim/fade_out.xml2
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_add_white_24dp.pngbin0 -> 127 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_apps_black_24dp.pngbin182 -> 96 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_arrow_back_white_24dp.pngbin287 -> 152 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_check_circle_black_48dp.pngbin0 -> 709 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_check_white_24dp.pngbin309 -> 181 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_close_black_24dp.pngbin301 -> 207 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_close_white_24dp.pngbin324 -> 221 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_cloud_black_24dp.pngbin0 -> 296 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_content_copy_black_24dp.pngbin284 -> 195 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_delete_white_24dp.pngbin0 -> 161 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_expand_less_black_24dp.pngbin234 -> 149 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_expand_more_black_24dp.pngbin244 -> 151 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_extension_black_24dp.pngbin0 -> 329 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_folder_white_24dp.pngbin224 -> 135 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_help_black_24dp.pngbin559 -> 458 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_ic_stat_tor.pngbin0 -> 920 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_lock_black_24dp.pngbin397 -> 308 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_lock_white_24dp.pngbin399 -> 309 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.pngbin0 -> 148 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_mode_edit_white_24dp.pngbin351 -> 219 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.pngbin282 -> 195 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_refresh_white_24dp.pngbin531 -> 387 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_reorder_grey_500_24dp.pngbin0 -> 134 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_repeat_white_24dp.pngbin296 -> 198 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_save_white_24dp.pngbin341 -> 247 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_search_white_24dp.pngbin504 -> 396 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_settings_black_24dp.pngbin561 -> 453 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_settings_ethernet_black_24dp.pngbin0 -> 350 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_share_black_24dp.pngbin499 -> 398 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_stat_notify_24dp.pngbin0 -> 919 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_stat_retyped_bad.png (renamed from OpenKeychain/src/main/res/drawable-hdpi/uid_mail_bad.png)bin942 -> 942 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_stat_retyped_ok.png (renamed from OpenKeychain/src/main/res/drawable-hdpi/uid_mail_ok.png)bin1252 -> 1252 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_stat_tor.pngbin0 -> 920 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_stat_tor_off.pngbin0 -> 1090 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_sync_black_24dp.pngbin0 -> 368 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/ic_vpn_key_black_24dp.pngbin381 -> 282 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_add_white_24dp.pngbin0 -> 88 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_apps_black_24dp.pngbin173 -> 84 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_arrow_back_white_24dp.pngbin240 -> 118 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_check_circle_black_48dp.pngbin0 -> 493 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_check_white_24dp.pngbin243 -> 137 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_close_black_24dp.pngbin257 -> 164 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_close_white_24dp.pngbin279 -> 175 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_cloud_black_24dp.pngbin0 -> 212 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_content_copy_black_24dp.pngbin214 -> 130 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_delete_white_24dp.pngbin0 -> 115 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_expand_less_black_24dp.pngbin206 -> 126 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_expand_more_black_24dp.pngbin206 -> 125 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_extension_black_24dp.pngbin0 -> 224 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_folder_white_24dp.pngbin206 -> 122 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_help_black_24dp.pngbin390 -> 298 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_ic_stat_tor.pngbin0 -> 540 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_lock_black_24dp.pngbin291 -> 205 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_lock_white_24dp.pngbin296 -> 208 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.pngbin0 -> 131 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_mode_edit_white_24dp.pngbin272 -> 165 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_play_arrow_white_24dp.pngbin257 -> 157 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_refresh_white_24dp.pngbin346 -> 254 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_reorder_grey_500_24dp.pngbin0 -> 101 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_repeat_white_24dp.pngbin236 -> 133 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_save_white_24dp.pngbin257 -> 168 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_search_white_24dp.pngbin346 -> 247 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_settings_black_24dp.pngbin416 -> 322 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_settings_ethernet_black_24dp.pngbin0 -> 253 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_share_black_24dp.pngbin355 -> 262 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_stat_notify_24dp.pngbin0 -> 654 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_stat_retyped_bad.png (renamed from OpenKeychain/src/main/res/drawable-mdpi/uid_mail_bad.png)bin647 -> 647 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_stat_retyped_ok.png (renamed from OpenKeychain/src/main/res/drawable-mdpi/uid_mail_ok.png)bin862 -> 862 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_stat_tor.pngbin0 -> 540 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_stat_tor_off.pngbin0 -> 727 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_sync_black_24dp.pngbin0 -> 250 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/ic_vpn_key_black_24dp.pngbin293 -> 196 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_add_white_24dp.pngbin0 -> 97 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_apps_black_24dp.pngbin193 -> 105 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.pngbin336 -> 151 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_check_circle_black_48dp.pngbin0 -> 922 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_check_white_24dp.pngbin363 -> 199 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_close_black_24dp.pngbin360 -> 235 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_close_white_24dp.pngbin402 -> 257 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_cloud_black_24dp.pngbin0 -> 357 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_content_copy_black_24dp.pngbin304 -> 180 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_delete_white_24dp.pngbin0 -> 151 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_expand_less_black_24dp.pngbin259 -> 171 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_expand_more_black_24dp.pngbin272 -> 168 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_extension_black_24dp.pngbin0 -> 331 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_folder_white_24dp.pngbin273 -> 181 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_help_black_24dp.pngbin700 -> 579 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_ic_stat_tor.pngbin0 -> 1188 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_lock_black_24dp.pngbin498 -> 365 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_lock_white_24dp.pngbin465 -> 372 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_light.pngbin0 -> 184 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_mode_edit_white_24dp.pngbin378 -> 239 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.pngbin318 -> 220 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.pngbin637 -> 509 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_reorder_grey_500_24dp.pngbin0 -> 138 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_repeat_white_24dp.pngbin314 -> 185 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_save_white_24dp.pngbin359 -> 273 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_search_white_24dp.pngbin591 -> 465 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_settings_black_24dp.pngbin691 -> 557 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_settings_ethernet_black_24dp.pngbin0 -> 447 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_share_black_24dp.pngbin614 -> 483 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_notify_24dp.pngbin0 -> 1249 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_retyped_bad.png (renamed from OpenKeychain/src/main/res/drawable-xhdpi/uid_mail_bad.png)bin1299 -> 1299 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_retyped_ok.png (renamed from OpenKeychain/src/main/res/drawable-xhdpi/uid_mail_ok.png)bin1606 -> 1606 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_tor.pngbin0 -> 1188 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_tor_off.pngbin0 -> 2037 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_sync_black_24dp.pngbin0 -> 467 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/ic_vpn_key_black_24dp.pngbin446 -> 342 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_add_white_24dp.pngbin0 -> 97 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_apps_black_24dp.pngbin213 -> 115 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_arrow_back_white_24dp.pngbin410 -> 188 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_check_circle_black_48dp.pngbin0 -> 1346 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_check_white_24dp.pngbin460 -> 276 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_close_black_24dp.pngbin425 -> 309 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_close_white_24dp.pngbin492 -> 347 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_cloud_black_24dp.pngbin0 -> 518 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_content_copy_black_24dp.pngbin397 -> 258 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_delete_white_24dp.pngbin0 -> 194 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_expand_less_black_24dp.pngbin304 -> 213 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_expand_more_black_24dp.pngbin316 -> 215 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_extension_black_24dp.pngbin0 -> 495 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_folder_white_24dp.pngbin342 -> 245 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_help_black_24dp.pngbin986 -> 834 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_ic_stat_tor.pngbin0 -> 2589 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_lock_black_24dp.pngbin636 -> 527 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.pngbin760 -> 540 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_mode_edit_white_24dp.pngbin490 -> 302 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.pngbin399 -> 283 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.pngbin875 -> 734 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_reorder_grey_500_24dp.pngbin0 -> 186 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_repeat_white_24dp.pngbin397 -> 234 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_save_white_24dp.pngbin489 -> 391 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_search_white_24dp.pngbin871 -> 728 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_settings_black_24dp.pngbin969 -> 827 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_settings_ethernet_black_24dp.pngbin0 -> 649 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_share_black_24dp.pngbin804 -> 675 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_notify_24dp.pngbin0 -> 1948 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_retyped_bad.png (renamed from OpenKeychain/src/main/res/drawable-xxhdpi/uid_mail_bad.png)bin2338 -> 2338 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_retyped_ok.png (renamed from OpenKeychain/src/main/res/drawable-xxhdpi/uid_mail_ok.png)bin2364 -> 2364 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_tor.pngbin0 -> 2589 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_tor_off.pngbin0 -> 3776 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_sync_black_24dp.pngbin0 -> 669 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/ic_vpn_key_black_24dp.pngbin622 -> 476 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.pngbin0 -> 102 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_apps_black_24dp.pngbin236 -> 114 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.pngbin530 -> 231 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_check_circle_black_48dp.pngbin0 -> 1812 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.pngbin587 -> 308 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_close_black_24dp.pngbin565 -> 377 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.pngbin662 -> 436 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_cloud_black_24dp.pngbin0 -> 669 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_content_copy_black_24dp.pngbin480 -> 319 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_delete_white_24dp.pngbin0 -> 243 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_expand_less_black_24dp.pngbin360 -> 261 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_expand_more_black_24dp.pngbin379 -> 256 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_extension_black_24dp.pngbin0 -> 605 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_folder_white_24dp.pngbin504 -> 325 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_help_black_24dp.pngbin1323 -> 1122 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_ic_stat_tor.pngbin0 -> 4105 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_lock_black_24dp.pngbin830 -> 685 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_lock_white_24dp.pngbin971 -> 702 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_mode_edit_white_24dp.pngbin632 -> 355 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.pngbin477 -> 343 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_refresh_white_24dp.pngbin1148 -> 967 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_repeat_white_24dp.pngbin478 -> 257 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_save_white_24dp.pngbin747 -> 504 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.pngbin1090 -> 915 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_settings_black_24dp.pngbin1257 -> 1073 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_settings_ethernet_black_24dp.pngbin0 -> 894 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_share_black_24dp.pngbin1052 -> 888 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_stat_notify_24dp.pngbin0 -> 2710 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_stat_tor.pngbin0 -> 4105 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_stat_tor_off.pngbin0 -> 5960 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_sync_black_24dp.pngbin0 -> 875 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxxhdpi/ic_vpn_key_black_24dp.pngbin789 -> 636 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable/cardview_header.xml4
-rw-r--r--OpenKeychain/src/main/res/drawable/divider.xml2
-rw-r--r--OpenKeychain/src/main/res/drawable/fab_label_background.xml5
-rw-r--r--OpenKeychain/src/main/res/drawable/scrim_bottom.xml8
-rw-r--r--OpenKeychain/src/main/res/drawable/section_header_dark.xml (renamed from OpenKeychain/src/main/res/drawable/section_header.xml)4
-rw-r--r--OpenKeychain/src/main/res/drawable/section_header_light.xml11
-rw-r--r--OpenKeychain/src/main/res/drawable/selector_transparent_button.xml7
-rw-r--r--OpenKeychain/src/main/res/drawable/yubikey_phone.pngbin352625 -> 347902 bytes
-rw-r--r--OpenKeychain/src/main/res/layout/add_user_id_dialog.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/api_app_settings_activity.xml8
-rw-r--r--OpenKeychain/src/main/res/layout/api_app_settings_fragment.xml4
-rw-r--r--OpenKeychain/src/main/res/layout/backup_fragment.xml71
-rw-r--r--OpenKeychain/src/main/res/layout/certify_fingerprint_fragment.xml5
-rw-r--r--OpenKeychain/src/main/res/layout/create_key_email_fragment.xml4
-rw-r--r--OpenKeychain/src/main/res/layout/create_key_final_fragment.xml12
-rw-r--r--OpenKeychain/src/main/res/layout/create_key_name_fragment.xml4
-rw-r--r--OpenKeychain/src/main/res/layout/create_key_passphrase_fragment.xml7
-rw-r--r--OpenKeychain/src/main/res/layout/create_key_start_fragment.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/create_yubi_key_blank_fragment.xml76
-rw-r--r--OpenKeychain/src/main/res/layout/create_yubi_key_import_fragment.xml (renamed from OpenKeychain/src/main/res/layout/create_yubikey_import_fragment.xml)4
-rw-r--r--OpenKeychain/src/main/res/layout/create_yubi_key_pin_fragment.xml109
-rw-r--r--OpenKeychain/src/main/res/layout/create_yubi_key_pin_repeat_fragment.xml110
-rw-r--r--OpenKeychain/src/main/res/layout/create_yubi_key_wait_fragment.xml (renamed from OpenKeychain/src/main/res/layout/create_yubikey_wait_fragment.xml)4
-rw-r--r--OpenKeychain/src/main/res/layout/decrypt_files_activity.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/decrypt_files_fragment.xml149
-rw-r--r--OpenKeychain/src/main/res/layout/decrypt_files_input_fragment.xml83
-rw-r--r--OpenKeychain/src/main/res/layout/decrypt_files_list_fragment.xml49
-rw-r--r--OpenKeychain/src/main/res/layout/decrypt_list_entry.xml323
-rw-r--r--OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml11
-rw-r--r--OpenKeychain/src/main/res/layout/del_rev_dialog.xml24
-rw-r--r--OpenKeychain/src/main/res/layout/drawer_custom_header.xml16
-rw-r--r--OpenKeychain/src/main/res/layout/encrypt_asymmetric_fragment.xml99
-rw-r--r--OpenKeychain/src/main/res/layout/encrypt_decrypt_overview_fragment.xml219
-rw-r--r--OpenKeychain/src/main/res/layout/encrypt_text_fragment.xml7
-rw-r--r--OpenKeychain/src/main/res/layout/file_list_entry.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/foldable_linearlayout.xml4
-rw-r--r--OpenKeychain/src/main/res/layout/help_about_fragment.xml23
-rw-r--r--OpenKeychain/src/main/res/layout/import_keys_activity.xml24
-rw-r--r--OpenKeychain/src/main/res/layout/key_list_content.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/key_list_fragment.xml24
-rw-r--r--OpenKeychain/src/main/res/layout/key_list_item.xml52
-rw-r--r--OpenKeychain/src/main/res/layout/key_list_selectable_item.xml22
-rw-r--r--OpenKeychain/src/main/res/layout/key_server_preference.xml117
-rw-r--r--OpenKeychain/src/main/res/layout/keyspinner_item.xml57
-rw-r--r--OpenKeychain/src/main/res/layout/keyspinner_item_none.xml21
-rw-r--r--OpenKeychain/src/main/res/layout/linked_id_item.xml1
-rw-r--r--OpenKeychain/src/main/res/layout/log_display_item.xml6
-rw-r--r--OpenKeychain/src/main/res/layout/main_activity.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/nfc_activity.xml35
-rw-r--r--OpenKeychain/src/main/res/layout/nfc_operation_activity.xml177
-rw-r--r--OpenKeychain/src/main/res/layout/passphrase_repeat_dialog.xml8
-rw-r--r--OpenKeychain/src/main/res/layout/preference_toolbar.xml (renamed from OpenKeychain/src/main/res/layout/preference_toolbar_activity.xml)5
-rw-r--r--OpenKeychain/src/main/res/layout/recipient_selection_list_entry.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/settings_keyserver_fragment.xml7
-rw-r--r--OpenKeychain/src/main/res/layout/settings_keyserver_item.xml46
-rw-r--r--OpenKeychain/src/main/res/layout/toolbar_inner_layout.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/toolbar_inner_layout_white.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/toolbar_result_decrypt.xml5
-rw-r--r--OpenKeychain/src/main/res/layout/toolbar_standalone_white.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/toolbar_tabs.xml6
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_activity.xml374
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_adv_certs_fragment.xml4
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_adv_keybase_fragment.xml167
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_adv_keybase_proof.xml4
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_adv_share_fragment.xml17
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_adv_user_id_item.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_fragment.xml238
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_yubikey.xml2
-rw-r--r--OpenKeychain/src/main/res/menu/decrypt_item_context_menu.xml22
-rw-r--r--OpenKeychain/src/main/res/menu/encrypt_activity.xml10
-rw-r--r--OpenKeychain/src/main/res/menu/encrypt_file_fragment.xml20
-rw-r--r--OpenKeychain/src/main/res/menu/encrypt_text_fragment.xml12
-rw-r--r--OpenKeychain/src/main/res/menu/key_list.xml5
-rw-r--r--OpenKeychain/src/main/res/menu/key_list_multi.xml19
-rw-r--r--OpenKeychain/src/main/res/menu/key_view.xml8
-rw-r--r--OpenKeychain/src/main/res/menu/keyserver_pref_menu.xml10
-rw-r--r--OpenKeychain/src/main/res/raw-bg/help_changelog.md269
-rw-r--r--OpenKeychain/src/main/res/raw-cs/help_about.md58
-rw-r--r--OpenKeychain/src/main/res/raw-cs/help_changelog.md33
-rw-r--r--OpenKeychain/src/main/res/raw-de/help_about.md60
-rw-r--r--OpenKeychain/src/main/res/raw-de/help_certification.md32
-rw-r--r--OpenKeychain/src/main/res/raw-de/help_changelog.md245
-rw-r--r--OpenKeychain/src/main/res/raw-de/help_start.md16
-rw-r--r--OpenKeychain/src/main/res/raw-es/help_about.md60
-rw-r--r--OpenKeychain/src/main/res/raw-es/help_changelog.md33
-rw-r--r--OpenKeychain/src/main/res/raw-et/help_about.md58
-rw-r--r--OpenKeychain/src/main/res/raw-et/help_changelog.md33
-rw-r--r--OpenKeychain/src/main/res/raw-eu/help_about.md78
-rw-r--r--OpenKeychain/src/main/res/raw-eu/help_certification.md38
-rw-r--r--OpenKeychain/src/main/res/raw-eu/help_changelog.md165
-rw-r--r--OpenKeychain/src/main/res/raw-eu/help_start.md24
-rw-r--r--OpenKeychain/src/main/res/raw-fa/help_about.md (renamed from OpenKeychain/src/main/res/raw-bg/help_about.md)68
-rw-r--r--OpenKeychain/src/main/res/raw-fa/help_certification.md (renamed from OpenKeychain/src/main/res/raw-is/help_certification.md)6
-rw-r--r--OpenKeychain/src/main/res/raw-fa/help_changelog.md (renamed from OpenKeychain/src/main/res/raw-ro/help_changelog.md)41
-rw-r--r--OpenKeychain/src/main/res/raw-fa/help_start.md16
-rw-r--r--OpenKeychain/src/main/res/raw-fi/help_about.md58
-rw-r--r--OpenKeychain/src/main/res/raw-fi/help_changelog.md33
-rw-r--r--OpenKeychain/src/main/res/raw-fr/help_about.md58
-rw-r--r--OpenKeychain/src/main/res/raw-fr/help_certification.md4
-rw-r--r--OpenKeychain/src/main/res/raw-fr/help_changelog.md39
-rw-r--r--OpenKeychain/src/main/res/raw-fr/help_start.md6
-rw-r--r--OpenKeychain/src/main/res/raw-hu/help_about.md (renamed from OpenKeychain/src/main/res/raw-pt/help_about.md)58
-rw-r--r--OpenKeychain/src/main/res/raw-hu/help_certification.md (renamed from OpenKeychain/src/main/res/raw-bg/help_certification.md)0
-rw-r--r--OpenKeychain/src/main/res/raw-hu/help_changelog.md (renamed from OpenKeychain/src/main/res/raw-is/help_changelog.md)33
-rw-r--r--OpenKeychain/src/main/res/raw-hu/help_start.md (renamed from OpenKeychain/src/main/res/raw-bg/help_start.md)0
-rw-r--r--OpenKeychain/src/main/res/raw-is/help_about.md46
-rw-r--r--OpenKeychain/src/main/res/raw-is/help_start.md16
-rw-r--r--OpenKeychain/src/main/res/raw-it/help_about.md68
-rw-r--r--OpenKeychain/src/main/res/raw-it/help_certification.md38
-rw-r--r--OpenKeychain/src/main/res/raw-it/help_changelog.md35
-rw-r--r--OpenKeychain/src/main/res/raw-it/help_start.md24
-rw-r--r--OpenKeychain/src/main/res/raw-ja/help_about.md58
-rw-r--r--OpenKeychain/src/main/res/raw-ja/help_certification.md8
-rw-r--r--OpenKeychain/src/main/res/raw-ja/help_changelog.md33
-rw-r--r--OpenKeychain/src/main/res/raw-nl/help_about.md58
-rw-r--r--OpenKeychain/src/main/res/raw-nl/help_changelog.md33
-rw-r--r--OpenKeychain/src/main/res/raw-pl/help_about.md66
-rw-r--r--OpenKeychain/src/main/res/raw-pl/help_certification.md6
-rw-r--r--OpenKeychain/src/main/res/raw-pl/help_changelog.md33
-rw-r--r--OpenKeychain/src/main/res/raw-pt/help_certification.md28
-rw-r--r--OpenKeychain/src/main/res/raw-pt/help_changelog.md269
-rw-r--r--OpenKeychain/src/main/res/raw-pt/help_start.md16
-rw-r--r--OpenKeychain/src/main/res/raw-ro/help_about.md46
-rw-r--r--OpenKeychain/src/main/res/raw-ro/help_certification.md28
-rw-r--r--OpenKeychain/src/main/res/raw-ro/help_start.md16
-rw-r--r--OpenKeychain/src/main/res/raw-ru/help_about.md58
-rw-r--r--OpenKeychain/src/main/res/raw-ru/help_certification.md10
-rw-r--r--OpenKeychain/src/main/res/raw-ru/help_changelog.md33
-rw-r--r--OpenKeychain/src/main/res/raw-ru/help_start.md22
-rw-r--r--OpenKeychain/src/main/res/raw-sl/help_about.md58
-rw-r--r--OpenKeychain/src/main/res/raw-sl/help_changelog.md33
-rw-r--r--OpenKeychain/src/main/res/raw-sr/help_about.md80
-rw-r--r--OpenKeychain/src/main/res/raw-sr/help_certification.md14
-rw-r--r--OpenKeychain/src/main/res/raw-sr/help_changelog.md35
-rw-r--r--OpenKeychain/src/main/res/raw-sv/help_about.md68
-rw-r--r--OpenKeychain/src/main/res/raw-sv/help_certification.md18
-rw-r--r--OpenKeychain/src/main/res/raw-sv/help_changelog.md53
-rw-r--r--OpenKeychain/src/main/res/raw-sv/help_start.md20
-rw-r--r--OpenKeychain/src/main/res/raw-tr/help_about.md58
-rw-r--r--OpenKeychain/src/main/res/raw-tr/help_changelog.md33
-rw-r--r--OpenKeychain/src/main/res/raw-uk/help_about.md58
-rw-r--r--OpenKeychain/src/main/res/raw-uk/help_changelog.md33
-rw-r--r--OpenKeychain/src/main/res/raw-zh-rTW/help_about.md66
-rw-r--r--OpenKeychain/src/main/res/raw-zh-rTW/help_certification.md32
-rw-r--r--OpenKeychain/src/main/res/raw-zh-rTW/help_changelog.md95
-rw-r--r--OpenKeychain/src/main/res/raw-zh-rTW/help_start.md22
-rw-r--r--OpenKeychain/src/main/res/raw-zh/help_about.md78
-rw-r--r--OpenKeychain/src/main/res/raw-zh/help_certification.md18
-rw-r--r--OpenKeychain/src/main/res/raw-zh/help_changelog.md33
-rw-r--r--OpenKeychain/src/main/res/raw-zh/help_start.md24
-rw-r--r--OpenKeychain/src/main/res/raw/help_about.md35
-rw-r--r--OpenKeychain/src/main/res/raw/help_changelog.md19
-rw-r--r--OpenKeychain/src/main/res/raw/help_faq.md5
-rw-r--r--OpenKeychain/src/main/res/values-cs/strings.xml276
-rw-r--r--OpenKeychain/src/main/res/values-de/strings.xml910
-rw-r--r--OpenKeychain/src/main/res/values-es/strings.xml476
-rw-r--r--OpenKeychain/src/main/res/values-et/strings.xml24
-rw-r--r--OpenKeychain/src/main/res/values-eu/strings.xml834
-rw-r--r--OpenKeychain/src/main/res/values-fa/strings.xml333
-rw-r--r--OpenKeychain/src/main/res/values-fi/strings.xml42
-rw-r--r--OpenKeychain/src/main/res/values-fr/strings.xml546
-rw-r--r--OpenKeychain/src/main/res/values-hu/strings.xml (renamed from OpenKeychain/src/main/res/values-bg/strings.xml)17
-rw-r--r--OpenKeychain/src/main/res/values-is/strings.xml70
-rw-r--r--OpenKeychain/src/main/res/values-it/strings.xml209
-rw-r--r--OpenKeychain/src/main/res/values-ja/strings.xml413
-rw-r--r--OpenKeychain/src/main/res/values-nl/strings.xml350
-rw-r--r--OpenKeychain/src/main/res/values-pl/strings.xml101
-rw-r--r--OpenKeychain/src/main/res/values-pt/strings.xml70
-rw-r--r--OpenKeychain/src/main/res/values-ro/strings.xml70
-rw-r--r--OpenKeychain/src/main/res/values-ru/strings.xml259
-rw-r--r--OpenKeychain/src/main/res/values-sl/strings.xml100
-rw-r--r--OpenKeychain/src/main/res/values-sr/strings.xml569
-rw-r--r--OpenKeychain/src/main/res/values-sv/strings.xml199
-rw-r--r--OpenKeychain/src/main/res/values-tr/strings.xml69
-rw-r--r--OpenKeychain/src/main/res/values-uk/strings.xml67
-rw-r--r--OpenKeychain/src/main/res/values-v21/themes.xml17
-rw-r--r--OpenKeychain/src/main/res/values-zh-rTW/strings.xml364
-rw-r--r--OpenKeychain/src/main/res/values-zh/strings.xml125
-rw-r--r--OpenKeychain/src/main/res/values/arrays.xml20
-rw-r--r--OpenKeychain/src/main/res/values/attr.xml4
-rw-r--r--OpenKeychain/src/main/res/values/attrs.xml22
-rw-r--r--OpenKeychain/src/main/res/values/colors.xml63
-rw-r--r--OpenKeychain/src/main/res/values/strings.xml409
-rw-r--r--OpenKeychain/src/main/res/values/styles.xml26
-rw-r--r--OpenKeychain/src/main/res/values/themes.xml115
-rw-r--r--OpenKeychain/src/main/res/xml/account_desc.xml3
-rw-r--r--OpenKeychain/src/main/res/xml/account_preferences.xml10
-rw-r--r--OpenKeychain/src/main/res/xml/cloud_search_prefs.xml21
-rw-r--r--OpenKeychain/src/main/res/xml/experimental_preferences.xml36
-rw-r--r--OpenKeychain/src/main/res/xml/gui_preferences.xml10
-rw-r--r--OpenKeychain/src/main/res/xml/keyserver_sync_adapter_desc.xml8
-rw-r--r--OpenKeychain/src/main/res/xml/passphrase_preferences.xml (renamed from OpenKeychain/src/main/res/xml/adv_preferences.xml)0
-rw-r--r--OpenKeychain/src/main/res/xml/preference_headers.xml23
-rw-r--r--OpenKeychain/src/main/res/xml/proxy_prefs.xml34
-rw-r--r--OpenKeychain/src/main/res/xml/sync_adapter_desc.xml2
-rw-r--r--OpenKeychain/src/main/res/xml/sync_preferences.xml10
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/WorkaroundBuildConfig.java16
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java)65
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java)14
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java)75
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/KeyRingTest.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/KeyRingTest.java)8
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java845
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java)118
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java)21
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java)26
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java)14
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperKeyringTest.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperKeyringTest.java)15
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperSaveTest.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperSaveTest.java)27
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/support/KeyringBuilder.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringBuilder.java)0
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java)0
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/support/ProviderHelperStub.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/ProviderHelperStub.java)0
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/support/TestDataUtil.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/TestDataUtil.java)0
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/util/Iso7816TLVTest.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/Iso7816TLVTest.java)8
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/util/ParcelableFileCacheTest.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/ParcelableFileCacheTest.java)11
-rw-r--r--OpenKeychain/src/test/java/org/sufficientlysecure/keychain/util/TestingUtils.java (renamed from OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/TestingUtils.java)0
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/COPYING (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/COPYING)0
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/README (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/README)0
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000001-006.public_key (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000001-006.public_key)bin171 -> 171 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000002-013.user_id (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000002-013.user_id)0
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000003-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000003-002.sig)bin113 -> 113 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000004-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000004-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000005-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000005-002.sig)bin113 -> 113 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000006-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000006-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000007-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000007-002.sig)bin220 -> 220 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000008-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000008-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000009-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000009-002.sig)bin158 -> 158 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000010-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000010-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000011-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000011-002.sig)bin96 -> 96 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000012-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000012-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000013-014.public_subkey (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000013-014.public_subkey)bin171 -> 171 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000014-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000014-002.sig)bin195 -> 195 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000015-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000015-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000016-006.public_key (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000016-006.public_key)bin1201 -> 1201 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000017-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000017-002.sig)bin123 -> 123 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000018-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000018-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000019-013.user_id (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000019-013.user_id)0
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000020-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000020-002.sig)bin130 -> 130 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000021-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000021-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000022-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000022-002.sig)bin186 -> 186 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000023-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000023-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000024-014.public_subkey (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000024-014.public_subkey)bin608 -> 608 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000025-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000025-002.sig)bin105 -> 105 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000026-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000026-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000027-006.public_key (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000027-006.public_key)bin421 -> 421 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000028-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000028-002.sig)bin99 -> 99 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000029-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000029-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000030-013.user_id (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000030-013.user_id)0
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000031-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000031-002.sig)bin132 -> 132 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000032-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000032-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000033-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000033-002.sig)bin96 -> 96 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000034-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000034-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000035-006.public_key (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000035-006.public_key)bin143 -> 143 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000036-013.user_id (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000036-013.user_id)0
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000037-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000037-002.sig)bin192 -> 192 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000038-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000038-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000039-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000039-002.sig)bin72 -> 72 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000040-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000040-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000041-017.attribute (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000041-017.attribute)bin1761 -> 1761 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000042-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000042-002.sig)bin192 -> 192 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000043-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000043-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000044-014.public_subkey (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000044-014.public_subkey)bin272 -> 272 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000045-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000045-002.sig)bin161 -> 161 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000046-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000046-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000047-005.secret_key (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000047-005.secret_key)bin610 -> 610 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000048-013.user_id (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000048-013.user_id)0
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000049-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000049-002.sig)bin220 -> 220 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000050-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000050-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000051-007.secret_subkey (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000051-007.secret_subkey)bin611 -> 611 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000052-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000052-002.sig)bin195 -> 195 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000053-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000053-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000054-005.secret_key (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000054-005.secret_key)bin1275 -> 1275 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000055-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000055-002.sig)bin123 -> 123 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000056-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000056-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000057-013.user_id (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000057-013.user_id)0
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000058-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000058-002.sig)bin130 -> 130 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000059-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000059-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000060-007.secret_subkey (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000060-007.secret_subkey)bin698 -> 698 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000061-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000061-002.sig)bin104 -> 104 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000062-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000062-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000063-005.secret_key (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000063-005.secret_key)bin484 -> 484 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000064-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000064-002.sig)bin99 -> 99 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000065-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000065-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000066-013.user_id (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000066-013.user_id)0
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000067-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000067-002.sig)bin106 -> 106 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000068-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000068-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000069-005.secret_key (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000069-005.secret_key)bin513 -> 513 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000070-013.user_id (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000070-013.user_id)0
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000071-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000071-002.sig)bin192 -> 192 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000072-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000072-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000073-017.attribute (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000073-017.attribute)bin1761 -> 1761 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000074-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000074-002.sig)bin192 -> 192 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000075-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000075-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000076-007.secret_subkey (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000076-007.secret_subkey)bin961 -> 961 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000077-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000077-002.sig)bin161 -> 161 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000078-012.ring_trust (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000078-012.ring_trust)bin4 -> 4 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/002182-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/002182-002.sig)bin363 -> 363 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000005-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000005-002.sig)bin1089 -> 1089 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000017-002.sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000017-002.sig)bin1089 -> 1089 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-bzip2.gpg (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-bzip2.gpg)bin442 -> 442 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-zlib.gpg (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-zlib.gpg)bin322 -> 322 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig.gpg (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig.gpg)bin324 -> 324 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/onepass_sig (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/onepass_sig)bin15 -> 15 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/pubring.gpg (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/pubring.gpg)bin179272 -> 179272 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/secring.gpg (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/secring.gpg)bin9329 -> 9329 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/symmetrically_encrypted (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/symmetrically_encrypted)bin528 -> 528 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa-sha384.txt.gpg (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa-sha384.txt.gpg)bin150 -> 150 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa.gpg (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa.gpg)bin150 -> 150 bytes
-rw-r--r--OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-rsa.gpg (renamed from OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-rsa.gpg)bin236 -> 236 bytes
-rw-r--r--OpenKeychain/src/test/resources/public-key-canonicalize.blob (renamed from OpenKeychain-Test/src/test/resources/public-key-canonicalize.blob)bin1224 -> 1224 bytes
-rw-r--r--OpenKeychain/src/test/resources/public-key-for-sample.blob (renamed from OpenKeychain-Test/src/test/resources/public-key-for-sample.blob)bin35198 -> 35198 bytes
-rw-r--r--OpenKeychain/src/test/resources/sample-altered.txt (renamed from OpenKeychain-Test/src/test/resources/sample-altered.txt)0
-rw-r--r--OpenKeychain/src/test/resources/sample.txt (renamed from OpenKeychain-Test/src/test/resources/sample.txt)0
-rw-r--r--OpenKeychain/src/test/resources/test-keys/bad_user_id_encoding.asc (renamed from OpenKeychain-Test/src/test/resources/test-keys/bad_user_id_encoding.asc)0
-rw-r--r--OpenKeychain/src/test/resources/test-keys/broken_cert_version.asc (renamed from OpenKeychain-Test/src/test/resources/test-keys/broken_cert_version.asc)0
-rw-r--r--OpenKeychain/src/test/resources/test-keys/cooperpair/9E669861368BCA0BE42DAF7DDDA252EBB8EBE1AF.asc (renamed from OpenKeychain-Test/src/test/resources/test-keys/cooperpair/9E669861368BCA0BE42DAF7DDDA252EBB8EBE1AF.asc)0
-rw-r--r--OpenKeychain/src/test/resources/test-keys/cooperpair/A55120427374F3F7AA5F1166DDA252EBB8EBE1AF.asc (renamed from OpenKeychain-Test/src/test/resources/test-keys/cooperpair/A55120427374F3F7AA5F1166DDA252EBB8EBE1AF.asc)0
-rw-r--r--OpenKeychain/src/test/resources/test-keys/cooperpair/readme (renamed from OpenKeychain-Test/src/test/resources/test-keys/cooperpair/readme)0
-rw-r--r--OpenKeychain/src/test/resources/test-keys/divert_to_card_sec.asc (renamed from OpenKeychain-Test/src/test/resources/test-keys/divert_to_card_sec.asc)0
-rw-r--r--OpenKeychain/src/test/resources/test-keys/mailvelope_07_no_key_flags.asc (renamed from OpenKeychain-Test/src/test/resources/test-keys/mailvelope_07_no_key_flags.asc)0
-rw-r--r--OpenKeychain/src/test/resources/test-keys/stripped_flags.asc (renamed from OpenKeychain-Test/src/test/resources/test-keys/stripped_flags.asc)0
-rw-r--r--OpenKeychain/src/test/resources/test-keys/symantec_public.asc (renamed from OpenKeychain-Test/src/test/resources/test-keys/symantec_public.asc)0
-rw-r--r--OpenKeychain/src/test/resources/test-keys/symantec_secret.asc (renamed from OpenKeychain-Test/src/test/resources/test-keys/symantec_secret.asc)0
-rw-r--r--README.md43
-rw-r--r--build.gradle16
m---------extern/KeybaseLib0
m---------extern/openkeychain-api-lib0
m---------extern/openpgp-api-lib0
m---------extern/safeslinger-exchange0
m---------extern/spongycastle0
-rw-r--r--gradle/wrapper/gradle-wrapper.properties6
-rw-r--r--settings.gradle3
-rwxr-xr-xtools/android-wait-for-emulator25
-rwxr-xr-xtools/checkstyle2
-rw-r--r--tools/reset_yubikey.txt13
772 files changed, 45264 insertions, 15112 deletions
diff --git a/.gitmodules b/.gitmodules
index 9d4e0f9dd..0855c2f2a 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,11 +4,11 @@
ignore = dirty
[submodule "extern/openpgp-api-lib"]
path = extern/openpgp-api-lib
- url = https://github.com/open-keychain/openpgp-api-lib.git
+ url = https://github.com/open-keychain/openpgp-api.git
ignore = dirty
[submodule "extern/openkeychain-api-lib"]
path = extern/openkeychain-api-lib
- url = https://github.com/open-keychain/openkeychain-api-lib.git
+ url = https://github.com/open-keychain/openkeychain-intents.git
ignore = dirty
[submodule "extern/KeybaseLib"]
path = extern/KeybaseLib
diff --git a/.travis.yml b/.travis.yml
index 4bc9d776a..e4433144b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,19 +1,36 @@
-language: java
+language: android
jdk: openjdk7
-before_install:
- # Install base Android SDK
- - sudo apt-get update -qq
- - if [ `uname -m` = x86_64 ]; then sudo apt-get install -qq --force-yes libgd2-xpm lib32z1 lib32stdc++6; fi
- - wget http://dl.google.com/android/android-sdk_r24.1.2-linux.tgz
- - tar xzf android-sdk_r24.1.2-linux.tgz
- - export ANDROID_HOME=$PWD/android-sdk-linux
- - export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools
+# container based build, we don't need root anyways
+sudo: false
+# env:
+ # global:
+ # - ANDROID_API_LEVEL=21
+ # - ANDROID_ABI=armeabi-v7a
+ # - ADB_INSTALL_TIMEOUT=8 # minutes (2 minutes by default)
+android:
+ components:
+ - build-tools-22.0.1
+ - build-tools-21.1.2
+ - build-tools-21.1.1
+ - build-tools-19.1.0
+ - android-22
+ - android-21
+ - android-19
+ - platform-tools
+ - extra-android-support
+ - extra-android-m2repository
+ licenses:
+ - 'android-sdk-preview-license-52d11cd2'
+ - 'android-sdk-license-.+'
+ - 'google-gdk-license-.+'
- # Install required Android components.
- #- echo "y" | android update sdk -a --filter build-tools-19.1.0,android-19,platform-tools,extra-android-support,extra-android-m2repository --no-ui --force
- - ( sleep 5 && while [ 1 ]; do sleep 1; echo y; done ) | android update sdk --no-ui --all --force --filter build-tools-22.0.1,build-tools-21.1.2,build-tools-21.1.1,build-tools-19.1.0,android-22,android-21,android-19,platform-tools,extra-android-support,extra-android-m2repository
-install: echo "Installation done"
-script:
- - ./gradlew assemble -S -q
- - ./gradlew --info OpenKeychain-Test:testDebug
+# doesn't work, travis is just too slow
+# before_script:
+# - echo no | android create avd --force -n test -t android-$ANDROID_API_LEVEL --abi $ANDROID_ABI
+# - emulator -avd test -no-skin -no-audio -no-window &
+# - ./tools/android-wait-for-emulator
+# - adb shell input keyevent 82 &
+script:
+ # - ./gradlew connectedAndroidTest
+ - ./gradlew testDebug jacocoTestReport coveralls
diff --git a/.tx/config b/.tx/config
index 16292a787..f3a8543aa 100644
--- a/.tx/config
+++ b/.tx/config
@@ -1,6 +1,6 @@
[main]
host = https://www.transifex.com
-lang_map = af_ZA: af-rZA, am_ET: am-rET, ar_AE: ar-rAE, ar_BH: ar-rBH, ar_DZ: ar-rDZ, ar_EG: ar-rEG, ar_IQ: ar-rIQ, ar_JO: ar-rJO, ar_KW: ar-rKW, ar_LB: ar-rLB, ar_LY: ar-rLY, ar_MA: ar-rMA, ar_OM: ar-rOM, ar_QA: ar-rQA, ar_SA: ar-rSA, ar_SY: ar-rSY, ar_TN: ar-rTN, ar_YE: ar-rYE, arn_CL: arn-rCL, as_IN: as-rIN, az_AZ: az-rAZ, ba_RU: ba-rRU, be_BY: be-rBY, bg_BG: bg-rBG, bn_BD: bn-rBD, bn_IN: bn-rIN, bo_CN: bo-rCN, br_FR: br-rFR, bs_BA: bs-rBA, ca_ES: ca-rES, co_FR: co-rFR, cs_CZ: cs-rCZ, cy_GB: cy-rGB, da_DK: da-rDK, de_AT: de-rAT, de_CH: de-rCH, de_DE: de-rDE, de_LI: de-rLI, de_LU: de-rLU, dsb_DE: dsb-rDE, dv_MV: dv-rMV, el_GR: el-rGR, en_AU: en-rAU, en_BZ: en-rBZ, en_CA: en-rCA, en_GB: en-rGB, en_IE: en-rIE, en_IN: en-rIN, en_JM: en-rJM, en_MY: en-rMY, en_NZ: en-rNZ, en_PH: en-rPH, en_SG: en-rSG, en_TT: en-rTT, en_US: en-rUS, en_ZA: en-rZA, en_ZW: en-rZW, es_AR: es-rAR, es_BO: es-rBO, es_CL: es-rCL, es_CO: es-rCO, es_CR: es-rCR, es_DO: es-rDO, es_EC: es-rEC, es_ES: es-rES, es_GT: es-rGT, es_HN: es-rHN, es_MX: es-rMX, es_NI: es-rNI, es_PA: es-rPA, es_PE: es-rPE, es_PR: es-rPR, es_PY: es-rPY, es_SV: es-rSV, es_US: es-rUS, es_UY: es-rUY, es_VE: es-rVE, et_EE: et-rEE, eu_ES: eu-rES, fa_IR: fa-rIR, fi_FI: fi-rFI, fil_PH: fil-rPH, fo_FO: fo-rFO, fr_BE: fr-rBE, fr_CA: fr-rCA, fr_CH: fr-rCH, fr_FR: fr-rFR, fr_LU: fr-rLU, fr_MC: fr-rMC, fy_NL: fy-rNL, ga_IE: ga-rIE, gd_GB: gd-rGB, gl_ES: gl-rES, gsw_FR: gsw-rFR, gu_IN: gu-rIN, ha_NG: ha-rNG, hi_IN: hi-rIN, hr_BA: hr-rBA, hr_HR: hr-rHR, hsb_DE: hsb-rDE, hu_HU: hu-rHU, hy_AM: hy-rAM, id_ID: id-rID, ig_NG: ig-rNG, ii_CN: ii-rCN, is_IS: is-rIS, it_CH: it-rCH, it_IT: it-rIT, iu_CA: iu-rCA, ja_JP: ja-rJP, ka_GE: ka-rGE, kk_KZ: kk-rKZ, kl_GL: kl-rGL, km_KH: km-rKH, kn_IN: kn-rIN, ko_KR: ko-rKR, kok_IN: kok-rIN, ky_KG: ky-rKG, lb_LU: lb-rLU, lo_LA: lo-rLA, lt_LT: lt-rLT, lv_LV: lv-rLV, mi_NZ: mi-rNZ, mk_MK: mk-rMK, ml_IN: ml-rIN, mn_CN: mn-rCN, mn_MN: mn-rMN, moh_CA: moh-rCA, mr_IN: mr-rIN, ms_BN: ms-rBN, ms_MY: ms-rMY, mt_MT: mt-rMT, nb_NO: nb-rNO, ne_NP: ne-rNP, nl_BE: nl-rBE, nl_NL: nl-rNL, nn_NO: nn-rNO, nso_ZA: nso-rZA, oc_FR: oc-rFR, or_IN: or-rIN, pa_IN: pa-rIN, pl_PL: pl-rPL, prs_AF: prs-rAF, ps_AF: ps-rAF, pt_BR: pt-rBR, pt_PT: pt-rPT, qut_GT: qut-rGT, quz_BO: quz-rBO, quz_EC: quz-rEC, quz_PE: quz-rPE, rm_CH: rm-rCH, ro_RO: ro-rRO, ru_RU: ru-rRU, rw_RW: rw-rRW, sa_IN: sa-rIN, sah_RU: sah-rRU, se_FI: se-rFI, se_NO: se-rNO, se_SE: se-rSE, si_LK: si-rLK, sk_SK: sk-rSK, sl_SI: sl-rSI, sma_NO: sma-rNO, sma_SE: sma-rSE, smj_NO: smj-rNO, smj_SE: smj-rSE, smn_FI: smn-rFI, sms_FI: sms-rFI, sq_AL: sq-rAL, sr_BA: sr-rBA, sr_CS: sr-rCS, sr_ME: sr-rME, sr_RS: sr-rRS, sv_FI: sv-rFI, sv_SE: sv-rSE, sw_KE: sw-rKE, syr_SY: syr-rSY, ta_IN: ta-rIN, te_IN: te-rIN, tg_TJ: tg-rTJ, th_TH: th-rTH, tk_TM: tk-rTM, tn_ZA: tn-rZA, tr_TR: tr-rTR, tt_RU: tt-rRU, tzm_DZ: tzm-rDZ, ug_CN: ug-rCN, uk_UA: uk-rUA, ur_PK: ur-rPK, uz_UZ: uz-rUZ, vi_VN: vi-rVN, wo_SN: wo-rSN, xh_ZA: xh-rZA, yo_NG: yo-rNG, zh_CN: zh-rCN, zh_HK: zh-rHK, zh_MO: zh-rMO, zh_SG: zh-rSG, zh_TW: zh-rTW, zu_ZA: zu-rZA, no_NO: no-rNO, he_IL: iw-rIL, he: iw
+lang_map = he: iw, zh_TW: zh-rTW
[open-keychain.strings]
file_filter = OpenKeychain/src/main/res/values-<lang>/strings.xml
diff --git a/CHANGELOG b/CHANGELOG
index 21ef4ce47..3d6145672 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1 +1 @@
-Please go to https://github.com/open-keychain/open-keychain/blob/development/OpenKeychain/src/main/res/raw/help_changelog.md \ No newline at end of file
+Please go to https://github.com/open-keychain/open-keychain/blob/HEAD/OpenKeychain/src/main/res/raw/help_changelog.md \ No newline at end of file
diff --git a/Graphics/drawables/ic_stat_notify.svg b/Graphics/drawables/ic_stat_notify.svg
new file mode 100644
index 000000000..ae82d7147
--- /dev/null
+++ b/Graphics/drawables/ic_stat_notify.svg
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ x="0px"
+ y="0px"
+ width="626px"
+ height="626px"
+ viewBox="0 0 626 626"
+ enable-background="new 0 0 626 626"
+ xml:space="preserve"
+ id="svg2"
+ inkscape:version="0.48.5 r10040"
+ sodipodi:docname="ic_stat_notify.svg"><metadata
+ id="metadata36"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
+ id="defs34" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="2558"
+ inkscape:window-height="1419"
+ id="namedview32"
+ showgrid="false"
+ inkscape:zoom="1.066308"
+ inkscape:cx="338.58371"
+ inkscape:cy="291.05308"
+ inkscape:window-x="0"
+ inkscape:window-y="19"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" /><rect
+ style="fill:#000000;fill-opacity:1;stroke:none"
+ id="rect3061"
+ width="991.27081"
+ height="833.71783"
+ x="-1041.9932"
+ y="-121.61116" /><path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="M 313 67.96875 C 177.67445 67.96875 67.96875 177.67445 67.96875 313 C 67.96875 448.32555 177.67445 558.03125 313 558.03125 C 346.28067 558.03125 378.00686 551.39443 406.9375 539.375 L 361.03125 494.28125 L 349.28125 481.6875 C 337.58171 484.19233 325.44744 485.5 313 485.5 C 217.73326 485.5 140.5 408.26674 140.5 313 C 140.5 217.73326 217.73326 140.5 313 140.5 C 408.26674 140.5 485.5 217.73326 485.5 313 C 485.5 335.18469 481.31599 356.39409 473.6875 375.875 C 501.45629 405.73338 520.78366 426.83966 526.625 433.09375 C 546.61998 397.60419 558.03125 356.63734 558.03125 313 C 558.03125 177.67445 448.32555 67.96875 313 67.96875 z M 464.3125 395.90625 C 448.62246 424.4791 425.05924 448.1265 396.5625 463.9375 C 419.18842 453.23201 445.28941 433.65488 464.3125 395.90625 z "
+ id="path4081" /><path
+ style="fill:#94c061;fill-rule:evenodd"
+ inkscape:connector-curvature="0"
+ id="path14"
+ d="m 415.298,-360.17398 -79.39409,79.13921 c -47.06722,-48.09353 -70.60084,-105.92166 -70.60084,-173.48607 0,-0.28376 0,-0.53694 0,-0.76463 0.15123,-61.1806 19.69177,-114.42094 58.62164,-159.72272 3.23693,-3.76198 6.6064,-7.47128 10.11011,-11.12961 1.36784,-1.42222 2.75606,-2.83933 4.16298,-4.24795 7.6463,-7.6463 15.60525,-14.72677 23.87345,-21.23972 0.28037,-0.2209 0.56413,-0.44859 0.84959,-0.67967 0.57093,-0.44859 1.13675,-0.88697 1.69918,-1.31687 0.26167,-0.2022 0.51655,-0.3993 0.76463,-0.59471 l 22.34419,-15.20764 c 1.27778,-0.77823 2.56746,-1.54286 3.86563,-2.29389 l 0,0.0425 c 0.69666,-0.40441 1.38993,-0.81391 2.08149,-1.23191 l 2.08149,-1.14694 c 0.79692,-0.4248 1.58874,-0.84959 2.37885,-1.27438 7.68708,-4.10522 15.56108,-7.75675 23.61858,-10.9597 6.87317,-2.72039 13.88398,-5.11283 21.02732,-7.17903 1.99994,-0.57772 4.01006,-1.12995 6.03208,-1.6567 20.62972,-5.38129 42.3775,-8.07109 65.24844,-8.07109 15.63073,0 30.72623,1.27438 45.28139,3.82315 5.09753,0.84959 10.19507,1.89798 15.2926,3.14348 12.84069,3.08401 25.25828,7.18922 37.25447,12.31904 23.88359,7.88391 -10.10015,24.79841 4.817,34.16003 -4.83471,78.85212 20.58246,73.23946 -44.79016,57.68053 -0.3959,-0.11214 -0.76463,-0.1988 -1.10446,-0.25487 -2.43662,-0.56583 -4.9548,-1.07558 -7.56134,-1.52926 -0.33814,-0.0561 -0.59472,-0.085 -0.76293,-0.085 -9.00735,-1.18943 -14.01822,-1.81303 -15.03943,-1.8691 l -0.085,0 c -32.2283,-2.15286 -61.39809,4.72881 -87.50766,20.64501 -7.19262,4.36179 -14.13036,9.43044 -20.81493,15.20765 -0.16992,0.11214 -0.28376,0.25487 -0.33983,0.42479 -0.22599,0.16992 -0.48087,0.39591 -0.76463,0.67967 -0.90057,0.78502 -1.79433,1.57854 -2.67451,2.37885 -3.48501,3.17576 -6.76952,6.46197 -9.85523,9.85523 -2.0628,-2.54707 -4.00156,-5.15191 -5.81969,-7.81622 1.73147,-1.93536 3.03304,-3.9166 3.90811,-5.94712 0.73745,-1.47489 1.33216,-3.00415 1.78414,-4.58778 0.79352,-2.6762 1.18943,-5.50704 1.18943,-8.49589 0,-7.49337 -2.46381,-14.00802 -7.39143,-19.54054 l -0.25488,-0.29736 c -0.12064,-0.11215 -0.27696,-0.26847 -0.46727,-0.46727 -0.11724,-0.12914 -0.24468,-0.27017 -0.38231,-0.4248 -0.0867,-0.0867 -0.17332,-0.17162 -0.25488,-0.25488 -1.07558,-1.07558 -2.23782,-2.0679 -3.48332,-2.97356 -3.84014,-2.8801 -8.10168,-4.63535 -12.78631,-5.26745 l -0.0425,0 c -0.73404,-0.16992 -1.52586,-0.25318 -2.37885,-0.25488 -0.73744,-0.11214 -1.47318,-0.16991 -2.20893,-0.16991 -0.56583,0 -1.10446,0.0578 -1.61422,0.16991 -1.13335,0 -2.23782,0.085 -3.31339,0.25488 l -0.085,0 c -0.43499,0.0697 -0.85979,0.15463 -1.27439,0.25488 l -3.73819,1.0195 c -0.74764,0.28037 -1.48508,0.59132 -2.20893,0.93455 -0.1988,0.085 -0.39591,0.16992 -0.59471,0.25488 -2.94128,1.35594 -5.66166,3.28111 -8.15606,5.7772 -0.33983,0.33984 -0.65078,0.70856 -0.93454,1.10447 -0.10535,0.11215 -0.20391,0.22599 -0.29736,0.33984 -3.23523,3.59715 -5.40169,7.6463 -6.49936,12.14912 -0.56582,2.37885 -0.84958,4.84265 -0.84958,7.39142 0,5.9896 1.54285,11.32842 4.63026,16.01475 1.11466,1.68049 2.43152,3.28112 3.95058,4.80018 2.57766,2.57765 5.3813,4.58778 8.41093,6.03208 3.82825,1.81302 8.02012,2.71869 12.57392,2.71869 3.58357,0 6.93944,-0.55224 10.06763,-1.6567 2.71699,3.67532 5.66336,7.25719 8.83573,10.7473 0.0663,-0.0901 0.13763,-0.17502 0.21239,-0.25488 -0.6066,0.81221 -1.20302,1.63291 -1.78413,2.46381 -1.18943,1.69918 -2.29389,3.39836 -3.3134,5.09753 -2.71869,4.1358 -5.23857,8.43982 -7.56134,12.91545 -0.0272,0.0561 -0.0561,0.11215 -0.085,0.16992 -1.72297,3.56997 -3.33719,7.16544 -4.84266,10.78978 -1.07558,2.72039 -2.0662,5.49344 -2.97356,8.32597 -3.85204,11.83818 -6.25807,24.44098 -7.22151,37.80671 -2.58614,36.25366 6.46198,68.69437 27.14437,97.31872 z m 272.71805,-271.95343 c -67.56645,59.01822 -26.3037,18.5581 -51.23021,-39.29349 18.15062,10.49243 35.22566,23.59139 51.23021,39.29349 z m -170.04522,100.93117 c -0.0969,-0.11724 -0.19541,-0.23109 -0.29736,-0.33983 0.43159,0.29905 0.85638,0.61 1.27438,0.93454 -0.32284,-0.2056 -0.64738,-0.4044 -0.97702,-0.59471 z"
+ clip-rule="evenodd"
+ sodipodi:nodetypes="ccscccccccccccccccccscccccccccccccccccccscccccccccscccccccccccscscsccccccccccccccccccc" /><path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 311.15625,162.40625 c -27.54488,0.0769 -52.73447,7.08376 -75.5625,21.03125 -7.17053,4.38388 -14.10585,9.46363 -20.8125,15.21875 -0.16822,0.11215 -0.28765,0.26759 -0.34375,0.4375 -0.22599,0.16992 -0.46624,0.37248 -0.75,0.65625 -9.23333,8.0439 -17.18571,16.79238 -23.8125,26.25 -1.18942,1.69918 -2.29299,3.42582 -3.3125,5.125 -2.71868,4.1358 -5.23972,8.43061 -7.5625,12.90625 -1.75525,3.62435 -3.37699,7.25708 -4.90625,10.9375 -1.07728,2.73228 -2.09433,5.52651 -3,8.34375 -3.85373,11.84497 -6.25872,24.45356 -7.21875,37.8125 -2.46551,34.57997 5.6511,65.67368 24.34375,93.3125 3.64304,5.35072 7.69695,10.58657 12.125,15.6875 0.13594,0.15293 0.25842,0.31243 0.40625,0.46875 1.79603,2.05431 3.6428,4.02828 5.5,5.96875 3.69742,3.78237 7.48456,7.32689 11.40625,10.625 4.70162,4.02025 9.66532,7.72022 14.875,11.0625 17.04786,11.15681 36.11939,18.37004 57.1875,21.65625 10.23118,0.92672 14.45425,0.67625 25.1875,-0.8125 2.59124,-0.15462 5.03797,-0.3501 7.34375,-0.5625 8.66581,-0.79182 17.49435,-2.01315 26.5,-3.65625 l 2.375,2.40625 1.03125,1 58.4375,58.53125 25.9375,25.9375 -0.0937,0 34.15625,34.15625 c 1.99144,2.80364 4.23455,5.32638 6.71875,7.5625 10.3293,9.25882 24.91593,13.56177 43.8125,12.96875 19.99422,-0.68137 35.04377,-1.86332 45.125,-3.5625 1.6414,-0.34153 3.14066,-0.66016 4.5,-1 1.58703,-0.22769 3.04691,-0.81309 4.40625,-1.71875 0.51146,-0.33984 1.05917,-0.69784 1.625,-1.09375 l 0.25,-0.5 c 1.41541,-1.69918 2.32284,-3.54831 2.71875,-5.53125 2.32278,-9.91131 3.89398,-26.54019 4.6875,-49.875 0.50296,-17.38259 -3.67277,-31.01127 -12.5,-40.875 -2.13756,-2.36526 -4.54085,-4.49704 -7.21875,-6.4375 l -45.28125,11.875 -0.15625,0.1875 0,-0.1875 c -0.0748,0.0119 -0.15245,0.0455 -0.21875,0.0625 -0.60661,0.0663 -1.19437,0.0262 -1.75,-0.125 -0.56582,-0.33984 -1.00391,-0.80918 -1.34375,-1.375 -0.11382,-0.11215 -0.1875,-0.23633 -0.1875,-0.40625 -0.22773,-0.56752 -0.26366,-1.15292 -0.0937,-1.71875 l 0.0937,-0.0937 L 527.25,452.5 c 0.16817,-0.73574 0.13394,-1.44533 -0.0937,-2.125 -0.45198,-0.6219 -0.96543,-1.05998 -1.53125,-1.34375 -0.6236,-0.45363 -1.2938,-0.48966 -2.03125,-0.0937 l -43.03125,11.3125 -0.0312,0 0,0.0625 c -0.6236,0.11214 -1.28908,0.0714 -1.96875,-0.15625 -0.32624,-0.16142 -0.60417,-0.39864 -0.84375,-0.6875 -0.1954,-0.22259 -0.35726,-0.46284 -0.5,-0.75 l 0,-0.25 c -0.22769,-0.56582 -0.22769,-1.15766 0,-1.78125 l -0.0937,0.15625 11.8125,-43.40625 c 0.17162,-0.67797 0.0898,-1.38756 -0.25,-2.125 -0.28377,-0.6219 -0.71542,-1.17132 -1.28125,-1.625 -0.22769,-0.0578 -13.86279,-14.91591 -40.9375,-44.59375 3.34229,-6.51465 6.85725,-17.19233 10.59375,-32.03125 0.54372,-2.20043 1.05311,-4.36753 1.5,-6.5 0.92944,-4.52151 1.63902,-8.89981 2.125,-13.15625 0.79521,-7.17734 0.95132,-13.99931 0.46875,-20.4375 0.0561,-0.6236 0.0561,-1.33148 0,-2.125 -0.67967,-4.47563 -1.50701,-8.92418 -2.46875,-13.34375 -4.53,-19.4267 -13.16241,-37.58911 -25.90625,-54.46875 -2.265,-2.8886 -4.5916,-5.72997 -6.96875,-8.5625 -2.49269,-2.8886 -5.01373,-5.64516 -7.5625,-8.25 -0.77482,-0.77822 -1.56043,-1.53191 -2.34375,-2.28125 -3.16897,-3.06192 -12.49571,-11.12846 -15.8125,-13.84375 -0.0175,0.0325 -0.0449,0.0613 -0.0625,0.0937 0.019,0.0117 0.0435,0.0195 0.0625,0.0312 -15.80815,26.64295 -82.81482,28.45557 -77.23469,63.0171 1.07615,6.6653 -9.75353,-8.45181 -15.70281,7.6704 -0.007,0.0297 -0.0274,0.0654 -0.0312,0.0937 -1.8708,8.27839 -7.67138,20.68401 -14.4375,26.59375 -0.11215,0.0561 -0.26588,0.21151 -0.4375,0.4375 -0.16818,0.0561 -0.25,0.13786 -0.25,0.25 -0.16991,0.0561 -0.28765,0.13786 -0.34375,0.25 -0.73745,0.6253 -1.45006,1.2514 -2.1875,1.875 -0.17162,0.0561 -0.36133,0.17214 -0.53125,0.34375 -4.1341,3.05682 -8.31456,5.11192 -12.5625,6.1875 l -4.84375,1.03125 c -0.45368,0 -0.88534,0.0754 -1.28125,0.1875 -1.69917,0.16817 -3.43867,0.25 -5.25,0.25 l -0.53125,0 -0.5,0 -0.5,0 c -0.22769,-0.11551 -0.49918,-0.1875 -0.78125,-0.1875 l -0.40625,0 c -8.51118,-0.56073 -15.87986,-3.3597 -22.09375,-8.40625 -1.6669,-1.36614 -3.24283,-2.90476 -4.75,-4.59375 -0.22769,-0.24468 -0.46151,-0.48662 -0.6875,-0.75 l -0.1875,0 c -0.0289,-0.0305 -0.0319,-0.0666 -0.0625,-0.0937 -4.58608,-5.32523 -7.61414,-11.08331 -9.03125,-17.3125 -0.25658,-1.22681 -0.45442,-2.48581 -0.59375,-3.75 -0.32288,-2.64052 -0.40293,-5.35941 -0.25,-8.15625 l 0,-0.25 c 0.0884,-1.33046 0.2438,-2.63187 0.4375,-3.90625 0.0782,-0.47067 0.1599,-0.94238 0.25,-1.40625 0.0255,-0.15463 0.0615,-0.31412 0.0937,-0.46875 0.29226,-1.45619 0.65027,-2.87367 1.09375,-4.25 0.65759,-2.03052 1.48663,-3.96039 2.46875,-5.8125 l 0.0937,-0.21875 c 0.54714,-1.01271 1.14066,-2.00022 1.78125,-2.96875 2.04071,-3.05003 4.56175,-5.84898 7.5625,-8.40625 0.33814,-0.33983 0.69784,-0.65374 1.09375,-0.9375 5.45945,-4.83246 11.52672,-7.94703 18.1875,-9.34375 3.56657,-0.74593 7.30895,-1.01744 11.21875,-0.78125 8.17475,0.68817 15.1908,3.12846 21.09375,7.28125 0.43159,0.29906 0.86325,0.61466 1.28125,0.9375 0.59132,0.44519 1.18247,0.88564 1.75,1.375 0.31435,0.28037 0.62825,0.58954 0.9375,0.875 3.45613,3.28621 6.01957,6.87144 7.71875,10.78125 0.16991,0.33814 0.32838,0.65374 0.5,0.9375 1.54625,2.85802 2.72518,5.85246 3.5,8.96875 1.95589,6.42618 1.94908,8.21988 1.25777,14.57075 0.69428,-7.86605 29.12532,9.42629 47.82432,-0.87225 29.74605,-16.38273 53.89536,-60.71838 46.58793,-70.41036 -12.44511,-10.39363 -38.41388,-20.78513 -53.70127,-24.31939 -0.39591,-0.11385 -0.75391,-0.1939 -1.09375,-0.25 -2.43492,-0.56582 -4.95596,-1.07757 -7.5625,-1.53125 -0.33814,-0.0561 -0.61133,-0.0625 -0.78125,-0.0625 -9.00734,-1.18942 -14.01005,-1.8189 -15.03125,-1.875 l -0.0937,0 c -4.0313,-0.27208 -8.00252,-0.41724 -11.9375,-0.40625 z"
+ id="path20"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccscccccccccccscccscccccccccccccccccccccccccccccscccccccsc" /></svg> \ No newline at end of file
diff --git a/Graphics/get-material-icons.sh b/Graphics/get-material-icons.sh
index 11e0cda18..cc0bc9137 100755
--- a/Graphics/get-material-icons.sh
+++ b/Graphics/get-material-icons.sh
@@ -59,3 +59,14 @@ python copy OpenKeychain file white folder 24
# multi select
python copy OpenKeychain action white lock 24
+python copy OpenKeychain action white delete 24
+
+# yubikey dialog
+python copy OpenKeychain action black check_circle 48
+
+# preference header
+python copy OpenKeychain file black cloud 24 # cloud
+python copy OpenKeychain action black lock 24 # password settings
+python copy OpenKeychain action black settings_ethernet 24 # proxy
+python copy OpenKeychain notification black sync 24 # sync
+python copy OpenKeychain action black extension 24 # experimental
diff --git a/Graphics/update-drawables.sh b/Graphics/update-drawables.sh
index fc6cb70bf..fd70ff8c2 100755
--- a/Graphics/update-drawables.sh
+++ b/Graphics/update-drawables.sh
@@ -22,7 +22,7 @@ SRC_DIR=./drawables/
#inkscape -w 512 -h 512 -e "$PLAY_DIR/$NAME.png" $NAME.svg
-for NAME in "ic_cloud_search" "ic_action_encrypt_file" "ic_action_encrypt_text" "ic_action_verified_cutout" "ic_action_encrypt_copy" "ic_action_encrypt_save" "ic_action_encrypt_share" "status_lock_closed" "status_lock_error" "status_lock_open" "status_signature_expired_cutout" "status_signature_invalid_cutout" "status_signature_revoked_cutout" "status_signature_unknown_cutout" "status_signature_unverified_cutout" "status_signature_verified_cutout" "key_flag_authenticate" "key_flag_certify" "key_flag_encrypt" "key_flag_sign" "yubi_icon" "status_signature_verified_inner"
+for NAME in "ic_cloud_search" "ic_action_encrypt_file" "ic_action_encrypt_text" "ic_action_verified_cutout" "ic_action_encrypt_copy" "ic_action_encrypt_save" "ic_action_encrypt_share" "status_lock_closed" "status_lock_error" "status_lock_open" "status_signature_expired_cutout" "status_signature_invalid_cutout" "status_signature_revoked_cutout" "status_signature_unknown_cutout" "status_signature_unverified_cutout" "status_signature_verified_cutout" "key_flag_authenticate" "key_flag_certify" "key_flag_encrypt" "key_flag_sign" "yubi_icon" "ic_stat_notify" "status_signature_verified_inner"
do
echo $NAME
inkscape -w 24 -h 24 -e "$MDPI_DIR/${NAME}_24dp.png" "$SRC_DIR/$NAME.svg"
diff --git a/OpenKeychain-Test/build.gradle b/OpenKeychain-Test/build.gradle
deleted file mode 100644
index 2bf35b3d1..000000000
--- a/OpenKeychain-Test/build.gradle
+++ /dev/null
@@ -1,121 +0,0 @@
-buildscript {
- repositories {
- jcenter()
- }
-
- dependencies {
- // NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information
- classpath 'com.novoda:gradle-android-test-plugin:0.10.4'
- }
-}
-
-apply plugin: 'java'
-apply plugin: 'android-test'
-apply plugin: 'jacoco'
-
-dependencies {
- testCompile 'junit:junit:4.11'
- testCompile 'com.google.android:android:4.1.1.4'
- testCompile('com.squareup:fest-android:1.0.8') { exclude module: 'support-v4' }
- testCompile 'org.apache.maven:maven-ant-tasks:2.1.3'
- testCompile ('org.robolectric:robolectric:2.4') {
- exclude module: 'classworlds'
- exclude module: 'maven-artifact'
- exclude module: 'maven-artifact-manager'
- exclude module: 'maven-error-diagnostics'
- exclude module: 'maven-model'
- exclude module: 'maven-plugin-registry'
- exclude module: 'maven-profile'
- exclude module: 'maven-project'
- exclude module: 'maven-settings'
- exclude module: 'nekohtml'
- exclude module: 'plexus-container-default'
- exclude module: 'plexus-interpolation'
- exclude module: 'plexus-utils'
- exclude module: 'support-v4' // crazy but my android studio don't like this dependency and to fix it remove .idea and re import project
- exclude module: 'wagon-file'
- exclude module: 'wagon-http-lightweight'
- exclude module: 'wagon-http-shared'
- exclude module: 'wagon-provider-api'
- }
-}
-
-test {
- exclude '**/*$*'
-}
-
-android {
- projectUnderTest ':OpenKeychain'
-}
-
-jacoco {
- toolVersion = "0.7.2.201409121644"
-}
-
-
-def coverageSourceDirs = [
- '../OpenKeychain/src/main/java',
- '../OpenKeychain/src/gen',
- '../OpenKeychain/build/source/apt/debug',
- '../OpenKeychain/build/source/generated/buildConfig/debug',
- '../OpenKeychain/build/source/generated/r/debug'
- ]
-
-jacocoTestReport {
- reports {
- xml.enabled = true
- html.destination "${buildDir}/jacocoHtml"
- }
- // class R is used, but usage will not be covered, so ignore this class from report
- classDirectories = fileTree(dir: '../OpenKeychain/build/intermediates/classes/debug/org/sufficientlysecure/keychain', exclude: [ 'R*.class' ])
- additionalSourceDirs = files(coverageSourceDirs)
- executionData = files('build/jacoco/testDebug.exec')
-}
-
-// new workaround to force add custom output dirs for android studio
-task addTest {
- def file = file(project.name + ".iml")
- doLast {
- try {
- def parsedXml = (new XmlParser()).parse(file)
- def node = parsedXml.component[1]
- def outputNode = parsedXml.component[1].output[0]
- def outputTestNode = parsedXml.component[1].'output-test'[0]
- def rewrite = false
-
- new Node(node, 'sourceFolder', ['url': 'file://$MODULE_DIR$/' + "${it}", 'isTestSource': "true"])
-
- if(outputNode == null) {
- new Node(node, 'output', ['url': 'file://$MODULE_DIR$/build/resources/testDebug'])
- } else {
- if(outputNode.attributes['url'] != 'file://$MODULE_DIR$/build/resources/testDebug') {
- outputNode.attributes = ['url': 'file://$MODULE_DIR$/build/resources/testDebug']
- rewrite = true
- }
- }
-
- if(outputTestNode == null) {
- new Node(node, 'output-test', ['url': 'file://$MODULE_DIR$/build/test-classes/debug'])
- } else {
- if(outputTestNode.attributes['url'] != 'file://$MODULE_DIR$/build/test-classes/debug') {
- outputTestNode.attributes = ['url': 'file://$MODULE_DIR$/build/test-classes/debug']
- rewrite = true
- }
- }
-
- if(rewrite) {
- def writer = new StringWriter()
- new XmlNodePrinter(new PrintWriter(writer)).print(parsedXml)
- file.text = writer.toString()
- }
- } catch (FileNotFoundException e) {
- // iml not found, common on command line only builds
- }
-
- }
-}
-
-// always do the addtest on prebuild
-gradle.projectsEvaluated {
- testDebugClasses.dependsOn(addTest)
-}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java
deleted file mode 100644
index dabfb008c..000000000
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java
+++ /dev/null
@@ -1,541 +0,0 @@
-/*
- * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.pgp;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.openintents.openpgp.OpenPgpMetadata;
-import org.openintents.openpgp.OpenPgpSignatureResult;
-import org.robolectric.*;
-import org.robolectric.shadows.ShadowLog;
-import org.spongycastle.bcpg.sig.KeyFlags;
-import org.spongycastle.jce.provider.BouncyCastleProvider;
-import org.spongycastle.openpgp.PGPEncryptedData;
-import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
-import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
-import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType;
-import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
-import org.sufficientlysecure.keychain.util.InputData;
-import org.sufficientlysecure.keychain.util.Passphrase;
-import org.sufficientlysecure.keychain.util.ProgressScaler;
-import org.sufficientlysecure.keychain.util.TestingUtils;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.security.Security;
-import java.util.HashSet;
-
-@RunWith(RobolectricTestRunner.class)
-@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
-public class PgpEncryptDecryptTest {
-
- static Passphrase mPassphrase = TestingUtils.genPassphrase(true);
-
- static UncachedKeyRing mStaticRing1, mStaticRing2;
- static Passphrase mKeyPhrase1 = TestingUtils.genPassphrase(true);
- static Passphrase mKeyPhrase2 = TestingUtils.genPassphrase(true);
-
- static PrintStream oldShadowStream;
-
- @BeforeClass
- public static void setUpOnce() throws Exception {
- Security.insertProviderAt(new BouncyCastleProvider(), 1);
- oldShadowStream = ShadowLog.stream;
- // ShadowLog.stream = System.out;
-
- PgpKeyOperation op = new PgpKeyOperation(null);
-
- {
- SaveKeyringParcel parcel = new SaveKeyringParcel();
- parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
- parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.DSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
- parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.ELGAMAL, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
- parcel.mAddUserIds.add("bloom");
- parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhrase1);
-
- PgpEditKeyResult result = op.createSecretKeyRing(parcel);
- Assert.assertTrue("initial test key creation must succeed", result.success());
- Assert.assertNotNull("initial test key creation must succeed", result.getRing());
-
- mStaticRing1 = result.getRing();
- }
-
- {
- SaveKeyringParcel parcel = new SaveKeyringParcel();
- parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
- parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.DSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
- parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.ELGAMAL, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
- parcel.mAddUserIds.add("belle");
- parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhrase2);
-
- PgpEditKeyResult result = op.createSecretKeyRing(parcel);
- Assert.assertTrue("initial test key creation must succeed", result.success());
- Assert.assertNotNull("initial test key creation must succeed", result.getRing());
-
- mStaticRing2 = result.getRing();
- }
-
- }
-
- @Before
- public void setUp() {
- ProviderHelper providerHelper = new ProviderHelper(Robolectric.application);
-
- // don't log verbosely here, we're not here to test imports
- ShadowLog.stream = oldShadowStream;
-
- providerHelper.saveSecretKeyRing(mStaticRing1, new ProgressScaler());
- providerHelper.saveSecretKeyRing(mStaticRing2, new ProgressScaler());
-
- // ok NOW log verbosely!
- ShadowLog.stream = System.out;
- }
-
- @Test
- public void testSymmetricEncryptDecrypt() {
-
- String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
- byte[] ciphertext;
-
- { // encrypt data with a given passphrase
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
-
- PgpSignEncryptOperation op = new PgpSignEncryptOperation(Robolectric.application,
- new ProviderHelper(Robolectric.application), null);
-
- InputData data = new InputData(in, in.available());
-
- PgpSignEncryptInputParcel b = new PgpSignEncryptInputParcel();
- b.setSymmetricPassphrase(mPassphrase);
- b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
-
- PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(), data, out);
-
- Assert.assertTrue("encryption must succeed", result.success());
-
- ciphertext = out.toByteArray();
- }
-
- { // decryption with same passphrase should yield the same result
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
- InputData data = new InputData(in, in.available());
-
- PgpDecryptVerify.Builder b = new PgpDecryptVerify.Builder(Robolectric.application,
- new ProviderHelper(Robolectric.application),
- null, // new DummyPassphraseCache(mPassphrase, 0L),
- data, out);
- DecryptVerifyResult result = b.build().execute(new CryptoInputParcel(mPassphrase));
- Assert.assertTrue("decryption must succeed", result.success());
- Assert.assertArrayEquals("decrypted ciphertext should equal plaintext",
- out.toByteArray(), plaintext.getBytes());
- Assert.assertNull("signature should be an error", result.getSignatureResult());
-
- OpenPgpMetadata metadata = result.getDecryptMetadata();
- Assert.assertEquals("filesize must be correct",
- out.toByteArray().length, metadata.getOriginalSize());
- }
-
- { // decryption with a bad passphrase should fail
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
- InputData data = new InputData(in, in.available());
-
- PgpDecryptVerify.Builder b = new PgpDecryptVerify.Builder(
- Robolectric.application,
- new ProviderHelper(Robolectric.application),
- null, // new DummyPassphraseCache(mPassphrase, 0L),
- data, out);
- DecryptVerifyResult result = b.build().execute(new CryptoInputParcel(
- new Passphrase(new String(mPassphrase.getCharArray()) + "x")));
- Assert.assertFalse("decryption must succeed", result.success());
- Assert.assertEquals("decrypted plaintext should be empty", 0, out.size());
- Assert.assertNull("signature should be an error", result.getSignatureResult());
- }
-
- { // decryption with an unset passphrase should fail
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
- InputData data = new InputData(in, in.available());
-
- PgpDecryptVerify.Builder b = new PgpDecryptVerify.Builder(
- Robolectric.application,
- new ProviderHelper(Robolectric.application),
- null, // new DummyPassphraseCache(mPassphrase, 0L),
- data, out);
- DecryptVerifyResult result = b.build().execute(new CryptoInputParcel());
- Assert.assertFalse("decryption must succeed", result.success());
- Assert.assertEquals("decrypted plaintext should be empty", 0, out.size());
- Assert.assertNull("signature should be an error", result.getSignatureResult());
- }
-
- }
-
- @Test
- public void testAsymmetricEncryptDecrypt() {
-
- String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
- byte[] ciphertext;
-
- { // encrypt data with a given passphrase
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
-
- PgpSignEncryptOperation op = new PgpSignEncryptOperation(Robolectric.application,
- new ProviderHelper(Robolectric.application), null);
-
- InputData data = new InputData(in, in.available());
- PgpSignEncryptInputParcel b = new PgpSignEncryptInputParcel();
-
- b.setEncryptionMasterKeyIds(new long[]{ mStaticRing1.getMasterKeyId() });
- b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
- PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(), data, out);
- Assert.assertTrue("encryption must succeed", result.success());
-
- ciphertext = out.toByteArray();
- }
-
- { // decryption with provided passphrase should yield the same result
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
- InputData data = new InputData(in, in.available());
-
- PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out, null, null, null);
- DecryptVerifyResult result = b.build().execute(new CryptoInputParcel(mKeyPhrase1));
- Assert.assertTrue("decryption with provided passphrase must succeed", result.success());
- Assert.assertArrayEquals("decrypted ciphertext with provided passphrase should equal plaintext",
- out.toByteArray(), plaintext.getBytes());
- Assert.assertNull("signature be empty", result.getSignatureResult());
-
- OpenPgpMetadata metadata = result.getDecryptMetadata();
- Assert.assertEquals("filesize must be correct",
- out.toByteArray().length, metadata.getOriginalSize());
-
- }
-
- // TODO how to test passphrase cache?
-
- { // decryption with passphrase cached should succeed
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
- InputData data = new InputData(in, in.available());
-
- PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out,
- mKeyPhrase1, mStaticRing1.getMasterKeyId(), null);
-
- DecryptVerifyResult result = b.build().execute(new CryptoInputParcel());
- Assert.assertTrue("decryption with cached passphrase must succeed", result.success());
- Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
- out.toByteArray(), plaintext.getBytes());
- Assert.assertNull("signature should be empty", result.getSignatureResult());
- }
-
- { // decryption with no passphrase provided should return status pending
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
- InputData data = new InputData(in, in.available());
-
- PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out,
- null, mStaticRing1.getMasterKeyId(), null);
- DecryptVerifyResult result = b.build().execute(new CryptoInputParcel());
- Assert.assertFalse("decryption with no passphrase must return pending", result.success());
- Assert.assertTrue("decryption with no passphrase should return pending", result.isPending());
- Assert.assertEquals("decryption with no passphrase should return pending passphrase",
- RequiredInputType.PASSPHRASE, result.getRequiredInputParcel().mType);
- }
-
- }
-
- @Test
- public void testMultiAsymmetricEncryptDecrypt() {
-
- String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
- byte[] ciphertext;
-
- { // encrypt data with a given passphrase
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
-
- PgpSignEncryptOperation op = new PgpSignEncryptOperation(Robolectric.application,
- new ProviderHelper(Robolectric.application), null);
-
- InputData data = new InputData(in, in.available());
-
- PgpSignEncryptInputParcel b = new PgpSignEncryptInputParcel();
- b.setEncryptionMasterKeyIds(new long[] {
- mStaticRing1.getMasterKeyId(),
- mStaticRing2.getMasterKeyId()
- });
- b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
-
- PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(), data, out);
- Assert.assertTrue("encryption must succeed", result.success());
-
- ciphertext = out.toByteArray();
- }
-
- { // decryption with passphrase cached should succeed for the first key
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
- InputData data = new InputData(in, in.available());
-
- PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out,
- mKeyPhrase1, mStaticRing1.getMasterKeyId(), null);
-
- DecryptVerifyResult result = b.build().execute(new CryptoInputParcel());
- Assert.assertTrue("decryption with cached passphrase must succeed for the first key", result.success());
- Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
- out.toByteArray(), plaintext.getBytes());
- Assert.assertNull("signature should be empty", result.getSignatureResult());
-
- OpenPgpMetadata metadata = result.getDecryptMetadata();
- Assert.assertEquals("filesize must be correct",
- out.toByteArray().length, metadata.getOriginalSize());
- }
-
- { // decryption with passphrase cached should succeed for the first key
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
- InputData data = new InputData(in, in.available());
-
- // allow only the second to decrypt
- HashSet<Long> allowed = new HashSet<>();
- allowed.add(mStaticRing2.getMasterKeyId());
-
- // provide passphrase for the second, and check that the first is never asked for!
- PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out,
- mKeyPhrase2, mStaticRing2.getMasterKeyId(), null);
- b.setAllowedKeyIds(allowed);
-
- DecryptVerifyResult result = b.build().execute(new CryptoInputParcel());
- Assert.assertTrue("decryption with cached passphrase must succeed for the first key", result.success());
- Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
- out.toByteArray(), plaintext.getBytes());
- Assert.assertNull("signature should be empty", result.getSignatureResult());
- }
-
- { // decryption with passphrase cached should succeed for the other key if first is gone
-
- // delete first key from database
- new ProviderHelper(Robolectric.application).getContentResolver().delete(
- KeyRingData.buildPublicKeyRingUri(mStaticRing1.getMasterKeyId()), null, null
- );
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
- InputData data = new InputData(in, in.available());
-
- PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out,
- mKeyPhrase2, mStaticRing2.getMasterKeyId(), null);
-
- DecryptVerifyResult result = b.build().execute(new CryptoInputParcel());
- Assert.assertTrue("decryption with cached passphrase must succeed", result.success());
- Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
- out.toByteArray(), plaintext.getBytes());
- Assert.assertNull("signature should be empty", result.getSignatureResult());
- }
-
- }
-
- @Test
- public void testMultiAsymmetricSignEncryptDecryptVerify() {
-
- String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
- byte[] ciphertext;
-
- { // encrypt data with a given passphrase
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
-
- PgpSignEncryptOperation op = new PgpSignEncryptOperation(Robolectric.application,
- new ProviderHelper(Robolectric.application), null);
-
- InputData data = new InputData(in, in.available());
- PgpSignEncryptInputParcel b = new PgpSignEncryptInputParcel();
-
- b.setEncryptionMasterKeyIds(new long[] {
- mStaticRing1.getMasterKeyId(),
- mStaticRing2.getMasterKeyId()
- });
- b.setSignatureMasterKeyId(mStaticRing1.getMasterKeyId());
- b.setSignatureSubKeyId(KeyringTestingHelper.getSubkeyId(mStaticRing1, 1));
- b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
-
- PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(mKeyPhrase1), data, out);
- Assert.assertTrue("encryption must succeed", result.success());
-
- ciphertext = out.toByteArray();
- }
-
- { // decryption with passphrase cached should succeed for the first key
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
- InputData data = new InputData(in, in.available());
-
- PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out,
- mKeyPhrase1, mStaticRing1.getMasterKeyId(), null);
-
- DecryptVerifyResult result = b.build().execute(new CryptoInputParcel());
- Assert.assertTrue("decryption with cached passphrase must succeed for the first key", result.success());
- Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
- out.toByteArray(), plaintext.getBytes());
- Assert.assertEquals("signature should be verified and certified",
- OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED, result.getSignatureResult().getStatus());
-
- OpenPgpMetadata metadata = result.getDecryptMetadata();
- Assert.assertEquals("filesize must be correct",
- out.toByteArray().length, metadata.getOriginalSize());
- }
-
- { // decryption with passphrase cached should succeed for the other key if first is gone
-
- // delete first key from database
- new ProviderHelper(Robolectric.application).getContentResolver().delete(
- KeyRingData.buildPublicKeyRingUri(mStaticRing1.getMasterKeyId()), null, null
- );
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
- InputData data = new InputData(in, in.available());
-
- PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out,
- mKeyPhrase2, mStaticRing2.getMasterKeyId(), null);
-
- DecryptVerifyResult result = b.build().execute(new CryptoInputParcel());
- Assert.assertTrue("decryption with cached passphrase must succeed", result.success());
- Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
- out.toByteArray(), plaintext.getBytes());
- Assert.assertEquals("signature key should be missing",
- OpenPgpSignatureResult.SIGNATURE_KEY_MISSING,
- result.getSignatureResult().getStatus());
- }
-
- }
-
- @Test
- public void testForeignEncoding () throws Exception {
- String plaintext = "ウィキペディア";
- byte[] plaindata = plaintext.getBytes("iso-2022-jp");
-
- { // some quick sanity checks
- Assert.assertEquals(plaintext, new String(plaindata, "iso-2022-jp"));
- Assert.assertNotEquals(plaintext, new String(plaindata, "utf-8"));
- }
-
- byte[] ciphertext;
- { // encrypt data with a given passphrase
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(plaindata);
-
- PgpSignEncryptOperation op = new PgpSignEncryptOperation(Robolectric.application,
- new ProviderHelper(Robolectric.application), null);
-
- InputData data = new InputData(in, in.available());
- PgpSignEncryptInputParcel b = new PgpSignEncryptInputParcel();
-
- b.setEncryptionMasterKeyIds(new long[]{ mStaticRing1.getMasterKeyId() });
- b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
- // this only works with ascii armored output!
- b.setEnableAsciiArmorOutput(true);
- b.setCharset("iso-2022-jp");
- PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(), data, out);
- Assert.assertTrue("encryption must succeed", result.success());
-
- ciphertext = out.toByteArray();
- }
-
- { // decryption with provided passphrase should yield the same result
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
- InputData data = new InputData(in, in.available());
-
- PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out, null, null, null);
- DecryptVerifyResult result = b.build().execute(new CryptoInputParcel(mKeyPhrase1));
- Assert.assertTrue("decryption with provided passphrase must succeed", result.success());
- Assert.assertArrayEquals("decrypted ciphertext should equal plaintext bytes",
- out.toByteArray(), plaindata);
- Assert.assertEquals("charset should be read correctly",
- "iso-2022-jp", result.getCharset());
- Assert.assertEquals("decrypted ciphertext should equal plaintext",
- new String(out.toByteArray(), result.getCharset()), plaintext);
- Assert.assertNull("signature be empty", result.getSignatureResult());
- }
-
- }
-
- private PgpDecryptVerify.Builder builderWithFakePassphraseCache (
- InputData data, OutputStream out,
- final Passphrase passphrase, final Long checkMasterKeyId, final Long checkSubKeyId) {
-
- return new PgpDecryptVerify.Builder(Robolectric.application,
- new ProviderHelper(Robolectric.application),
- null,
- data, out) {
- public PgpDecryptVerify build() {
- return new PgpDecryptVerify(this) {
- @Override
- public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId)
- throws NoSecretKeyException {
- if (checkMasterKeyId != null) {
- Assert.assertEquals("requested passphrase should be for expected master key id",
- (long) checkMasterKeyId, masterKeyId);
- }
- if (checkSubKeyId != null) {
- Assert.assertEquals("requested passphrase should be for expected sub key id",
- (long) checkSubKeyId, subKeyId);
- }
- if (passphrase == null) {
- return null;
- }
- return passphrase;
- }
- };
- }
- };
- }
-
-}
diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle
index 48fc38ca7..7c68ae062 100644
--- a/OpenKeychain/build.gradle
+++ b/OpenKeychain/build.gradle
@@ -1,26 +1,41 @@
apply plugin: 'com.android.application'
apply plugin: 'witness'
+apply plugin: 'jacoco'
+apply plugin: 'com.github.kt3k.coveralls'
dependencies {
// NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information
// NOTE: libraries are pinned to a specific build, see below
// from local Android SDK
- compile 'com.android.support:support-v4:22.1.1'
- compile 'com.android.support:appcompat-v7:22.1.1'
- compile 'com.android.support:recyclerview-v7:22.1.0'
- compile 'com.android.support:cardview-v7:22.1.0'
+ compile 'com.android.support:support-v4:22.2.1'
+ compile 'com.android.support:appcompat-v7:22.2.1'
+ compile 'com.android.support:design:22.2.1'
+ compile 'com.android.support:recyclerview-v7:22.2.1'
+ compile 'com.android.support:cardview-v7:22.2.1'
- // UI testing libs
- androidTestCompile 'com.android.support.test:runner:0.2'
- androidTestCompile 'com.android.support.test:rules:0.2'
- androidTestCompile 'com.android.support.test.espresso:espresso-core:2.1'
- androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.1'
+ // Unit tests in the local JVM with Robolectric
+ // https://developer.android.com/training/testing/unit-testing/local-unit-tests.html
+ // http://robolectric.org/getting-started/
+ // http://www.vogella.com/tutorials/Robolectric/article.html
+ testCompile 'junit:junit:4.12'
+ testCompile 'org.robolectric:robolectric:3.0'
+
+ // UI testing with Espresso
+ androidTestCompile 'com.android.support.test:runner:0.3'
+ androidTestCompile 'com.android.support.test:rules:0.3'
+ androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2'
+ androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2'
+ androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2') {
+ exclude group: 'com.android.support', module: 'appcompat'
+ exclude group: 'com.android.support', module: 'support-v4'
+ exclude module: 'recyclerview-v7'
+ }
// Temporary workaround for bug: https://code.google.com/p/android-test-kit/issues/detail?id=136
// from https://github.com/googlesamples/android-testing/blob/master/build.gradle#L21
configurations.all {
- resolutionStrategy.force 'com.android.support:support-annotations:22.1.1'
+ resolutionStrategy.force 'com.android.support:support-annotations:22.2.0'
}
// JCenter etc.
@@ -34,14 +49,15 @@ dependencies {
compile 'org.ocpsoft.prettytime:prettytime:3.2.7.Final'
compile "com.splitwise:tokenautocomplete:1.3.3@aar"
compile 'se.emilsjolander:stickylistheaders:2.6.0'
- compile 'org.sufficientlysecure:html-textview:1.1'
- compile 'com.mikepenz.materialdrawer:library:2.8.2@aar'
- compile 'com.mikepenz.iconics:library:0.9.1@aar'
+ compile 'org.sufficientlysecure:html-textview:1.2'
+ compile 'com.mikepenz:materialdrawer:3.0.9@aar'
+ compile 'com.mikepenz:iconics:1.0.2'
compile 'com.mikepenz.iconics:octicons-typeface:2.2.0@aar'
compile 'com.mikepenz.iconics:meteocons-typeface:1.1.1@aar'
compile 'com.mikepenz.iconics:community-material-typeface:1.0.0@aar'
+ compile 'com.nispok:snackbar:2.11.0'
+ compile 'com.squareup.okhttp:okhttp:2.4.0'
compile 'org.thoughtcrime.ssl.pinning:AndroidPinning:1.0.0'
- compile 'com.nispok:snackbar:2.10.8'
// libs as submodules
compile project(':extern:openpgp-api-lib:openpgp-api')
@@ -52,17 +68,18 @@ dependencies {
compile project(':extern:spongycastle:prov')
compile project(':extern:minidns')
compile project(':extern:KeybaseLib:Lib')
- compile project(':extern:safeslinger-exchange')
+ compile project(':extern:safeslinger-exchange:safeslinger-exchange')
}
// Output of ./gradlew -q calculateChecksums
// Comment out the libs referenced as git submodules!
dependencyVerification {
verify = [
- 'com.android.support:support-v4:1e2e4d35ac7fd30db5ce3bc177b92e4d5af86acef2ef93e9221599d733346f56',
- 'com.android.support:appcompat-v7:9a2355537c2f01cf0b95523605c18606b8d824017e6e94a05c77b0cfc8f21c96',
- 'com.android.support:recyclerview-v7:522d323079a29bcd76173bd9bc7535223b4af3e5eefef9d9287df1f9e54d0c10',
- 'com.android.support:cardview-v7:8dc99af71fec000baa4470c3907755264f15f816920861bc015b2babdbb49807',
+ 'com.android.support:support-v4:c62f0d025dafa86f423f48df9185b0d89496adbc5f6a9be5a7c394d84cf91423',
+ 'com.android.support:appcompat-v7:4b5ccba8c4557ef04f99aa0a80f8aa7d50f05f926a709010a54afd5c878d3618',
+ 'com.android.support:design:58be3ca6a73789615f7ece0937d2f683b98b594bb90aa10565fa760fb10b07ee',
+ 'com.android.support:recyclerview-v7:b0f530a5b14334d56ce0de85527ffe93ac419bc928e2884287ce1dddfedfb505',
+ 'com.android.support:cardview-v7:2c2354761a4e20ba451ae903ab808f15c9acc8343b1e74001869c2d0a672c1fc',
'com.eftimoff:android-patternview:cec80e7265b8d8278b3c55b5fcdf551e4600ac2c8bf60d8dd76adca538af0b1e',
'com.journeyapps:zxing-android-embedded:702a4f58154dbd9baa80f66b6a15410f7a4d403f3e73b66537a8bfb156b4b718',
'com.journeyapps:zxing-android-integration:562737821b6d34c899b6fd2234ce0a8a31e02ff1fd7c59f6211961ce9767c7c8',
@@ -72,27 +89,29 @@ dependencyVerification {
'org.commonjava.googlecode.markdown4j:markdown4j:e952e825d29e1317d96f79f346bfb6786c7c5eef50bd26e54a80823704b62e13',
'com.splitwise:tokenautocomplete:20bee71cc59b3828eb000b684d46ddf738efd56b8fee453a509cd16fda42c8cb',
'se.emilsjolander:stickylistheaders:8c05981ec5725be33f7cee5e68c13f3db49cd5c75f1aaeb04024920b1ef96ad4',
- 'org.sufficientlysecure:html-textview:ca24b1522be88378634093815ce9ff1b4920c72e7513a045a7846e14069ef988',
- 'com.mikepenz.materialdrawer:library:970317ed1a3cb96317f7b8d62ff592b3103eb46dfd68d9b244e7143623dc6d7a',
- 'com.mikepenz.iconics:library:4698a36ee4c2af765d0a85779c61474d755b90d66a59020105b6760a8a909e9e',
- 'com.mikepenz.iconics:octicons-typeface:67ed7d456a9ce5f5307b85f955797bfb3dd674e2f6defb31c6b8bbe2ede290be',
+ 'org.sufficientlysecure:html-textview:1d3bed31ef837437154de8d2362a0e6b0e59b6c3535d87ee48c2fab12c84f9bb',
+ 'com.mikepenz:iconics:c1a02203d8e0d638959463c00af3ab9096e0a7c1ad5928762eb10ef5ce8a63cd',
+ 'com.mikepenz:materialdrawer:70c3efb3842461db41df6a918ea93969a7da21e63c092be838b153e5a47a17bf',
'com.mikepenz.iconics:meteocons-typeface:39a8a9e70cd8287cdb119af57a672a41dd09240dba6697f5a0dbda1ccc33298b',
+ 'com.mikepenz.iconics:octicons-typeface:67ed7d456a9ce5f5307b85f955797bfb3dd674e2f6defb31c6b8bbe2ede290be',
+ 'com.nispok:snackbar:46b5eb9d630d329e13c2ce00ee9fb115ffb66c23c72cff32ee97eedd76824c6f',
'com.mikepenz.iconics:community-material-typeface:f1c5afee5f0f10d66beb3ed0df977246a02a9c46de4e05d7c0264bcde53b6b7f',
- 'com.nispok:snackbar:80bebc8e5d8b3d728cd5f2336e2d0c1cc2a6b7dc4b55d36acd6b75a78265590a',
-// 'OpenKeychain.extern:openpgp-api-lib:f05a9215cdad3a6597e4c5ece6fcec92b178d218195a3e88d2c0937c48dd9580',
-// 'OpenKeychain.extern:openkeychain-api-lib:50f6ebb5452d3fdc7be137ccf857a0ff44d55539fcb7b91baef495766ed7f429',
-// 'com.madgag.spongycastle:core:df8fcc028a95ac5ffab3b78c9163f5cfa672e41cd50128ca55d458b6cfbacf4b',
-// 'com.madgag.spongycastle:pg:160b345b10a2c92dc731453eec87037377f66a8e14a0648d404d7b193c4e380d',
-// 'com.madgag.spongycastle:pkix:0b4f3301ea12dd9f25d71770e6ea9f75e0611bf53062543e47be5bc15340a7e4',
-// 'com.madgag.spongycastle:prov:7325942e0b39f5fb35d6380818eed4b826e7dfc7570ad35b696d778049d8c36a',
-// 'OpenKeychain.extern:minidns:77b1786d29469e3b21f9404827cab811edc857cd68bc732cd57f11307c332eae',
-// 'OpenKeychain.extern.KeybaseLib:Lib:c91cda4a75692d8664644cd17d8ac962ce5bc0e266ea26673a639805f1eccbdf',
-// 'OpenKeychain.extern:safeslinger-exchange:d222721bb35408daaab9f46449364b2657112705ee571d7532f81cbeb9c4a73f',
-// 'OpenKeychain.extern.snackbar:lib:52357426e5275412e2063bdf6f0e6b957a3ea74da45e0aef35d22d9afc542e23',
- 'com.android.support:support-annotations:7bc07519aa613b186001160403bcfd68260fa82c61cc7e83adeedc9b862b94ae',
+ 'com.squareup.okhttp:okhttp:bc0da7ac1f5441619faa2082811938acf7df97e4a8e08f0e043ff4937414d5ad',
+// 'OpenKeychain.extern.openkeychain-api-lib:openkeychain-intents:111d7d53b9e920ad3405f8f3eb0ab7bd3aee66d577442452754b83c7c1c1d49a',
+// 'OpenKeychain.extern.openpgp-api-lib:openpgp-api:544b7b2e20955556b83d1b72763543aa789836ebc1e77b332ed7cd83ef765c4a',
+// 'com.madgag.spongycastle:core:97276487be598747ba78c063c90cea7fc3c7ad9bc7aeba03c0b9c98692052b8a',
+// 'com.madgag.spongycastle:pkix:979aa4b2aaef94866e0f97b05b1922244eaf8b650f3691a3c44760ff0a41562b',
+// 'com.madgag.spongycastle:pg:da319de706d946f178140959c74aec126f7803f1104dbad89bb1f55a53f6e1a9',
+// 'OpenKeychain.extern:minidns:8274d50124d9584e95df0c5da7798269ac9caf0eab560df929c2c658ca624037',
+// 'com.madgag.spongycastle:prov:902a484219bbf4e395a1c32da65b2453133e195bcc92336dc8c33b7c58edcd60',
+ 'com.android.support:support-annotations:beac5cae60bdb597df9af9c916f785c2f71f8c8ae4be9a32d4298dea85496a42',
+// 'OpenKeychain.extern.KeybaseLib:Lib:d52e7888cea6de9e077501bb533270b2a86b52cb8af49e5f44ee8c4bb19ea017',
+// 'OpenKeychain.extern.safeslinger-exchange:safeslinger-exchange:76e5da6b4f5f8835b12649e17569f0d0d8d89552815a61383c128545632689d1',
+ 'com.squareup.okio:okio:b53c1760864e1c39b5275d9023e2a6fbe8f3189e6e67b4c87877b8ec8f92e05a',
]
}
+
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
@@ -100,19 +119,50 @@ android {
defaultConfig {
minSdkVersion 15
targetSdkVersion 22
-
+ versionCode 35100
+ versionName "3.5.1"
+ applicationId "org.sufficientlysecure.keychain"
+ // the androidjunitrunner is broken regarding coverage, see here:
+ // https://code.google.com/p/android/issues/detail?id=170607
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ // this workaround runner fixes the coverage problem, BUT doesn't work
+ // with android studio single test execution. use it to generate coverage
+ // data, but keep the other one otherwis
+ // testInstrumentationRunner "org.sufficientlysecure.keychain.JacocoWorkaroundJUnitRunner"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
-
+
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+
+ // Reference them in the java files with e.g. BuildConfig.ACCOUNT_TYPE.
+ buildConfigField "String", "ACCOUNT_TYPE", "\"org.sufficientlysecure.keychain.account\""
+ buildConfigField "String", "PROVIDER_CONTENT_AUTHORITY", "\"org.sufficientlysecure.keychain.provider\""
+
+ // Reference them in .xml files.
+ resValue "string", "account_type", "org.sufficientlysecure.keychain.account"
+ resValue "string", "provider_content_authority", "org.sufficientlysecure.keychain.provider"
+ }
+
+ debug {
+ applicationIdSuffix ".debug"
+
+ // Reference them in the java files with e.g. BuildConfig.ACCOUNT_TYPE.
+ buildConfigField "String", "ACCOUNT_TYPE", "\"org.sufficientlysecure.keychain.debug.account\""
+ buildConfigField "String", "PROVIDER_CONTENT_AUTHORITY", "\"org.sufficientlysecure.keychain.debug.provider\""
+
+ // Reference them in .xml files.
+ resValue "string", "account_type", "org.sufficientlysecure.keychain.debug.account"
+ resValue "string", "provider_content_authority", "org.sufficientlysecure.keychain.debug.provider"
+
+ // Enable code coverage (Jacoco)
+ testCoverageEnabled true
}
}
@@ -158,13 +208,53 @@ android {
// Disable preDexing, causes com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000) on some systems
dexOptions {
preDexLibraries = false
+ javaMaxHeapSize "2g"
}
packagingOptions {
exclude 'LICENSE.txt'
+ exclude 'META-INF/LICENSE.txt'
+ exclude 'META-INF/NOTICE.txt'
+ exclude '.readme'
}
}
+// apply plugin: 'spoon'
+
+task jacocoTestReport(type:JacocoReport) {
+ group = "Reporting"
+ description = "Generate Jacoco coverage reports"
+
+ classDirectories = fileTree(
+ dir: "${buildDir}/intermediates/classes/debug",
+ excludes: ['**/R.class',
+ '**/R$*.class',
+ '**/*$ViewInjector*.*',
+ '**/BuildConfig.*',
+ '**/Manifest*.*']
+ )
+
+ sourceDirectories = files("${buildDir.parent}/src/main/java")
+ additionalSourceDirs = files([
+ "${buildDir}/generated/source/buildConfig/debug",
+ "${buildDir}/generated/source/r/debug"
+ ])
+ executionData = files([
+ "${buildDir}/jacoco/testDebug.exec",
+ "${buildDir}/outputs/code-coverage/connected/coverage.ec"
+ ])
+
+ reports {
+ xml.enabled = true
+ html.enabled = true
+ }
+}
+
+// Fix for: No report file available: [/home/travis/build/open-keychain/open-keychain/OpenKeychain/build/reports/cobertura/coverage.xml, /home/travis/build/open-keychain/open-keychain/OpenKeychain/build/reports/jacoco/test/jacocoTestReport.xml]
+coveralls {
+ jacocoReportPath 'build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml'
+}
+
// NOTE: This disables Lint!
tasks.whenTaskAdded { task ->
if (task.name.contains('lint')) {
diff --git a/OpenKeychain/src/androidTest/assets/ci.png b/OpenKeychain/src/androidTest/assets/ci.png
new file mode 100644
index 000000000..3a6117082
--- /dev/null
+++ b/OpenKeychain/src/androidTest/assets/ci.png
Binary files differ
diff --git a/OpenKeychain/src/androidTest/assets/pa.png b/OpenKeychain/src/androidTest/assets/pa.png
new file mode 100644
index 000000000..3c6aa7fda
--- /dev/null
+++ b/OpenKeychain/src/androidTest/assets/pa.png
Binary files differ
diff --git a/OpenKeychain/src/androidTest/assets/re.png b/OpenKeychain/src/androidTest/assets/re.png
new file mode 100644
index 000000000..a441bbc87
--- /dev/null
+++ b/OpenKeychain/src/androidTest/assets/re.png
Binary files differ
diff --git a/OpenKeychain/src/androidTest/assets/valodim.pub.asc b/OpenKeychain/src/androidTest/assets/valodim.pub.asc
new file mode 100644
index 000000000..e9fe0518a
--- /dev/null
+++ b/OpenKeychain/src/androidTest/assets/valodim.pub.asc
@@ -0,0 +1,1032 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1
+
+mQINBFAB3UABEADCyB/vbIBA3m1BwcyjTieEMLySwYgt54EQ2hglOocdtIhqC+b0
+5t6sLSkwx2ukxrU2cegnCBkdyF/FZ/+Et638CUEBbf4bjplwpt2IPLazQgjkwjMu
+hz0OcYDpMhwimTvh3mIl+0wzpOts6mEmMw0QZdl3RXvIW+NSynOn7qmz/fAv4Htt
+6lv2Ka0s6R2voyi+5U7CcIqizPad5qZVn2uxmovcFreTzFt6nk37ZbbTfvA3e5F0
+bRRQeH3viT5XxpJF4Y76v/Ua+5N3Kd18K0sX85rD1G7cmxR2CZ5gW1X24sDqdYZd
+Dbf10N39UIwjJHPTeuVMQqry792Ap0Etyj135YFCE0loDnZYKvy2Y1i0RuEdTUIo
+nIHrLhe2J0bXQGbQImHIyMgB9/lva8D+yvy2gyf2vjRhmJEEco7w9FdzP7p3PhKr
+UiTjRsjHw8iV8LOCFx9njZOq9mism9ZZ16tZpx9mXOf11HcH1RtVuyyQRS/4ytQP
+zwshXdSDDW6Btkmo9AbZQKC54/hSyzpp3Br2T2xDH7ecnonDB/jv8rWuKXSTbX3x
+WAIrNBNDcTYaNe4jkms4HF7jJE19eRlqsXMMx6Fxvrh4TtKICwJYJ3AUmXrK3XTi
+/mjqYfJ1fpBn54rWs8nhSR1fuZPD+aMlcP8BDUPlNKPKtj0DGSh3/VlnnwARAQAB
+tDBWaW5jZW50IEJyZWl0bW9zZXIgPHYuYnJlaXRtb3NlckBtdWdlbmd1aWxkLmNv
+bT6JAjsEEwECACUCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAhkBBQJTRDeb
+AAoJEHvRgyDerfoRxxcP/01iELqZkwGGmlACy1S+j8xO/+E+5ZCEZBZmqfewf8e0
+1LwHWA9fPsSNZS8xOaKVJQhghkk4hWbweK8BDfzzT3y7KzE5LiSUmpdE4Ory51uY
+ttGUKmuctXuzgNZjF+hHxAvBor12DnyV265gJytjE+Qi4A/WNBVbgOBkeocVrXQy
+2lM8zoPZnw1PCNfsVG24TxWxeD+IKeSxufsmT1VDMUpZZb0HKN30I3Nh++5cO0jH
+pss1mDyQQzWwIKH+m1kboJ6NqnNxhRJfO/92lVeXQT4TiWw+9yA1DmjQfKbfHoV9
+Wp/Sa7R2A21AD2uaaEPoNZFHfQ9dOjYk9zLX2CCbrouzwbiq+UNpzQiubi2CQpCu
+95C8KE38QHwYze1kLc0E+BEYi1m3KDy/PNYPVQJ8QEfw/dMaxmxyqLolBvVIow9+
+S7Kdm1E4FKQgG+Rr4QSrLJ88BQS8w2GIi9OqcnIY5pTD+I9/7/4GH5t+2zZXDORf
+jlEJ4fPkNboh078UclfJac9O9MbP4nXKqACSjpvQ6NJyZRsyMkObd6GITH2DlDqW
+5Rl9fhGBb7dkI/PhnzKgzYZONPyEIckscptV2yrcKKtPD9DKggBJZ5pThHRk+qgW
+JPOkmQrhMKCperQh1ZkipkLw+rNXV+sqNKAzCrOrnYijiJBfWfLKxXCazQT+Sj4W
+iEYEEBECAAYFAlNEOMkACgkQxCa+aHUWYdSgxgCg2mkIHFTrI5tMxevXbs+NYmS5
+5f4AmwemK8Rbq/AngSl2+x8LJBOTDUASiEYEEBECAAYFAlOgXm8ACgkQ/TXUs5uJ
+xp/HqACdGfuSPVu5j+ONsk7BjeEJC1shDsUAn3IrebdaEhXbH5kRejsQvqhCtGCD
+iEYEExEIAAYFAlNFnI8ACgkQioOL5NhIDy5ehgCgyAVo3puGieKvHaZoAwx6uyU9
+eiAAn1oKOF75K7+fkD/OrcHpKJzNl5ppiQEcBBABAgAGBQJTR+60AAoJENjTIPvv
+LEnwsjkIAKJZBG7RWdlNFQmhDxr2CWBZz6hoqqYIlgpo4wsVIthEQEtJdsVfle1y
+0+FC1CpN2Ly0KFql4YBaZrq3sfDNwbetyBPrAIojzFTrA9++8dfWYcrT83zH9zDT
+EP8e5SOYqam1dA+tic6ZqruEtY/jBTb/yyz61Syh0Dv1xLaxOcftG5M0XvnMwENk
+8NAql+NLhiWbB23Yq30bahYb5tVsf+neX5b7Ik0Td2RUQEXSG92Q/ybsuEu4olM9
+ifDs5vu4NvOe/1oMzegVaZ8K+2CFQBjnUTvHBr11Yvg4Nlh/mnfIEgp3OvhSPgRd
+d37SsNUAGx6p0frGkcqC0jYNY72i1FuJARwEEAEIAAYFAlQRYoQACgkQcYwHAQAB
+IoITJQf7BMXIL5kh16a3HdTVsDpiuEocXEbEXj+brV2BBKmtMSko/w+Sh6VJlWLu
+T2PzrdW0kQtddKCYCYaxrrppM0ANA8c1MZ1sVyjSjiKKuTVogOmVyKLyVq2A1BRC
+GovJBpXSfzur7T6/TbNgeyS22C9xGynQ9QC0Lx5ncYjaUQds4yeKY1+AkmuUHELz
+5jU7QDufAVbFhYd0nS8ENG693wz40LsqN6ygonhzKM1AhOu49SHR3frWJIS17uyS
+owSGjBtsbY7xKzE8UviNpQcbp4x0tAjEikExukQuWQelaqUMyxnvcZkliasfsQKQ
+rA/YUNq01+6VHCsruFt53E5BPuO3MYkBHAQSAQIABgUCU0VwGgAKCRCylLwGx1Uq
+eizJB/9IfRlqi90wNYXQ4wMIQ/TUmbcUHe1eV21zav4KzSCxDHvFfKGJzlZSnzef
+U1BndFDSdfSOXKEAm9luooSlwmKdseil8vphEq8/62Tcct6ftTU1hd7JWz+Q+Y/z
+hRRRYIGHvopIuMQPxvA2wgCDmfIqENnh3b0lLwzQpux/K8fHuCZ6nDkIZ3UY6gNv
+caPOOxyEJ+wMAoqc6zgYiH2DwLciZMKHepwI3dr8eAHddcMOQ4d+RDlbMwm+Ngjv
+Pm9ohRsVK0kyffrOxIoq1wjFHUBnpGtGGoU+11WWKZj1/SxvALqrGoIhOn0Mv5KI
+q5e7C+9Pq8TT73lTRkilcOjBqOFtiQIcBBABAgAGBQJTRZX4AAoJELJBPGbdLD/q
+cfoQAJtGg0j+gbLyCDjWoK7hBg0+VMGZykAJC3+e7tq3HmWiHho9cBE+i5GJCnn8
+7P9WW4J3Hvm2j4UB7BHgnEgb40K3rAXP2z+MRQ53jQ8RpU5iIoAAVGuXarA4Db+B
+o8ccVyu9YHj748Z79mL0fC0/BuL8MSs2uf5RdB///XvJxEEhDgifPXnwRjm6Vf8C
+3C7HT1e/ywswXcsbPe4yL5964rEcv3xijSwMMXlcmFDF/1DMdvs3xjiexkDqhUtH
+eoq6+tWgFUy2axek0rdK2XAGPqfk951L5tGJ6zdRduu65uciXiYZgrl/r90U6Cyo
+Wu2FwID87RNIIt8P5twCdAMLNsuHBauhTKA4BZ+K/Q1NhfkPlOb7WX7Guog161ov
+/OKKeI+CqReuRPCP6ttiuUCs3b6ZFAO5kSSXMItWWEUJ0ePiusztywq3/GKdaIXE
+LCm35fsGvF69r/3XkKm0hlf4/5RAvz5kd2WyVnA8ZzRZmfdCryR5wSM93UItDRAE
+rmLJutYFAPhxOA426yiuxEvrPbkWlMjEc7C0kZ1wbdXj5IL8Hjw8Ui4zVNNe/C2J
+FaXH8Q4nhEIQC7y1z6cHbEE3hQqg1E5bR0T3AAyciQZHjHFOBDHWnOLccoZ5gxej
+0yTnU4HIjFyarUEU3O2eMhZt+hTTgqPqY4kOyHZjIUQ3K+kqiQIcBBABAgAGBQJT
+RZ1FAAoJEGK53P9mJMGiBmAQANM0Y6LI0eVR5HAsDgHqessvzXrFRD9AsGp+7Mb6
+0uO1cGBb87dg2BDVmQlUaW32/L94UP0LHX56AxCdcKgO9qU1KRDw6mNg2zWLtEkn
+377DYQs+jlZPWpafsM1ZmY9MjX+gNLTFKNS4TmqSWLvwRfp8RfwsTkr4JzCAt36X
+/XfgJQcI+m8qJX914lKkdc2NCV/QwQoyssKg3JEOWQhwJsG/59GK1QQAw2YC5nLR
+/4bYoEyULGLDGXxLgRzkxicpindz3FC07uET7ftknBu4cF9dwDB8lLfOBKpR1S72
+PRuQs+koLfouuV3a792qWokwu+WTx0UPkuI9SauzjBgHTa1QqjIpNxXGg+oGyvEF
+1gsyOlU9K2+rP7iYYglxpMpdJY4nWb1j5pbC4dEdpEZclpzNC8iyp34hqnqyHZJt
++ccWRIBayaBLAv5E8YBW2mn2u24S1trtoFEUyb32qCdi/+i6GZXMHEvTADQs8ce1
+qD6Cq9PjGXTVg8aU65BXytldP0rFuNK/W52oSpmK/GXW0DHhNxN/l30S5ocvVFNb
+ufMY84SPhjnQMPMKzHMfOhJUyynD6FV9Quhoqq9wEPSMERG4n553dxEMnDkKRAR1
+rKZ4K8sQrbvqHez1W99XgezqLrcPn1qwz9O677TST4D5Gw4AGgrtfI2gB/PirCtk
+M58XiQIcBBABAgAGBQJToCvpAAoJEC13xQLG7M66dNwP/3dXBdu+LRB8fnA9mS48
+qxOO/+LrdO9nKPRGo6JJRGdhOeySqPsChDoWojpl0Ldz69HHs+3AqhcXIq2LeSNN
+IRnVFCIrzHv1e2zX6jIgtpnFZmomynoaW9ssyHXTsH/3h2mO3fJcJZUyMXx9N0Et
+hraNGrTHvCcMHIDGmnunZlQ54k1uzUPZKT1P9fjnqv28E/nAVKNM6md5qYdIRjsy
+6B3+7/WSAXa/5f6DgTq1GvumfqS7i2nVwsjzbuhRj1+X0WHGa02Fxq2Fp9xRII12
+YhvmBowWnnndkeFQEM8iHLWCVjP8nElBWT+oyTdFCOLtA90JWPv+TPjURSe+OvaT
+Qco8d3w0FZpERfJnzFMcUBm6fS48jkMkH7GZS+044rQlw0e6EnMzKYnbmrflerb6
+FVOl9qhaZWTxq5yB15jgfLMNLUETLwqkPTcJyiDugtZnYC9Nq5webZQVWLIRBnxx
+wAt6G8nVxlmWXP6da5yunqqPBvLpYcNDHt8Q5LaBNEyYDSk7E7aEpPTj6Q+9kwc1
+RTAf3vrpLrAxmEgCBUgQ/2DAKSqde+Q4ZH7SxIFLbyeRhqJLZr8TF4YTUSHt0fds
+jqCGSgDJCoMWWVuisARpI8uGRnBkk9xggZCwwLLObDv0EUzXmYXEIxUZJZK9IcuE
+LBRsPisP6uJ91w8bFAvnlqdGiQIcBBABAgAGBQJToGCMAAoJEAvp4xV6HixkLQQQ
+ALdrnBhbontdlJcxNBxui3put/WZp6SYjfW4fw2nNoYsGCeXYJJqLwhMz5tNS5Md
+77PLbSl/aD+hVYsJqTh1ZPBVwK+u701L3hCVs33Kqw7VXfc5OFo1i5EwcjW0YbqQ
+gnqcSxGJK0hilZqb2WYw3QrUJDDlEb8d1VAurykreXkX9QGng+qbRcNUseq1m5Mh
+7baOylIkb8Fx/NrbBRpBH/rRCwTX5YprtZb90gT0lxoXjb96h2/BkZ7laIv5p0wU
+2BIE809Sjdanop42pMwDBMjO7TwWNkshTWGXWLkr/mFnQs3imjT6Hp/koqYaX97P
+8rO8WJaEwkZdM/aN0QlUHnlc4Raz3KrpoS0GRU4yodnBNC/3GMg5xjZncIPyiZ3J
+RRniUPC/jzv4JHV2reY2NlqiDzrVM0XBVNonwSHCjrScYBQ57bylKzlyP5kojSnj
+Zlyp5IqXNSlglP+1LIluv66VIdWFxM5T7YYTZ1whtOPXQyuLCAbTJGqLkRwcYjc8
+p+F/L+57bY8Bb4fdOF5hGgjX5HlQb/aY8GPvXGVZ5KkbSxv/STm/vpAthZHE9MrX
+5CECyja8B2B+nrix4nc4C21BiFNoQ1ZsRLdedYkaaL5DgpQDQtfaPuCM+y5iURJU
+6TINQWBy/ZGjuEpFZveHrRHhbPYXqL2kt/KVgJ2V/YuQiQIcBBIBAgAGBQJT2OvU
+AAoJEN+Ya6qqBQPE608P/ibOUPKJiPQi/ft2NOEE85hTSBr15WLSF+0TGcpvT6EJ
+QPMc252ga8o/0blMx0WaptEH1+eyVXDMyq5ZUQP3EuhdgNiCnjw6HWl+naUxlV6q
+b1osRnpBFTIEypJqzm0mRleDXTO3zb6smvuFWKrD2EyhXYE7sDZZg8xV4WZw9FUG
+zkb/c/5+SProhNZJGpptD5ZUSkvS7E9sWc+kyVN4egnXSiEdwA0GHbGKOyB9brKF
+xtZKIKMQKVWdRk4aSLzZLDgp+0D8TCIy7H9uF/pERYsx4cWV95O6uKaO4LFaeSw3
+uALS+QrpbAz2w9pAH2XCoIbBSTwakJe+JgWOxu2pxC1aHdCHN9kWn1z2uyMrTFNV
+jYvsQQ/QN80jcCimQ476aqD/zX63FfDSUt1/oetNzjp7b5LGPxckLw0mLpl5hAbn
+VO3YcbrAW72wVf7ief5d/efc6pdFcF1VRUxoAQ4H8UhpMyjeNeVuunGYXA7MKEJ3
+pEhPYtdo117/1Chz1sv2tFQpfNsAYwte6xX2b+YHlpneLlKjkOkuwWMu6bIQZF/1
+pYxbM8RAri6q8oBIB0XmDmp9Lbc6IZ6jAbsFq3gq292tI6jIu5i0BJPslSlRKH5Z
+4e914XGIk9yhjZ3CjdQXXCKp3aK4oYG+aHjZAp9iCh245lVXJFjuUIH6CFo2L0tu
+iQIcBBMBAgAGBQJTRWymAAoJEHHtg3HMKeUl7bEP/0lIldbY+hLpDGMTfZAaqgFT
+ZNtZyxhxyPvPulpVxuE/YU56XWoTyyXjBtSKSLCkR6bUA/Fh8OTyDLjnv3FrvzOr
+A5wg/OEJWDeNg+blrHDmzcYjtdhBuoYQXUVT+nX41JGcJRNqbs63d9/UtYzQFSQK
+cuE0rdKvX3wwlYleD4e8DztbxJwbNwWl3HHW+Cbho4yE6Q6SpUMQsJdvicilw8jl
+T/JsHjUHpHyL5hhgrA1+gelzx0UzVUHt5MuxJTxECRIRZTjW7OTuaUYgC1GpvhCl
+1H0sqlTdi3Fq4Bzyz4hPqSVYro+E1wA9m+1XKh8FTIMgRoqV6DkICrKv+hq3Qwc7
+SAWcmRi7Uh8vjn17GaVEqf+DOTvH3I7eCwuBP3Flo3MCJelOW38xFcAzoGgde47C
+MYDiS2Cwl9ZkGIx1wTkyvbR+GvrqDv0zsCmlp51d/p4cVnl6Dq4JZnhhx65VOl3s
+1FOc79HsiXKKwrei8ozwjphSpOCfOeyj3BKx9GCjRoBJ1G1CJnxSoudUrfXBKxsq
+kIk7sTPiTaUM1qJ/wvfCAVujQc1h+x0R4f+7CtHqm5c9gFnkejj2XuXhBK/ix41u
+XZ5pSGaeGrgp9sLi1ZPfdsQ/FxUS5DjUYMtGGirBWeUNkrlxAuYQ77xMIX976dAx
+RyDdYOPZzPknCw+9BuBSiQIcBBMBCAAGBQJTRZzLAAoJELv2jwPo83lBpL8P/jlN
+DrZ90RGryWOkBhI+5DBQFmNhCTUd7elLn/6GAe5u1QXKs4JXVSrT6/roLh+fMCwN
+yjQvVlF4PLzVoetT1/p+fEn/0Ksf309hrGygQrFSGTpPh713MhZQ9rVXvfwOSC7F
+08Z+5A14LBvtzNxCro7Km5vG3DdYLFB/B1rYQBuuMoL0QIeojx3Vef3ReDhaAoOJ
+ivwMbliUUjBnPnE3eNklQNHzJpWl68Yjgrp/MsJPhFn8+HtsXTq9aJyYfP++7IZH
+MiGOUo+gDMn8O1LsyulIs5jq/V6gL5OCuA8s/z82okrviBh6wp8Qn0J9IP1AUIhW
+Pi67jN0lvXSSxepd2DD+VQC43JUmD2kWR067cfsntuw5ssT06okun7KIkNYvHw+6
+jBk4LBm+uQQfB5OJITGB0dQadHL1KGUyB4jLP0QTgcRd8BmuYr08rYMhLoDF3tUN
+uASUVHCQmgU1ga4ZDu7vbGQ9ooeSlQhk9BT6u2kBNVO1GM1a9m2dxUNJi92RFb04
+V8ZhQOuZuo+l6xKYI+5ZZlBXQNQHmKyo1K0SsmFVTeG4BK1dk03dmorLu2hms0u/
+wwDLBKqxCIZ6m9LQkaLkJYeXt8d4aaJCUoAYFkJwcicVfSHToel7EA9q51NV4Bf8
+KEN8yQQP5DjNbjqgJ6HWuoKlM4KpJr8Rwt8v8YmbiQIcBBMBCAAGBQJToBWEAAoJ
+EOLc3ZEyZpvWxAIP/iZuDbTL9cYAPw4t8I+Iq5Oekw0SlP6u6nBbM56rIHlHPHc9
+fJ25A4ckDT4DOgh7cxxw1UvrJDyBWaEFqThyPUstuv9WXbWundeY3+4suiuWy5lb
+kTF2mOiW7R+N4JORc9Dcfav3UjIEGKCtT+UGt9Kw1Q7F+rj0fb5kG3EX1553m+O0
+sfcIll3cuz3XT0XAc4lxYbn/Kye+NVI8ABCr2LGs81KbKo5MsHaZ4XBFU8th9vO5
+s1tTWSA5u5aAvLxIQ0wFC0Am3N3ClcoxEVloh9ZDINwIBENCHEgoTlGDp/T9AVqc
+SxcaK4iHQjXFOJc8XNkAIBDjSqsmubSwzcfIgSP9QTa2HTBIDdIxg5OvTgDIzuSR
+SvyLIaQUn4rKQIyU2yS0Qk4eFv/j0cMgDMBCWnAhcnsq8z2haZJ3iTi+4Wyd3pAk
+BM9Q/ONwGaDB2oOo8wSHLbFQ7DulWynFPWudzSHZhI+vDRZ6Qp3A7vcB955o+36g
+d0L8uBFzQ1BVNb2ROfbOldJDoDfa3BQx2uUTrFLHpmLbhPSE5/2qTxay9LCvMt9C
+rPB+gc6UNOYRc0QWOpyRHnR/xaOsMK7h44PbYBdLCZCWkOROJPqKkr6O7wErNGzX
+v8uqLNRu6eA38JQpudD6hiC+Nrrywg1T1nBV1vE2TROT9Dvw5c+5Cv0q/2lqiQEc
+BBABCAAGBQJUkeqLAAoJEOpEXEEHj6iN7rIH/13z3SEDfDhxX0fqn1nL5qqIaZCT
+LWV90+ueFWylh1jCDPVUuZ5O7GQQzQ6XFlQRnyFN5rEXNWON8deaxkM52H5VdXHV
++vhL1IRU2knpOuJGWUgWkdUHQNGTVk3yn2VrIhk1tmK4CE4E8o4HqmbTxy/tUlGQ
+n3VJCZ2NemlISclefQHyLB6V34mpnyAclQed4q6vRWB5VRUgRXcYDypHSgmld1Jz
+xeQ+H465F1hYRbvpGPA71Lr1PSUNNQYbLoUSJCjZF40ZYXgqNQPR3YDud5KPs2mP
+crEffBa3F6ez7ru4NnKG9+ux2gY8EqA0uYI2AHzYfBMW4GkhiRuWpjwh9mmJARwE
+EwECAAYFAlSR7CEACgkQ6kRcQQePqI3h4Qf+OuyMrZf8bnu7tUBDxB0PoAZXO5uQ
+WM9AxL3BtWBRaTA4Mm4lo57lL7PEoabjWjx7OQ61B0i5wa94RvRsrZH8FAdYza2+
++V42+I8EZYWp1aTsui5BIwPO/5o50X6eSN4UQg2a7vwoyt37qFgbIrC0IxxUt3tL
+RLyKYVeGpaaRGDjfGCkZA4eZOfFRsiHj9qXZIHoc8yxkw+mHR6Fl1M04IBxusjO3
+9wED+7jDZO2t2nOAES/b2w+CM1IWsenqd8ArsTHbbksH0Bn9JthGDJiJcM0wI+cA
+JW6A5HTKucdQMkhEq306Z5a4tgk8QJuqG2CYwESZax88kuntpUUJPkFppYkCHAQQ
+AQgABgUCVMdi7wAKCRDuL+86fajiiZgdD/4oui0hfItw/TQrSx7A7GIKOqLNyn6t
+Qd77S9lnkxIxXvuDcZCPnmrq1uFG5Uwb5wxt5iy6xp4iJ8kANeOZiabW9rwh7WIW
+qnwcvoTNy3J4mgDlXFdVIpvXnbKM6LiJ1lznJRVDmk6IiM8KjQ032z5miP4SCa9G
+K9qTszGREOj5tPcXQOLBDFrUa5t2yuE+alnhU2RjDfPSAZ1nSg4Imq11wISofqLC
+sXb/DomjemQDuexHfYiNgVoCaUpJ500AxguR2DThCiWVJGxG/vetxv4L1JzEQ0J7
+bo6bIaUX30lBiMJZY5tWUqx8YrDS55o+ljz9uMJXGkyW2TPes8hL5gDtcfE3+LGA
+nYyuyQb10TiFnfxcWdUIkvLGOIG9Myr2zcro1SVDuOj8cjwS8tThxd9TLf5/wY6R
+EUy4IcJ8zDjngU9m4axqZgn63U3815ygWZmVNFFjWx1Jf3aSNVkNnl/RftLltfqt
+NDCHntAi3wiMAQGUPgLaQb5hBBtM4SmXpDRN+qHUhMUZ06G1siHU/u9XU2MD6HCj
+Ct5hEHze9LJYjgCIiM+/xxGQO2k7aDzJM98BaYB6QE4iuI9hpMlfyHwXqDR6kKVu
+VYjjg0K9c79FECb52AUNlma9/yuF2fBODkySwK1FpAbPwUdJc7k5nOEVrcgn18ZV
+OjSD7Pf9CAz73IkCHAQQAQoABgUCVTU/ggAKCRAVP+OYghyDlLVJEACw97PpwmnO
+itPBG6Nh73Pg72Ziob50+ZMEh+7BjQGoU+f6n9mHX7jIP9rH17PGd1/IGk5xzqh9
+6yysdBdhak05WjnvOdRmJRkNLTbDzc7t8r4eNlj1eqXP+8g66dm7+7+RnXMjJSvZ
+xyFfCpmal+RWIUFhl4JWsJZS9efbcRCVfvHi+WD3U5aCUeM4xy5K9jnYLFzbMjFC
+2wiA1r0o+GvqnczCBHcOskKpzCYnA7FuAnqcpmouSsytxNujcNb+Fgh64f0vBurm
++jHPfESoHgk5lhDk4m6UEJ2JqW3rFp++rjdCbOunDFLqUq83m4AsuMd7Hmj8ObWM
+ZmN193zxF/lMZvaJjps/ULQOKkeUnnD49uZLW5IklW7T3XGSvu2rZIfOYLYx4dES
+MRU5NLBYjZFnTnKsIpKzeeOMtyXSL32YELYB0bMjUmDiJBsMQPWyk7WC4ZsRZqcT
+G5t575XGT1fdqYyCJti4P/JpcgEM927Q2jRsdCc2usbs9kB70EEjahwqNHJfaV3h
+XFkrFXF+zCHXUSm/Hz704SyVWcvp0cFThoqzlmkPwR8iA0CnvPYFjFadyP0872mZ
+HLCB1edmydjaO+pQWojzpXGwGHZYwdKpbdV/ke/jYXkVCBptzfthSawCExScMwaz
+f6Y7BOu16BiSB5MKGUcGcX1fqwUpGwouybQwVmluY2VudCBCcmVpdG1vc2VyICh1
+bmkpIDx2LmJyZWl0bW9zZXJAdHUtYnMuZGU+iQI4BBMBAgAiBQJTRDgXAhsDBgsJ
+CAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRB70YMg3q36ESnzD/4tNsZZlHcBIDeG
+VqfOGApLYv1NptkAkKpr1xUT4xFrtRiXL056c5fyevIDCOIsu0VfBgGIh4drOZSw
+hnZVizmakArn0R6+/hw77Mw78wqygnVCf396ZHJVFUqEBbiwwASeOYWFV7YuxxW3
+JxbnO54Sfl/Jz3HEQY3Zo3klUs7tAihlplC25KdRY9VKMYHWnYMjwleWhuxLhmkj
+EUpt+dHn9fOExUujror27N1VMMdeKS9Rc6QgMQKSsy+PfJMBTNy7eoxFNWQmLHnb
+0esookCY6DzI9utzjhfm6/os59IJ5Kc89fYFDpVziqUoaxA59Pu/nFBMwbsdutXQ
+j7cSSpUDLWI4D0PUXCYxK7AnGWsx5p3BeLRU3hTvuF6SWne97AB33I500q/QXPpD
+wyxHnwnIdCapRSZxlanb7o7jfIozYYb0bVKpepmZOS9ilHLALoz8wVHqn008GB4u
+/RzYwyC8HawczEZnYGAQB1QiPp/TFIthtB8mWRuSKEHh7qPXyA9IiJ2kUTagfR49
+0l8nK6QhHVemIf2GGdh4Vh4iQ6D8naenxcVxhvETwvuCZ4x4vKUdH1kj/1qF06VA
+7wCwdHZSBNCqOQ3I59eMrWfoYTRvXaSTg9Ab9ndQWGI5RqyPsjyqfv6O7pwt8okj
+hG+v3ctR1rNFlSIYVdF73nFFg5+aEIhGBBARAgAGBQJTRDjJAAoJEMQmvmh1FmHU
+MggAoLmuUndhJETEFNJX29LciQQ4gC9uAKDBmqShYLafMraPw8/FHVMrqfeD2ohG
+BBARAgAGBQJToF5vAAoJEP011LObicafqOEAn3PXET/SxR0go7/5pmvaWGeZG4vP
+AJ47SCO9+U/C01h3GLoANfG9zo6OKYhGBBMRCAAGBQJTRZyPAAoJEIqDi+TYSA8u
+WHwAoIhWiH+kSgBaZcWiLupziKEYDPL6AJ0cVzU+odBiA6jX3tknK7OV6yVkRokB
+HAQQAQIABgUCU0futQAKCRDY0yD77yxJ8OCSB/0ZrPsUyqpTwZ2YpM2dvQJ710sr
+MZVKwZDVppdGs7G+PBaC/YjeVzNvawLYxElxSEbFH/mBRya2w/NhWOuvg41t9AlR
+z8Ae0rHBrG+amR41rz7lybVZm2yUCaQl3ylx+MBSN3+iP9v+6huXe7KhNrarRCbk
+wJyarzrGNvODWyeo3O8sFgBlFBT3Pm6cnVQQZYMQTszLRp3kUL2sBSeSYWjjPHCW
+8M4OqzzeNrnqM9jn4Fks3GIaD5GgzeAU06gPscTIZLrOTVSrqFBXvmLM7dm9Nfeo
+gTB0+koBmavjgLfB0uaIX14MlPT8vcwr+KoWafCHQNDZv04pHAmBvdrcOn8piQEc
+BBABCAAGBQJUEWKEAAoJEHGMBwEAASKC6kQH/26qBxUCL1sWapTTYPkeUdV4boMA
+eU0tvKAMkHyu+xup464f9HH9W0Dg16P/SjyE37vQxtIQDDhPfEQZJtKOuQRc2w+D
+2S/OucfTSUlmpWOu28iF8062zCDrczF8/Ybp9Rrud4b+QTlxWocGssRwurgCOhYD
+Yzh8mXQqqDIe/fXuIf6z8+yGc0SShapnFM3Wn9wrI/Pq22uarEkIHPsZugapmFqv
+WxBvNOlpK7AD6OuBUicytkL1Xuk8OXyQIz18nFpqaQS9mp8UQzDOhOHjjXt1NLqv
+w8D1OxwLPkFNOuJWydQQN7TxXFOHDcHGY9myYZH7uHn5LO8gWrroMD9YvLOJARwE
+EgECAAYFAlNFcBoACgkQspS8BsdVKnrgYQf/Yo3ajmlm9kFbo2UgEAV0up1JmuIz
+M6dmcZDsiMeFeBEQ3RY/PAP7OvZthesyHjzwLgHlh4HOTmiIiCsXG/DGmJyYGchU
+6umSi/6rgtWplncoQxzS5plYBrceYAoX8yMovVnjwY7W8VAssRPPxI4vcaAvOwEH
+GlEGUgeSSAsVThrVGnPq8wEaJxdRverX0XF6EhvUT7wild6DJtUPXBmjEZCERZe2
+XvUKNc+sE8MA2k3iPkmCMwUtsR9GY3x9CCzFfVcD3RNFXV9szTBADm4KnbmKecyE
+rFQ9Ob2M9yJPRFuFaPK42wxzTgeCpH8HtiaV4qxDNZ29FdtDnKI8w5gHNIkCHAQQ
+AQIABgUCU0WV+AAKCRCyQTxm3Sw/6pDEEACekKbPq+UvKak1DHcpTBZOa9LbNkl9
+DLrUt9zVdaDA30oC/R7/Y7o+RSF/ekt0Pq7JSLPfZW1Nu6AQdlInhu8QccTxlSnL
+cK1ie0DbUanl/K0pjxeBAYC2z/0TvKx4Ia13TG8ofjME4z4Usu7H4cdTSrNofr/h
+EJT+oxm/aXiXpXVvMSpvglZomE1sgkzXQpvo+wjv2OdK7/s5VmYO8uqhUKNF/ZUf
+AOXQX5TBatcfe6JQu60CRw1Ergt6rYWz5xaZBunDVxaTc1ND+RcIEqVMgTB0Izhc
+0RkWXyCS8JBejzxEk9lXEOy1fWgtpmrVMzXkTJ54/6JTpqfNbkH7pvaXTDHMPemU
+5979giLP5wpbYoVfds3ky7eEX6S5Y8ybkIYdw+xM5S7TkVpnmrAM5PKv0QoKpMmq
+tt1xGNCDOzPyTvaSnlMObZuYT8sXYq/ovN4ODrBM9Npln0bDoOw+jaXXDHNoo3BS
+XAzYhnc0qmATf8ktpyDZt9w1qi5pWYSJTZMsQFJPciSblfhgVC8HjEZfhZaQaRZ1
+7/LPZM0u+C8CVDxGcOzPjnWK2b/tDBqiyIhxXU68BKKLIhwiNCGQtojFHl9OMI6e
+VsuzVAA0qhp0i29+ctfRycaOrBXeP9KstIqFVvAYIaq2KrLR3JuJX6yCEH2UXNeX
+I0SwD3K2NA9QBYkCHAQQAQIABgUCU0WdRQAKCRBiudz/ZiTBovh5EADQS3/6be9W
+1dbh7t9SK4eAS8SnkzpNluv++CyZYb9s9aTWagyV1IhSgQ7dW251s1DN8vKE77dJ
+Kh1yQLJoFDGgZgv36noJNDRyC8fcJs/zkK0tyoDS8DCwdVctuOMK0h1vPryrM1Ao
+bR2cJqh2/1ylz9JMwymlOuh2MrxSwUDONzbJu779aae5OMWOawtaRLZ3inWajvew
+Rn3FSQPpvd2C3T9tMwqsjJPWWAkGs8K8rrQn8Tftn4nbujzIN3qbRVMfzQm8Jr17
+SHwzPrAgoubEsj9ScPpZ/51p0tW1bwc2ollOjtyk+VZoMUEgrmpxgz0e2DYu6oJP
+6OWCKT5r0xIlA2SEeAoB2DOnkuH4Aj+RdlJnsR1hn2W9lImiqP+cVPe/0JdbTtKl
+bP35qOkfdWoF78ab9fg9rOl4Sihf4iQjGWeZIezhDuytaOe2U+RDIyMOXH5jijdT
+pbwkooTQzZmOyVEZdGoXBwKENRchvi7nakbQEKAX8iqaE0JyPCW2DbUqyFxbXrbq
+IoSjTT5bAhrC7R+hicfrWhEAs+hgIGpf7JVjmoTryBjOT2AV4B8WMA/skkwwFcyI
+ybvLMMfz5CdAioPyxLwZaHnBBslPurqKZd3vnwtfPb5x11LYGXQ49dXRFV+AHSqf
+umEyjSUwiSKi9t+GzO59iwr/wT9PJUGhBokCHAQQAQIABgUCU6Ar8QAKCRAtd8UC
+xuzOuhdBD/9neEzKYsHfoXKFnm/c7JuhZ8iqARM9dYC4LlrbCYKnN2jihFCG+DZp
+VBifnhmzN46sIYFe63nV699+ZZdpjJpOIicE4MiWZRrWoll0YafiiwbooQk4JGjX
+rk2Gy3qlE9AtUNxEog21CwkJ1e9QqukqApP6TcMpQaahtuI9JrOBtWD7e2bffXi5
+7RppT7ZVVvQElYQmozvCKRW64kzdecnXWzCxJlheS1POcX9LZPd14ZCNjoX4nrbI
+pRFRW86kJOoXXIwSYuGACt82m3IEZ82VLnD3hdzGdxVVZbYrgcRaz24YSUb3EXtK
+n8tvvIhGAnoY8oBwtQgG08gmpMWJ6oeqTcY5+z2ZdngPSN4tIwYq/GovazreRlb5
+0YRkxZj+7RwU0TFj5GIaexnYiPjpOBplSwGrd5R/ZgS9oQSKBrPx5O8G8iNu/oek
+Kz01KYTuN9u0xPM6i1TeWzWqp+9qrxChun23AveOcYEzq/P7l06P7BXFjBbC+ycn
+r4NGj4tt8U0oAi9rxIXqqfUgix8PJpNOxdLTXwmoPWQhAFgdPiErPN6f2KQlPfLY
+xQOQ6mQmNOSeA1CKiGaPvSqFQE/LQQUr6IyyH25sgH5I0onVnZFTbV2WfI+VxhDC
+fplK4NdzRHQsBlzk6MFkNT82c/HLcVtvXJyZx6eO+KWRmoawmIjZYokCHAQQAQIA
+BgUCU6BgjAAKCRAL6eMVeh4sZFiTEACRSSF+RXuKgLyFZhrI1xkF2nTGXK9iMdE8
+X/QYBVeris0qztpEKVBcChHXgLtkJ88M2c/jQG30nW4aXrTLW+lsklAAnGOG9e2U
+Md8pENx5uQyFA6XJLHIY07/g0ERrA/deuwBDFRCHoZH1FeHli1X91BkeRAVOA/LL
+RQA6OaF5uYVDLp/wXQTCHo2JlomhTqOyCQl3UtsQlWF30vUwBkNeZLIgBxgURH2v
+7LIWyj5vmS5lJxIZ/FmXoSB6O4cixXDf3dfwkHbDRrcyJB8vYE2ueU5U3AaB0NrS
+5OoOxqMKz9cP9/om5pTZpDYuGN4RYyGOSOx93dzy1TzHl99uCujaeTHE7+0ek5Dk
+drFP0dKcOemSaP+NXfG3fgb3ROY1mlclgFLlO3zym0pyucMDS1OLBDF2zKnoM3oH
+lvD8Wcl7Hw2nfNPM/VQv2jia21kw+IT5WiKoS3aX9o8OkzVv6xgfGm6z26pTFae9
+A3D0ZXST9YUHBmcSSjAYVuMcxVioXYEU4zKYVxSusuEPMuH68N6SufwaeH/r00tN
+m4ivBLO1KPswzssgtwnXpNvl4TQvx2bytZbZkbFl4+u3ZXIl6R3CymLJa0C6IKPJ
+veh6pwY4HB6VYOTsUT+le4kWjti+FCt6Q02LU5jS0Qj37jtgpBOCm2rYwlFKwdTD
+mOVwO3QC7IkCHAQSAQIABgUCU9jr1AAKCRDfmGuqqgUDxMtrD/9RkbZNxgzk7zHI
+aapoNu3H4zkZamEsEx3NpK0zuOwLk218tZjJwrSiyZe3o8D/mRiuRgC156yAhG24
+oyyQdumKUyUdY3HrCmdAW0G5DdEd+Qx6nPy3rqk51RU23TTLagWvaajKOyHxGktY
+kFqWvYVzGTqQ71SAizktlfkmqHgEQuZo4LqQwJrU7bVpmLPHlkWO44fJORHdWaHz
+GnYR9Unx/s3Q5E7NB400UYYcszUsT3WGOnWx9ADAx/iAmDNhXKdUc+zE0UfSdvyD
+emhgBSpQSenlnxS9GmW6i6OzB5Ay+t/hYtrfDXgxPVxoUxwvVNbhohopQQh2iEQt
+nBqtoaotnbFoMdAPtF1cKfiH3OTvpPjBfMiQao7IWBIJrUCR6/Z8JRk3rnJJK8Tk
+qf+en9QuhKl7Rmg+TjBI+Ki3kn/fu4HbocZo17d6f6XTJ9DxizRO4HFjI+3NqIPZ
+jGcLQ7/BWLEZEZi2Qew9QRYY6ZTFXxKFErT8v+2kOL8WSRsSWiSAG7yh2XOgVNgp
+5Hi/RWl6XlGwyLTR3WHkoZqK7YCj/zgBSwtZpZW8WgbMbhP+ToFrFDSY5MANC1LG
+fkvnLKXqLNduNJBpHjimhjU+9VK96FdFJLuccbXZUO2iPz5nu0uLfrvR98PAR8r5
+dB36GS72Q3o5FK53qFCuwMGOHhIhOokCHAQTAQIABgUCU0VspgAKCRBx7YNxzCnl
+JUt9D/9D6bjjMFVmbQOHqbAGIrOHGy2LNl6t4z3fe92nmQNTrjsTl6KOSO8jys5B
+RtQbhCs9gPRgfQ239zqdt0RIrZwfiSVGpIIR6Y0rNmy+OgpgVs4sQg8DkplSaXmo
+1ZQVXooLkZZjDq2ba3zf+MgWVNONkJh5Js5LmgkKfAbXyXtLP3VEVdDbecuNaxF/
+1edE8uyXRerMl1vl8XYlJke+/uIZ7XsgXckS5DoAVJWBoYBzm7BS7nMfPnt4ZCDb
+bTGobO4MfxzirJhmLqYhbk58GWBjcvr61xTOmXqAGy5UkMD88C4meN+lpGGBBhaF
+xPsCYb9vJT60hxldXdeIve7MLe8iJmCaQYaaVbLaMFShO0I5SRpmivpBqS9hdIFI
+Y0DksHNww3iaq0c4hb0ci1wo46B1nswklYB+fPnlab85Eh6udE+JLWManh+/pcN9
+ogThbkYqKhU4vTIKmQ1srsi5kqVpg2vAuHOa/r3cCtNqSudxHrBFlma1wRPRh+TY
+AnAk1p6yGb0kG6dVpmI4liDxmrgf+FDY/L/GoJFplF0J2IbLgowSmwPcub+8z6jX
+FRQs6+YmarfO6KzFX3OK4WAC6mS/Ljrk58QqypKcFshwBJM8dQ/mWZDJtfPzSL59
+9hrsxpwMAlQCrozP1H9rWuB+U6zde6wzw9ny/9SqcKuHNhj2eokCHAQTAQgABgUC
+U0WcywAKCRC79o8D6PN5QW1zD/9D25Sx4/qQMczSlrz2Pe+v2ZxIi6jXjrFL103R
+4TZo43CxJvkkgzFeRYCZppIdyZKnOLoUQl8T7ELwDrpUA76RvLEjFx7UYtQI6S3R
+yB4ZzOHWpLpthPX5hFVijom7EiZiofk3YohdKQ23+FyPqzXg4Bud8A71r3RfHmRN
+CTbJyMZdtrhBkh0Ue9l5N47SoJAFii/5Fq9faL7SyNaXGAURCGNda1oP4qnS5Th5
+7Dipw62ikcdwvQFTib/nWgyTF/lJVcKd4baVNLF9uBBUMs18xxjmLaSoasLJnz1i
+OLJDRQ1Tg4ChbF0Vpx/oWfP7a3dhNq1PFSZzC4cBJiGayg46lPEa+rsO+GCJugvV
+DgTqvPxsJoOe5kNMqJpL6xOuVCc+I72wa2hIigq4MY0uu7CyHm7SVp1Egju20L2F
+VZYpd3NRkIhh1WREcgsQORJFdjo/iDxLFgf2puddoE1pTejRrTdlsxDQxempokNm
+Fuh+/M7C1a12zGyDvHeA3rax7HxrmFhTjv82E3KSuDub3ZknuIGrWISW2I9L1WNY
+4KyY7/JpuIHXIlajOMG3Z9CDL+tzFKUs+PJL9XnaGkCJ8ab8SM2fvrOTppbIdaf1
+65SbqJZ0inY4Qh5MI3lz0cxNoZrG7eTuqCeODTu2MRVXcHuIuHdwm4KPrS2RaXEF
+n8sSIYkCHAQTAQgABgUCU6AVhAAKCRDi3N2RMmab1p5cD/91cHhiW2GnlWoW2HL8
+YlRE1cRpG3JfW2mrzsjuZbM0deNp+3x8BxG7Y+8gjS7YDwubXF/TkVVZe5p8S6vG
+DSI5yt5NRrrgTmhDby7A1m3hagwIgoADP7EL9tDP0Q2nER2gn5u9se4c6ciHZ1hq
+ME5muwPkHNZEFUs3jKwJ+3E9D3/r4pt1cgbAfIOncuPcKyJmKkoliYeDrUxLnCXJ
+7+0WKhhRqtc90xyU8FnTJ8GflJ2LwBEO+O9rYBSsDCBlgL3+MnVOjNEdDhhlAUHe
++7nXf6QTFyag33wo0YsZwDIdJSuMlI8Dhq9xEMo9RXox5LfwcSRfX+aQXrjpRvef
+ijBpRl0/Z43mZtPdFKfSizlzHYxtxf3p3Aaj2u2q2b6GBr1bo+0oGc33ua3zkrT2
+qi8xA5xApMVAP77vL1qnpqjRwsnIHpKpwhlXj3nZLTh0KB4iLrOsWj7TM0TUL0RN
+OrbB8jbTMVHsOrJTR4gmW4hZtSGqT1b4h6JF+uw3LPkmMmuG7uy9Hut1KJsMlmQe
+/e+IDVGM1SW2HLP8AmSlKWm/mM/hbKIdeCZokEM1xQL7Kfmd/10rJcvsN1wkZH4c
+GxrDyEmdpK1abdiCg9ZIlc9m9E6kzg0Zb5VhGv2U1Z06IkzmUiTIPBa/cf+b3QVR
+UQ2upcmmpLOme0LluIFt4VlI5IkBHAQQAQgABgUCVJHqiwAKCRDqRFxBB4+ojWCI
+B/9MEFLCjxwDHvHW1+79l/AIF1P2Wsq90/oAPS9fDDr3JIPuFjIKeKObxl8yH0O5
+KlVsWFXe04EMRvrAP0xJv7Kn3Iw63/m0jjPoSyiT+jEHYmLImepdWPURqo7Q9YYS
+xExC6O2D2z5oszDme1aBYoMkzkfAIuaT/TlTk8EwdPXC5QwR+uHAXC09muDduCCU
+ELGrgEyS/ypz47VTHZ9G6IoPc/OjavCD77Adxt5s2mzGRGBVablx/0zv/Mpx7EgZ
+KtHJR0fey2/nVmADBel2brNEHy5vfvzLlCWm+ZmguFsWEBpsLDUQ2XRU8hO57QWI
+H+kghH0tvkr96EL8kuV6UhVtiQEcBBMBAgAGBQJUkewhAAoJEOpEXEEHj6iNCvAH
+/ieQ6hapd0hkKTWiH/Jm2Bog45RH9kNqnklL6H6sp8rO2FmGEvyIe4WjAzq5c31h
+1UE/XubBuJ5fygFbLqQVgArpzIkIrcC92EWigfDSnicTgaQatdF4rW+oEkXAKlqW
+ZgDh4BGm7Qq4BzasztUYSl8ObJojhs3QidBYAgMcnCbm7ZBkF/LOYrEbfPpMPen9
+AI6c90q/fgOkr9uF2tcr2Zd2nxnOBf6IyNLr5L4HKqgiHq0m7v67wlA6khAapDws
+FhsNkvhrhJQsKDL1h5afh2aWYQWbVhog1m/fQQxoSxFaK+73yxcOkl+ik7N3ou9D
+k4o/0c/fxG311skHs3Pc1DKJAhwEEAEIAAYFAlTHYu8ACgkQ7i/vOn2o4olsbQ//
+R9P/WYKHjZLbJXKrPeFfAunpheZluudPIBI8pgfyMjR+Xfzcn9Vq0paI7yPD+LpT
+LYfc6MCAwki5Gbo+v/nyncbefUWnhznFOEuiaaI9XRBjdeY4fgXx37/hJ+drjHu9
+x1yZQ49fy1pEoxq7gc+p2jd+axoy34+fIlDmVI8Uxiirl+kP/sdBlduVwEhr+aae
+VmMXmB7Eu/gqDMQB9eSlr1pkJZ7anZsp2V0IlmzuVBWi5REzKTFcVOJDG42QbSO8
+/F5+ABGqGM9u1FMjKMwNP+1jPt79mrAl2m+vMqufPziAWFzqY6uRAmaozW2uN1L0
+tFmYzOwk1YHwkcw/N4DB2nPeK9BvK74SMCB4lLZKqqdNJbaC43+96CPwCROoM15Q
++ms5+pMkF/++/PebIfC7o/+vBmn7GFhoxf3swIN4p8b0AEe45silVS7vQV10NN/g
+Vye3/8ikM34a8qFv+OO+VCzbSLRxqlC3JyqqcsAcTCd3xXnieBOuR9N8AH+sWX7X
+GGmZgV5sDmIl+EjhoAg1y6qFbNdIniaaoSMfR/RBiEmsxKeZyhfoBFkS+o4RTmtO
+j/q05L0BrkYFJBM2mULtUN8nPqqd+wssVcUH8+cpLHFOZHt/b50FlwckMqg4QlW2
+r7CKwMQY+SWgoO2f/ggQ5h+f6AQ1becHW2AamUg3FeKJAhwEEAEKAAYFAlU1P4IA
+CgkQFT/jmIIcg5Q0lw//bzYhTR37JF5jHyJWfRBgWprLQTtZHoIme8bgzxcersfb
+t58FnUrTC2c16gLp+F/rAXCy4J+0SS7VjDGs96KEYoEHyGBm/DqMcfAA64gNJBvl
+8oRlDPG9pPNoJeB056pRm0i/5/VRSzKhE3Ut8c5zPQU0ec92U6f+smcgbxA+RlVG
+InpKWiIcDiMKa9CBJZ8ZrChTZx6hU2RBSnm17pEkRi37uyfWqj5xkbLHDNmH3LEE
+6AjQ7xUYgpaYuIT9UJ2/GeW8tlUbrdWV86N/MUyBR30EWaPiwBhUCf3MLHMTlKik
+PBwENFINrqhfb0kEbg5BcDzVdpW45Xx24EYW5226nD3VY1/2p2hyePV/XIzGvHj2
+7aFXvp884PriJFk50z4xVm1cjn0t01lBg6X2vg5BsHPoLW64uKm8mYnpilinKDXn
+H2zpzay4hlyGK50lsYVyXSHODtSQgdxIF7h4+IUPujMuS6LcSz3rP2mJhjw2/M3X
+BchL7DKoj0wlYg+dy00VmxV+iPZaaQOcYATWx9xRV1j/lSLGAwp7S4ToTjYO300d
+CaVSciQ31KUbAWwFcwCqk6llwOJDP3clqGsgJTxTDtuv7JvoF40PtY3QumVMZkYI
+K9yVXGTtCJFtoupdbmQmrE9Vpa1d5kEz7LVZDLIjfNMN1ml/ONefujSjhoZwREa0
+OlZpbmNlbnQgQnJlaXRtb3NlciAoU3RyYXR1bSAwKSA8di5icmVpdG1vc2VyQHN0
+cmF0dW0wLm9yZz6JAjgEEwECACIFAlNEOCwCGwMGCwkIBwMCBhUIAgkKCwQWAgMB
+Ah4BAheAAAoJEHvRgyDerfoRQ9MQAIGPzc1uCjK7p/7Ge7qmW6A8GiVngYqRnLka
+epoJ5CyS0k15V4vQr6hcVVTLkAUJPclT4CM05ycGZJg2RZA2qJ2dkh5XS6h18pJA
+shBisu626rGIikiRHkutOxN6p4spgvI8Uj1Kpyzw+1XaeHxpsaTZIAkamIetI1Mh
+6Wkyz1TXJw10A7t3TDw+8ZCDO2WFPIbK8QR0X7bMaQ0yXeHr7YIz7H8RQtkMMrdz
+5iNdjIvB/0ZTWpy17/ydFv8JwwUw9PygMTi/UY9qZnh6MpkcvErm1gilKcqoCp/D
+p8MLCwU1qAQGey4iAryW4VrbB/y5b/aXKC1usQyAQ4aiJkk5BfgtiLHZMxpzibvf
+IqZuz4hq2wE4l0NfxOfrsw+FIaKxsRC183ji5AMK+e4tRpaQxYXqk/dQ7Ge9iwdk
+o3e7MAPmh55o7lWhMWh7QDbKshYMpEqk7iE85SVoqOcW+CvbqQe1btHTDl3cOl27
+7Goh+v163423I/oDNJB39oLvt0YimfDtJYpsvEuyP+Wbmg4lrw1rAB515Dd4za8k
+ixt0YCRVpv4Ei4ueODrnGcAxzKbcaXMqCrSz66rJJ33cOTDL0FKbdnAk7a3r86FO
+AS54EgURXqiVhndSLx9J+uL0UOaU18aWs0arQTfnhnY718GXZC5FT1ktqcG2YRD0
+NA8mrss3iEYEEBECAAYFAlNEOMkACgkQxCa+aHUWYdT2GACfWDWjCOmQKv2/+lWv
+RDpwYL9ZqGwAoIIM/NO5Pt4/pHq0N+qeqwlqlpngiEUEExEIAAYFAlNFnI8ACgkQ
+ioOL5NhIDy510ACg+QyalVyVJQLFbbnndmg2NY9zaxoAljX0fT6wGDUxvDgHM9GC
+BNoBfPyIRgQQEQIABgUCU6BebwAKCRD9NdSzm4nGnyzSAKCQPjQ3/AiigRe5nhTf
+9RLshQXxyACfdUZCHWwxsj6xWInSBzfipl7yDRyJARwEEAECAAYFAlNH7rUACgkQ
+2NMg++8sSfA56wf+Ica0iJ0EYHqbkPLN3kVsvFrlVK1+32tDGmXBLNWbEm1f79ef
+rzh6BgxyNnMrkZEY0aMlOD9qdbFmym2tZeKBgxWsd0ftKQ10wRQJtZibl6SV+pNH
+QplCCbMlcZFo4mxr2fSya6/Q8bWGNYWLJ5Tx+YEVdoDHfBEh3qzNmEJAf6l/9HSn
+xIaqBPs2/vS70XJZwvVaCo8fg+CtIsnTiF05g0EqzaJNJYZ57LuQkReZuzO5Ksn5
+gdBVr9PVkORkB/+q9B5AouqgzDLMgohw5el09MM3JFqVLMVJAkuT2o4WXFexTQ3A
+NnNx1xZ3r8ILO2vWQAMN0a/km65jo6SHLAWQyIkBHAQQAQgABgUCVBFihAAKCRBx
+jAcBAAEigreZB/9SEDBHVl9k5s1bHrnI90bcKLA+5sqU+M0eGrbiiBp/SB8TkrVN
+Mbqy2D9K44XDhsxyiAj/x3adZkXEtkQUI2IyBhBmOhLGhOUnqDwNGHvHKCWNjerr
+OFCTsU/xrD65GZFmB8K6YyegPeVf+ThRUPPE686PeqdHfJLKUkehdFqcYCeBxipj
+NMAGo4USBGbxoU9+dCPPvfnhuz8ZeNNi2uY7CXDe+NK39aDG0xio29VUIuo6coTV
+Yi3k4imdILY1BYz6ydD5gpLGFQUOkBB/M0X94SbLpyJJvGXDBGJ7OuSVqyaLEvqc
+jh7yvvck2hPfZQJX6FwOINcXGLP1KojYJvlyiQEcBBIBAgAGBQJTRXAaAAoJELKU
+vAbHVSp6IkAIAIdgWWkJnr4neSBk2yiQE4zhTXY4Ol8790UhPJ718tEpObBMWvqQ
+5eci+wN1mGb6VT4e2I5tdka9sXXMca2N9JFa6jJDm3DPQh8pQZ92D+XqODyJT/zD
+PPnJXxIwZ6Wg8o7iGIgkVolMr8Y6lnLb2JDE/gBkMFP3wvpaLUMHqNqFTgqUfs6K
+Hev9B1ivLaKmIINAn2WfdEk4MVYpcYy+nbpKXJwQ4aXI8KKUqnxs9pBd2wsaBFLH
+EhRKLVmlE0wozs6mvsEnLVcpbgjvktR8h+hVrDd7FLa2gM2vntfrJikurMyZ5Nh/
+YshHfu9P3snEM3cbmpE2dwbHmndhopy4cvqJAhwEEAECAAYFAlNFlfgACgkQskE8
+Zt0sP+qfdA//a4CIQU9u/OYxL5nAU8HfpeNPHO1gD7Zc3ov4TBpbjMAgigGuEpl1
+GzinAob36NapeEJJslbKZ0Ne+6LU+qFaSbUOC8cVHBFxlFlELXqPMiYW5vgViXSB
+ErVey38QbKQ/PuRkS/niG0FK33QU5xtbWf3ghTS6mVDqE+isjxShnTsrAqJp6H2M
+9LDySuVaNG/liGjN8myV70JXoWsjm0Iy2MeQ8kfbPoMGLZL7k1uwNqNgYu280IvW
+kx0HB5A+z3C6sfWaihk6tk88WGnOp/zOg/BqWaGrwBxiBFobJJWUBpsBwQO8fQE+
+fLXKsyFMOVuVJYCUEEFF0txuvAN8OdEcV6PjvIq5CeXJJIfCoYxBqG49ExmcNm5P
+R86U8WhE2Qc8kWxtuhyLvqXO7uWJgPbostO8IW5SBwlZkEizC4jgt1QqK9elgQAT
+0UpcC+zWpzlLRBIMksrhw0/6uT959XJ06qWdGHax/25t3xsdgQhc6X3nfKryAZuj
+03zHOdBoyat1l5Ep0fsFqtscCFdXqi9Ww++Ily16VxemFdE15qYIVMA93qqNM3A4
+YvJ0UIQ5k/r8pJhMkTHItIB6cLtKG7GfVU8Ec6oFrZYsCsDLpaEpmxkOyQBW5yQO
+tgNvCUHxgSFleb4zynH7nsl6nRw4xgoO4nqF5+5adqJ5AFX9G9QNJ+2JAhwEEAEC
+AAYFAlNFnUUACgkQYrnc/2YkwaL/cBAA6uREsOn2c0QEZpbu/AbuhzdyDkkHyEGD
+lE1xn12ahSYvL69a4+9RpXiZR75SIQuPmbqCL0+2OlCBD4r50XKazhZJ9rYSyZva
+9Hv5cgVM8k7VK9VMym2q+rcjuuG/rxzl4coyXdbSJ4utROOr4+iHXLCvRueYngYk
+GlpUva35IicuxoeUKq4YWjfwcvpyHtD4RC1hY1vYI0M/+Q056YMwnxZCzr32JBos
+n8j9Jt9WmFYTlcxEwX0Sskwf+2YsX4mV89C7HNitya7Ix1XBzB32ORHsTikw8377
+HqE9Q5GKgkKFRZ0lK5fZSxgtrOY1TqSLDa5USkvvDOob+5KNKJbMeL4ZPM/0LR6q
+bIP1zLTqqgkpB3/z3RUNLf9YET8Dbqyvhm72QFfMknoUa0oss1ZinwPE0cin4fqx
+rHArOqUL2yQX4RYeL4EWtKXsdXoZ3X+JZ5i6SCE0DEqttLVeZI1G8BhnxXEzZXUI
+dY4Zc+LzzyAfgykOK9/fQRqDHMe3Q1R8I3RLBEW5QHtyzJBqGoOcH0E8lMVvjt8H
+g3y4O0v2oAcKeZnUmPExrGJGlaDdOT4UfKnT71smIdLChCQIvE5w5mvc/hiNiFMR
+11yPO8PMzIbT86s33x7uRKCGh+3MEzWBLp7OWxV1glGLxYFnrt69e/j3gw/MovfH
+5JM19aV1IrqJAhwEEAECAAYFAlOgK/EACgkQLXfFAsbszrq9Mw//VE8izQ3cInA4
+yhCt142PdX5VHCxZWjB3Ux/XWw/EFsUv6yf40BT/lBgSy6r3xzkrzKLbUyEnrBjQ
+0c8lXwrx1OFQy9t2dWvz8/Uash35eC0Z71MXpBpB/iiB2sikmDhlkIrG74LiL6WH
+Z1y8lpXusf8+AMRBAnrqzHqDk2VocaCGCMO8Y/QYVJ2OaCubsnt+x2nzJiSfQj0V
+vmCrPjrVqYupM7h8lCw1eZHGtf0B3EnRb6EgqoZokxQTN5izItLTFGikk1HUZ4Uu
+u5Uf690zjTyJFldatoX47zTtIDKH4zzi+38Vxu5acThdb/SlTePOCKmOZBiBs8Uy
+aLCObXpGSVFc4TPbpwBhWJd/fCVAuYbtnZedOZw9xgXKwGuKoX5Rw4FUidoQfwLn
+bxVxfq4xFVQeZg2xopmcWyyKydwDfArRhGRfiTDM86V63jdFEXssm4t5926FhN+S
+KekChTz9DZVAtVmoBXai96ZHuVKF1jT/8KlwJw626vhIWi3p8SpSGM1bSbkwryFn
+2R9LokmgMQGpqzwtA6O965id87LZJrcW/ED6qC4/wFx2PJpHnPstAJs8Q5mzSjee
+lZdkK2GyP5l8ShyLoO0WOBhdIBXJkzbo5BijzvMgc6LyUvo7Fd2SGLn5HUdmyl4H
+UxGJ+3c+QQKfqy6izdtpqlX5dRMjLNOJAhwEEAECAAYFAlOgYIwACgkQC+njFXoe
+LGRVRxAAnANjFpGNfVeDUayRk1ICa4/JIwsUQue5xA4AWgH7pQhz/uw1nfB15CFN
+8DD0qzFhhOp/o0WPn227f6iTI2CyBCUxXDLGQ37SmDTLZji++/XlrHwUQqRK6Icw
+jKHQSAxf4Ba47Z2l1SYjnqvMjU/BoxMHUsHoOStKDUgDtGSGa7sZmds65yX502ua
+WGM6/B83nq8jq1QL8Y3W5U8vrgDiuBWO0zz9/jwxquWo4ZgzrPh7lLatEUeKax1A
+HDCvn7tplieCkA7hHWEGa4331zuBJHAS9+0reINMlVuWgjiqUOUIxr02MB+ew52P
+MzK4fvRYK/01ddAoEHy4/77pixFSfOGbovVpaNTpwLtgsON1/JrXvO+ArdGnOz+V
+QANq3FVXiCExB2bV+rPdGB/Z2KmQCCUBXedM0RQgnQrlKsKYp4oWdhaLPB/BuptR
+gA4snmXRMOY4i/j2cWH0z6Br7tA1xj/EwYsPp/7tkuXQR4Zl28djETC5wEgrO9LJ
+s0s7Gqe+eGssM263JkJZ5NGgUABSylBUyU0kxOi/qnmXoeJjSkzqbvudj+RcB5sl
+awW9Vxqr2YVKDntADEbc0+V7yziPapaxSMl0JD+WUrsg0jrS0RYKwegblOfagfBk
+sl5EvlFZNe1cFd6jKrDbPVHFVgNTDFJYDfbtK2nyAuUOzIaL9nuJAhwEEgECAAYF
+AlPY69QACgkQ35hrqqoFA8S4wA//XvY0QIIs2jKvdwy54Dm7wXmBtR0YPt7v31Vi
+dbxocvAGQ3Wr0ykI+nzTD4uEDs+LgDlHn3PJOAn/8T1y3wH421Xt5fzTWjqXI0gw
+vwPFtyfAE0vIFO9rW7yrCgavNKPHe2W0b59n7EgoqdepM/eL/VSyfpR4Yo9GI144
+H9SVldRBrArPiRUbcNhpKnY6ZdTfHYhxz9IIQjbPOdYQ4Bn2XXAx9w0Dg1qKE0Ao
+Bk4OTeZstxGt2KFnIxFd/cAN9VcJqDWAWER1EHHe+kPia4XSblm6XhBzJ0IMuaXB
+Y7hBAtc51dOsgvrCKxyUf3gWPtH99AtHeFrgpfNeVMRpakz8XuM/GJ5BnJLT1kRN
+BNoQCBQbn+Up+EWNroKk8x+gdcY2q9hHrcsJMXEmaz/yQM7DtUNo2mHL/qllPmBq
+pXlY9QRiwrPpApIM4d4XGxOg8LXMZSdvUTho9ER/9gaU1YIhfDPIDPr6VP4JqyGc
+o3Ci+hv6cBLTOrFceb56GfhpFLuYEq8vRs9XCDTDJHIjm76LfFLu7qiDFwxJP/6o
+Vj4YHngk/CDnXG6u/7HGaUY6zy2vzhtlecM0V2ZHnIfITTKcDLujjOT2GXVajQuV
+y5YnjTh3RU474m/5+AVlr1AFGmXWKV4hs/+bxSscMhZ8qIj4PL+QA2oL8+Isux+M
+ivJOtVGJAhwEEwECAAYFAlNFbKYACgkQce2Dccwp5SXN2xAAvZ0rZpOFwTJyEsFG
+dgNkGs0iROSl6a0p1yd7yldafqaksDsIx6QrZKu0nJx5ieySzX6pmNsE++0730gg
+3vnDOim7BQp9q5rgzL+D3niS+TFyWHQozE6FJkRQ6qTviKRgcOMOADG5fd//s+Di
+eknBAzx1KWOblJsb1h9VeeOqOvT615u8nOzV4diV26377FYmkQwRAF16+nMP7IOK
+pPVPTQP+ZwPYI9F1zLu2UepQKMpYWMQ+SnD7p+bo4tSiyAM/x51bBc6v1pCFxI9r
++sDc3JW4+ABHu0hAHF66ElPvGfFXJozNdffaLuV5GJruJPb8QwE1V7wGbAeSKoen
+kBwST1FJGid9pGr5NLtoEZfwBjuaTnSeaJxql5e9oSHaqFEXZXglNzafSSQbJbve
+NcDgko2JgKhgwzk5Bz1erMpbauE+QgKb6iY1IvijuggEFOh9T7uTggue9QWz8RpB
+nQfTLGWwDiwHRA+S1l6n9QvpjIfSrSVnp7lgImE0wLHPSGiaLtuxIdNwgdVSuvHK
+tucnnqWX/PSwObwtrL2XDuBwftoHjisQFvUjWq1ZBPAPzogLqTOFRd8H0uPvKEVV
++0d55iYKSYaK+5EzI6jphNMhrLhhpR1y4p6o82lQxB9MrP2TObdaJJ8XCN26j4m6
++vwnTw58xZPYz5+XfgqV1cKzX3GJAhwEEwEIAAYFAlNFnMsACgkQu/aPA+jzeUHB
+CRAAge25UYDXAxcFuQnOoPpRFsUy7X9/wBFdr6driUlnxMNV0MBaB61+uUE8WU/D
+dP/nqsgFeyM+r9j083KjUn4Accqeq8tW1vGyZyOa4eWeBIe1Gyd7POZoYzZ2Vmx9
+8JBqITavgfcz16Me4CWLvzXa93ZupiBeSSA3yIqJJ9P0/OAzU64B7r5oqFBJXv0Z
+qg7nOE7fKdRwko5952HzaJRopoHe050Mbe063P8F3qohYqShgiKw8McuJPAXdoV2
+rTju5iowub2+GNQxMdBt6SBD1nf1pnSmi4IcAh6qh6d1VMuS83i/1oBn8hz+oBfC
+GSVcdMW0nbcCba7oYGXb1vhXknBagcPIwMv6tdAvV9m0/swi2W/cAl82atZPXgU/
+6CDsAeVUUmRrCp61mDRKXlP0Paqfpvar00hfkSBX3Eqtmy3NDHzU03JbjhJJKcZE
+HMcksd+RJGDev4YQxtdKYbFoGwzXaVkBsJ32/XxMsuK+8KoGhgG+I77x8V+AA1sT
+k8MSZ/EsLaklnRRUPRkCz2g/kFkrNiVBK7Ijf1Spy6XFO/zN0KFHZ1TqpEjOXBHR
++cnfsKg8t4Y0YuI/Zl79FJyt+dGrnCNs76T7kiTOKSPtC0m6OiZ+HfXID4sQb4+S
+xX0P2kuwajXuM3WTXUD7iIwdMxvfu8qXX/8x35Q8OwzGQymJARwEEAEIAAYFAlSR
+6osACgkQ6kRcQQePqI2D2gf9F69ygtZXSxy283+QR6kDH27fvZjmTeH7Kd6XQ3r/
+8U6AyE04kI0bmP0QItuvMW24Ek2QSSyz1W10muQdflL2M1T5QCYby2PEy6SvcIGF
+suYPmquY51+olKS6HfZ9ylYnpq4eoOYsJeu30gLl55s0S3bmLmHQ4Yd+HCONcgF6
+Ikw1Zutcq4KI3tiz1viKTitrR9JND1WZUCxPb4NDK8h0U4UmEX+Ba74IfpP3RRf7
+Hda1E706Armqfdju2hFUpTYv1buGYNGsux1Bnck/IYoqnfNLl4TAlLpjzfxSyAPe
+qsF7BYBW0V2tk4S/kd61ZJqfNjzwzk+vLv7oJuSt4NRyXIkBHAQTAQIABgUCVJHs
+IQAKCRDqRFxBB4+ojQ7vB/9mZYkHW1ae3tnmK6wRuMLJ/S1oVXvvtGiPZUjNqDFF
+z8VaLHamS5aKaFj3WH6NncFH5gr3tENwkzj2Q9bFQYBwXb8rarLc6fGxyH7lL/Db
+7H7tgvDNX062ouKYHrXUb3Nqrprf5LCDoNWECZOxKyNWi0YMKlxv+I5D08oRlYsA
+qD3qXx2Y7mQ7mF73dU9yXeBWPrWssIWhcfDtuYw+ypUHIc5jELPt3CiQ6cQm/nmm
+1l0EexKrATdaE1pekZkxhMK9vSmjf9C73yR0HXJfwlHlqyDLRnyXz8K0Rze+3AGa
+0EMBv7HtA1t0vARsWzlDv8F3Fk/8+nlDtpn9P9kD5mJ7iQIcBBABCAAGBQJUx2Lv
+AAoJEO4v7zp9qOKJRjUQALqH272emACuFw9uHWjzNFjKSOXps1uetlagzQjuajae
+G0B7mBKC/U0LJ42Y6xcfQJlXO+aDG20cDnAO3RObEH6z8joDvKPw4If/jVosjDom
+kySlaGeb+X1mxtk1Tn3cavFNj4tdB5dV8sRH1SdJuYyWNLgfTG6UlC1evMubjt01
+FNlgNmbizEjzIeQqyv4Y8pNxNEG/em9AoHrpsVyi6LxQbO59JMZO1O5x2bO0NabS
+xubOXFqH3ou2CxSZ1X0RICmuK3nhXt9G3MoIeAKYSoysi5J9MAdDudQd9NHhP89T
+3ZUrvsPgtE3OOJFOg2FsF96hBh4l+oQrb6XMwMBVlQRp/pemSorslE4RhGgkN+6A
+G0zzJ3ptjrEN2zig9uSRpkE1lkYD3ATRMVUrC4c+L90uH2KalqRsDGBBDq2TN98O
+wSGWhGj/DW3kum4aMA3ouj00fc0J3jEEpZH827xy4oKbxx2lkVNCk8Qj9AYguiWT
+OpVNTHzSEffijUO0my4muL1tSuz/uXFpWh9APZtfHh91IvtDjdmcGXcX34rFOnkd
+XzbH3g0oRNC4liH4gz9mbW1MGWozGR2yP4BVCwq38mQyWuUzGb9KQohPR/6KLFfn
+bMDFjKmp7ajsL6yy/gHrUqI69Agn3nyF/KQlOjkMBf32oYV4sA7+dHmL7l4IdEfL
+iQIcBBABCgAGBQJVNT+CAAoJEBU/45iCHIOUYU4QAMadm4gnnfCMYiQB+V4s/PgG
+eciTn65Q+ZEk8RFiCW2VvpSFbP+qDxWTvxktH3+JREfkZmSWGc/fj1Z5SIksl4ys
+zdZZJv2jdoAdkFhI//itD8086LNA/ElywjsjmOQi8pSjSUj+8cXYEZmwyJddUzr0
+mvoYUiC+bAaJKwN4ZyGdjKGcmn4vByT8ULTMBjLUeXgYnrPKVgk0SUBaPN+e2QoZ
+TR4mmcQNH6KIoGpnwPceUd/eNu03+f92pBInZQCCrP0sCUavZlBAg6Za5Or6qYYf
+myY5jQtI/Tgt2rMRXPVizQYY/b3z/hZQK4A5TigvFjlfMozs35kOpOuccqf2FLWX
+eyOirbZrBp7A9tdKveKVSqxMl+0DZjI3fXD3dw6wBrFLWlUvYKnj8aGh8xRYnV8Q
+CthNJoFWKiA5hRW0qUasKY4w3DbVS5F2ozyuL0G/FtK3uEL53c8FGqtH74VqX53K
+swK/5SlQPWhEsNk+kl3TZfmVWbDC3RYeXhElBEqSmf4QrmYMYQMbRUECLJPlF/8/
+JBimtVW6CVBu+fR4D8qMXWoGeMJIEWWNKAgOtcwW0MeciobbHl2tFVCO/DkxvYSG
+6SLdHxoymAqpxkIJNJDU+FuoNIZpPdJtnfZ9H2QEBz7iPkno1k5f545Chr//m4Am
+Z5EKjsYuH0BCso7gLljhtCpWYWxvZGltIFNreXdhbGtlciA8dmFsb2RpbUBtdWdl
+bmd1aWxkLmNvbT6JAjgEEwECACIFAlNEOE0CGwMGCwkIBwMCBhUIAgkKCwQWAgMB
+Ah4BAheAAAoJEHvRgyDerfoRppwQAIAOCdK/ELZoiXn2trTzBUPq1PNvY5BNHgbH
+1RQfjP80ljJESfiMHZWBhk3ASgveg/fOhx9DiUXZyrccRR8ZFknReCw4CD+x/4hT
+8vhiufk+palRK+0XaokE6/igKUCV8WsBcF9EgEDBHDx4Vo17DA2QGlOJ1eGuuJJE
+db7qMIZq6HxJjKgzD9q+djLz2LZS5mSTzPQiJPFZo0c5MDmGCyIpPed8zAxDhETm
+WdOZM4Sq7aupWDiGf6dGJjPpuWMOGbvwlHQmlCMvBJVsgg+fKBT9yn/iJqUphjam
+cyKQxgAXvEDUl28q9vXlkViMb358XTrAmWxmqlh5Acf4ESNZmEDqjdaCHYWbpFTF
+NUH6NnEGYpuAu9ybJGV4ruzxZKM4B0Fkq6ZEEAAW0Gd7oIg+sahmQ/f7pPM7xj5z
+IqG75tPTS7yHyZh38rUaAaRhuoKZoa4D7SCQ5nG3UnOHKGZODV4ZgiPAHt7YyGxg
+1HQvvxmRq+DUdKFFl6PYYiVp2VWRifK3cVTS3rjSSU6tHRDje3sYvdCdzVtOJ2XT
+b+XYTCmAU6jTRxQJrAlIZtJxvbvy1Zfp1G3/K1F4dsEQwZ0HGPAVo6WG+TjNL2uz
+Qv3i60Xm/R1m/PXf0Dgwpst8IZMp0Yr36+zoFzifb6uUNehddS9avH+LPZg6OKhb
+1P01BJkciEYEEBECAAYFAlNEOMkACgkQxCa+aHUWYdSYbgCgpuRaCDOpXkl0v8J8
+n1+Lfy6mTiYAn08iGSWI90qlgrGcCSOyQaE5l3RyiEYEEBECAAYFAlOgXm8ACgkQ
+/TXUs5uJxp/lBgCfQdTYyLddtoQeI4bKRo6PiAPM9F4AoJGaAQZhEhgbqos8I8w6
+k5Jhkxa1iQEcBBABCAAGBQJUEWKEAAoJEHGMBwEAASKCX3UH/j35yJhKyLSnEf1f
+8Bp/7oG2UyABc5QqHqgUdEESteqtZNKY0VPidlS6LmzN/w6WsxsTgoGOplaI4b4F
+bZ5vOtXruTvuibQAZOrETTwdnN7wdRPl2D5MbmUEVeZ3YeRG0i6jPWBpG5EpJp2E
+ed1s3wZTp7V5C7rpZeh5f0vqwFmNTN6lIq3nbCYwAoV4Z+LXOGDgzs6s8czog2Sf
+JepgWHTyxS1qPKPEKJRvrL5w8gcwtcEHrRivm0b8Wsx3P22a8Xd5ElMjhdAA41KV
+dJgZIvVMSwLogZRvXMsnhUcE2xeVqalt2nUQjH2hUHJNmB4O0j1Pv3HqXY1RxGA0
+Uc+Yv/GJARwEEgECAAYFAlNFcBoACgkQspS8BsdVKnohcQf8D6dcuXOFhPkZ/c/N
+Z0AH50YRCc5NO91cuJaKR/IeZP0XR+qXrcEo8UwFpGhGmcMfzuz+Zkg4Jn7LUyBs
+4gY0/4btO/iRHJInwowlpBZ7TXvJG/HJuAc0RFZ3KWEsAJKbOCOIAic5X1TkMggo
+fGhmfyiOYLXGGjZVgpMfUKyN5zC+QSkvFj2ObVBhaypcybW82AN1o67KfMuOBvSw
+b8/xhr67tKCjPBJWMQMCOlAQnT5YgYyEeszCHqZaa3VMxFnzme1aXXjLXHLBhj/i
+7FkMnoN6TiozW3mItN+sGdgNiNRMY3ipP1jgFkpel/UCr7FQ7NPibjOwOOkNLG4x
+0nDDAIkCHAQQAQIABgUCU0WV+AAKCRCyQTxm3Sw/6sk9EACFINg1hUdoisr0dR1V
+w2/FxtiI7l4TdQvfAVhD9oTdC6dudAvRG7W1rcmyRS/Fm9WA0kUgT9EzzMQ1iciW
+vouCAYKrRDWMWp0FYJ8+17c9XpIk1wMAuEvvBUd45QBD/2gSTKe2e+taYIXGTx3g
+Kv0IKUbocWZXSL7kzXhrkMH8eZwrP6OVQAr16dpchV1dTDdGc7mYYvfC1TvPkD/S
+PsGoLHuEp4oX/uwYldogBe5DJwdxDgyF6HuzFmAKS700JKhZTbP52mLTUyfxX67F
+mjXSrozus5eh0ZPyyJ0xlwb508TTUKHovLPybRvyxMQVBJQNvqhfyG28eqk3bFQ1
+mwG1Ab1s+lE/zzH3YAhmUldVVGu864OxAo5Y8PiaI7Atu3S7JYlSna8xxH29jpOO
+cWwPSBvuWlWDZ9xcprBcGYFBxqIwOk8Nc1SladVYMqwsnrjvcXZ7kKninEFPHD3s
+eQc2z7658a6vwkcxRE5EqKxPf55JDOmsBUMrn7o7OvbNwW7dVg5B9kKq03wlrPQ5
+klWKXHuAE76IkiC7EDm3VkoHoKxtg40puXwIz2NjrTdrk4jsAMw4kkWvZwh5oSqW
+SxEcW7MFh34kERbLRR5HbtX3lqu2qimyOy383uxJyTya/Outb6//bhQ830e8na4d
+tLAL0ZBxz07c+taPtvL7SKRigIkCHAQQAQIABgUCU0WdRQAKCRBiudz/ZiTBovLe
+EADH4Ojr6djykS15pNOrRpmz/mOq2xsEcA5Nl/yikbq+fRf9qUSE8tW2qXambDc3
+j/ZLHZGHNW9wz0zMavnWJ0NEqVOTnDq2cq5Cl99YmTvTgwl5Z7JW74WITD8OESiK
+NtsOYVHcGlhzrvqfTMagNvAnAP8IJjXL/aEn2rui0pBhMMqumU/mKvFw22uNtJHr
+qPB3oBWJDBbW7nZBw90LnMTc+NFOvkfIREvC/txBvB0N6bW8kQVIp8VZ1dtshTcA
+xOiyHwqFBu8FbHWiKhvu1XhOkVefrX66gT4ecTQKi/vcj7vkarvzg5kT0PjkWxs7
+9VGjonBvNflM/MxVF+efDKXeZ2eZcGH7LglIIYK1TVDzObwvJdBJcfTxmzMvP8XG
+cD7m2HPZ+zZd74L2DoaOGZ/Xda1vp5lbhtXsn1Q+mblZ8D2W5w5mXMeZ8TDe+iSm
+1w5hIQbAuM09OtDIGUO/aUYfpJhyAv7FE7yqpOxmnT52/xpKEaJCfiRDJrnaDIxd
+bflAKpT58ylPfjOBfMPOecdkNW2atvUvCxfjRFd+B/744fz+te+Rde5eqI6R/4Am
+MaMSV/GzjDF9ovrEw/ypVr5DDDCzA2kfSpFZkKPBwggoXh41apSyovnTzbnvLWzy
+yBs/Smt6cPia7DM8EXked4B28KPteMyXWoDJ2qcT2MXb8IkCHAQQAQIABgUCU6Ar
+8QAKCRAtd8UCxuzOurvzD/9tycNK9UkF8Stv38iToeUPypBBM4BYRal2oQKu+A+/
+PScAWmZ8ZVCvdF/ets63Bdt+TUhh5QFd4Cpdq/rubHMDyI8zAPj3hKhJALVgyEvo
+2mOmhZPKN7hFpndkkVBL+37C4FdIo9zrCB7PoMaY0ITB34UfXX/GpHz6jSNNCS8z
+f6vMcnOA2tSaGn+SozFhmccESe8L0w+JnmM74m9Mo8jFlwahkw1bdRULDW9q6PW+
+eiX7hkYtsW8hie6HIZRyFBVHqUGLuqqwoWNyfvpE6UMkpeY8DpRp0yJxXopVaXaG
+6vPZ6QdM1l86HAixJfQPLxm7RdmMrSUC5+MW8uJMe2ndlDbSGS/EbFHWtGGMZ5hS
+ivDeAPxp7Mg/lqRzPWqAPSDG4M78LWqrxZ4UE5KD/Gze/aYLHhnmffk0I6Bc9/0j
+qI+DCSHwcquoxnpFij9Y315LpMNLllC8FZPFNnRR5DdmbNEWZVIPSLrWAJUB7M5I
+FQnZFlJ2keElFxn7H6ixyFAW+di+6eILke7LxcfM4ZYMcKkP7B7aRnLYTrrZekiU
+Wf/YJXqBnG4FDd1ulkyoBkTnD2oIDrkuLFKlzdWwpC6OLKRTMmiY7NRWVNBsioJS
+wBD9XY5Vczbq6HHzNm4dX23jxBJH+EfcXOH7mGJyFGoFv5Q0ZE27s8ybFlNb4Ebj
+5okCHAQQAQIABgUCU6BgjAAKCRAL6eMVeh4sZLaqEADIJdA1w+kzvrhXpr6y71K4
+WJHyB3CzXU8HXhGd+lGZTAJYYvfZPVZVHIWNDahJsw5ZqQBjFaQrIed1iWYGpsDU
+hojuJDcey8lG9oUDxAk2F+fxGmIYmAUU+YGUWia77Gu2JpEMt/PxDw1XZQ/EJfqG
+jIS8+rcV32jtYDLNjdBO1lRQ1eGJFPw+hdKvoSpCGJm2A9MnU3jpTcBhFx9ntvY8
++tWtDFKPSXGzKSyQtuMit1pYaSZjCwX8Z3qn5goYg8fgSeG+9k6ySMg2gfBHNf9K
+d0llXnO+ude1xXsGTqPbiwgtWQpENoIIOl4OEVaHiOul5ZXwdUibBG31hmPvTdci
+OwAdL1D18WPtV2MlkuS2PpXKeF3/Q0iCn1nYC7VdTcxvdBhq4fElVtynynTSdxXL
+X6k71pZkaWeLpga29KUcGBOxT5azZWI8iMPHX4szdF7xpLCLTnfgZZmOOKxpkYrY
+Q0+EzTm5s0nh/Pz8t4dGA8c6tS/UrxzgrvQkt4481BXVjKaYU5hQIwqV7LX71qNt
+iDyjf0OHb7m5gtfaink6M5HM/RjIlwLsoKQdMqbF60duzLPmp/gkvVMzTRAw0Xiq
+SWFuSGqsLcTasK0fnAxskau1Rc/q42u4E+6x5ie5A8vrdG2FXqglsPJBssaUrLGp
+j0GvFvNTDI4VUmGCBFkI9YkCHAQSAQIABgUCU9jr1AAKCRDfmGuqqgUDxLo9D/9p
+qSs0k8RUnJAVonHVp43ltRAwbKJrsunhf40flZPOoGT2KgQmw0vRPLSKVWmo1GTT
+56N0B+9rmUgS+AfC4ZirCcOuEnLe2d1UlLGhicqwcjcf3ifQBFkwtWuZDkwfu8vT
+L4YkKsB7xgq9xV1qx/1eimE1VbOQ6ULE0Lxdq1qSXOP+n/GP3gNxbLtjN5HuYT5Z
+vWzbmPJoKos9+wyWKNtAdtAY1SvjaaFbdt5afGK/obKhEhyaPuzS4ygn12C+VwnH
+WGJ6lHf9eTitTn2cFXN3Ma3kOqRAsF92m6p6q6D1e9B7bHDtLYDZ8u+0kmOWCQtw
+YRV15F+49Ub2VR5Yyv1qGytnMOPQEfbE6oafLY0rM6AyJkBpgdsSY9ibBeugK6IA
+yjPFeE2NT+sGycc0CTsS4W75Jc0SQxnKdtSldktn9RnBpvehk44CpAC0lOmPvLZU
+kCXAQs8RtqKUJlrbCuyRira0rNcHs5eQmVPPCqkXq22AWycHiKcUpC5p2WzvdVUC
+YuQ9F/FcYw27BRWVUqZVhW1rfhl1U212AwoVp6y5xj0GiWGDXgcVDU8TYIMgolzN
+8r7+HEvyDxPR9Ue/536pieZV6OR8vYx5SI/KjQaOyEARUlTVhM6BZBs60JxrRKYp
+2qTYr815ShrkOKUON6hj5DWLajqTyFse8hpITVySnokCHAQTAQIABgUCU0VspgAK
+CRBx7YNxzCnlJYj0D/4qJcv3IOoFHa/jGRL0JlRgfkCB+tW+bNHUU6JJZu0kjpxE
+gUlXB+InNsabmZT0tdVBOqRFSCufXVfAFGa3oeUf8B5Wd9mIfYEKpjHwTWFupV91
+aw5yVNZvEFLt4Mx+PdiiOXojW4+4VQEM6QfkZVDrcbg7Idg6ZC0DOPSc4MsvkMLx
+CYcK8/AGNu/I3hyWzH0IOsUHwO4PJVm/Jk/BlxUD8CoVxEzzl55yvvBk617MQ5B4
+srz9+vT4EvxeeytOvjJwRAD9J8WILhb4LgjZptCpYgZ9a1AS1KHQadr/NK9BZNeA
+W1fkhCOcxyr0uBRunpYr8vVoqGrg84Cf9U9iRuW1xFh0Ka4YHW1369HC9ACNvnvq
+aRHgKasSD8sjcThQdicfumlpD3scAPZYeZ3yzalM8uegGrlLrbzHVtqpV6Fo2Qtb
+lB5C1rGKNiUsAivoyoKPalFbPO224b0zHbCzlpI+MBT+Po7/6oM2Qbs+Sz4qlfN/
+E+sbxKMjK0wsJV1H67X+0uH2JXgOrXC9Kx+7KxS1UJMvH7IfzfDrdxTrXNzoHUCz
+uoothyCr/Pqghy9GVWspCGhu8bbX99guzu5zJCvyYu37go2l2ntBxCUGtk9pN0tl
+t/aLESQlVrsrZc6B1OFm6mhX3a1MUGpJ80faNvagRH3jhSFNtcYSx+ZMjGpya4kB
+HAQQAQgABgUCVJHqiwAKCRDqRFxBB4+ojaSwB/0e5B2rYp8mno8MQ44Y/xhiwxxg
+FVSrOPuwjxDazPzqc695QCRqtJLo/wcufCFx6hPQBw6AV+fwvgjtM+2vkn+Ilhbb
+fZOIe+znFUNSkhrwB5PC2A+PgV4HEj+cj07XTW9GjtQEwXHhDRYwR8Epdh6u5S+3
+g4v892Hc0qVaCdW8hCMEv15ISZ9eliySSkfxU4nF1I1VjK2256s9w1SPkb6YSBW/
+KHGwNZt9SgUNZPOCDb180+jnQy1rNZt1i6pPmI+KVgASRRtm5IYBIha02xR7j7sS
+jACMV2lzA4/g1u+slgT6gtgShgi/X5Sc4vJ8lsKogOFY7mnZ/to6awQfZ3uNiQEc
+BBMBAgAGBQJUkewhAAoJEOpEXEEHj6iNujwH/Rgdrs4E6iTaev2e5u0uWgzXRG5A
+6Nyv9C8Alhne8lz4Kpk2ZmyfoYnKkym+C4D6GFgkuPly+HURbiIA/UuEpPKMHjXf
+ixpbam/5b0AFA4uQeKyPu/Y39sgMsAo+RUUennAKIWhm8LrQmsBAwdCWpwmkmJkl
+MnudIKObD4SlW4ZDM3Zm5kxDPqDl5FTiyUs1Y7yWynCMGMyQw0/F4mnw7ULq/t4R
+9nasG/OZQkaG7Tc4tu0Odmci70ckAe+iBqi/xcc0OAme+hIje08Bf7GBUSwN1O66
+8St9mMluFAf1+/ee/v3Qs4afeBT13jrUL8jEpKX2GKoUpeFt4SK8As+kRmKJAhwE
+EAEIAAYFAlTHYu8ACgkQ7i/vOn2o4olkMxAAgwURq0boEV/3rsuFiMwyF/76p3fK
+Sao8L3vDyUvR7bQpTm/5JHSErWaeL+ndS19W7HL7duh+V9BORf4sOqOhapJuREeB
+TBRed23SkM5qtvEoF08c8DoA+b1O2Cp5pkDk15yMmcMFre1SOKpUYn4jTjYf2/eN
+npyDfbg/oNw0Go2CoNlSbhwmEvexRf5wp1+7Ww1yiA2iXx4Bqyhr/vAWgqR8rH6Q
+CTKmNE2ABKn0RSI8do4Ake0f+Od+tz+EuCZrBDrh60OYSqI+MRiUKOuikjhXntFa
+X3kyPGVtQXUaI9ZtU245YTcZr7QXG+3WsMWjvo0nlHk7F6wT+qS9amg8xNLOhDK8
+895llcZ4Z/ptCcgvMHSSqTNlcPaiUsxSLjBqjsA9KoOgIramHVJ2YEfCBIkPWJdQ
+p4mjXQjPB9sn8k8XFGVghegJkeq1btrkd4iIrPjhpvbMydSIUIHL3/MC760FcwPt
+MX++RIl10N5DEQA9KOLuwgcQ9loaj1CemHKYwr4ZMnohaiePP/itvwL76a0C1Zm/
+XugtiR6yLFr2axDHQdjWjaHqMV1xOndsOddgWgN5AtJEQvwVnj9FsJY0lGk8piSh
+9qnzIAMvMmlqB5pk+YCClJdOec+g2A8DnBBeqkh2WNjju3s7l9mKd7flSeH3UxOg
+OQ7JRTngdK3pou+JAhwEEAEKAAYFAlU1P4IACgkQFT/jmIIcg5Qj8hAAvWHk0ACu
+aCAN0xfxuvm5MqPN7BlnjVbA/wVd8E2+mdjv6HAVWFAsDNG3DAO/Me/Sk1vx2PXF
+8HvOPlcqmnIim7lafOm0+dUHZZ0Dz27nciA3yZ0abC2rCDczpC/Au3dKdq7WNF0P
+JK7fr8/y8WhmEW9eOgOsjnXuyK3FMPaI6bjU1zoHNzQDozlEdj/jTahrd6hLUXVC
+E4tiAFR0GlBXdP0bXOFyiOXZdZWk9TsaQA4Ww3FT7NSL4KhcWm5F4rl0E0hmQZyB
++L1RlZ+wuBaP1bZraHDU97lbHH8MME41ZDK8mmY2gLVcCdw4JfGdF5mi3mOY2Hk/
+CgzlzxuV6xOOnIXmjPzZHX8jtKhXJRQMnqsEgzyN6xPyPmrgdmr27J2lgsBbOWU1
+/IOlkXEDzpNjHNaUHMEwdcHItnoxNqZw3KMAQ8tH6JtS/0/FeLHkan01lg9m7kMZ
+6h96ed6QzYMM34RRlFI8ZLgpLbLG93gfTqiosJvy6Tixg4psVd4PgzIrGYZTkA0c
+1a7T2TCOf1t67H9CRA2RX1zdbUSyUGB1st710tXJheUOYtysjqOXxD4rfNTHxrzQ
+W8QCu1XmK/7eLFEslWj/ahalt8NZMYem/C3ZRkDwVUi7nNlxpMeCAr70P2uwvZBY
+Bhr2rS3lS/dacRqCg8kul/G/KaaumFXQ0k20K1ZpbmNlbnQgQnJlaXRtb3NlciA8
+dmFsb2RpbUBtdWdlbmd1aWxkLmNvbT6JAjgEEwECACIFAlNEOIoCGwMGCwkIBwMC
+BhUIAgkKCwQWAgMBAh4BAheAAAoJEHvRgyDerfoRvewQAKxw7QENIPQOMm+2bIid
+7Dy06DwzXu4n6v96xIhV7D6lghnDtAdpvq11Yo9IAQxsKs1j0FLFPQPQMR/J8MBU
+1vqfM/0RFggKw+w4MnOq7WZ4FjQR3QYf7fXsKP4IvjdCPW46VBuI8pFDBxgyhdUE
+KCoTnj5cXQtQk8dTAT2N/DnQ8AQgTHHIfXlTCkNR/7e+Y08JEUd5SHFMrqkZ5kdp
+WODhys+9cqC2XaaSraFye4Kqm+ZYOWheX/FkDPAfL+q98BnDGia3vJZpgkfpk50P
+44gRlxznrKqtAMj44SRCd1qWOxbK/2NTZxkfFcL0CDwn7287zCJ3KyXf5iLKQO27
+imPJz1fU++DIbMAShTn53i1W6sKk4uz7fWQrgzROqk1e+aojLCEDON2YVz3ErddZ
+oPbs32VA+gw91E9ueDI9+1IAhN3Jjie2wpSgV+rRrw5WFlCfjYhAUbGhPu6E0o0Q
+oIdfVXdzi2fHG9prsG0U2T6PUoyI5VAdf+9v8qJhXkZxypk639WW7nS8MIFwsz83
+CXaXf4/7CdVo2E0OsgUgsgPdFCOmhWKX86zgBEE4ce1ZBNW1Hdwlf1Ln5dW+3WmY
+4WNTkltP6WwYgajG1UtN6OwkhiooArcsSHvqZTbrPRolpQF+P9Xe3lnWEc5OVHvD
+x1OKhGmbECYFMYzy8/BVAngqiEYEEBECAAYFAlNEOMkACgkQxCa+aHUWYdQojACg
+4JlFLT1PVZSZyyZ2RfAUngDz9q4AoJ+unl+U4apEdsTyCucl4m3AYAx2iEYEEBEC
+AAYFAlOgXm8ACgkQ/TXUs5uJxp/+pgCfSiSBJ+bbp1BpLF0VPqHr4506Ms4AnApn
+5m5DzliBkdrZP2Z4BbwcoVwjiEYEExEIAAYFAlNFnI8ACgkQioOL5NhIDy5xZgCg
+lpmOUutA5rtG8XPwU70j4DAI96UAoPMe23rvqA/Qf+yDyTtZ8+OzVkC5iQEcBBAB
+AgAGBQJTR+61AAoJENjTIPvvLEnwIFUH/0bO6uIzvVjojPTYiDFd8ASGMxzqCZCO
+i8kpQ6El1bkJVerC3LyLm7OtDnrB27IoXHNbRVye+9lgp8+3NukWiP4gXzDJ/hcm
+VADy6QB3REz6lK/SV5in7KPvWDcW1cVopQjyqP4+0SebnOswxMvNpmd9uwlOWVTv
+nOtRFDe67m0SxnS0/39iZi2CeBf7bgBVHff5Dw27ZH8EvvHgmwGpEhEy6DlUeB8r
+Y8MubRYl6xcWWCdEnKtcU/cihD6TIA+c/G592v/8Wb58hSQBqKaqk5ZL29lZssV7
+0790wlzRNZZ+bT0Q1LG3ofln4pbuEWTp2x/oSADjURWube9yl72QlWeJARwEEAEI
+AAYFAlQRYoQACgkQcYwHAQABIoIHSggAhf0PX4M4yAGIuYeCrQIEfb+zCNyME0ac
+GfBV9p2PXoTJLTtmtjJrzDswrXn8FhzgyEBE3T4YEfCeBybOdh8CBVQ0Nmo/Tq4/
+Fe4f9CliDojrNqUkp+l3CI0GVsOzDfKW7yrTAgPb25poie2NurjBZBLj+P+KgS0Q
+Rhj2Me6C8wkLUa/cFi7P5DhD0GTZDKt+X77s744kkXEAZ0NvfQE/WNXD6JWLPCcy
+okDD+NY/I31DfUa9m8iTLkzLcnFnmnTe6t7x8hnxlI0HlT+yz5T9UkA3718iSOpG
+zbP6K7JRxx8ZifgNN454MlVuKNf8YgC/zIE4fZXn79iwraflZXIglokBHAQQAQgA
+BgUCVBFmwAAKCRBxjAcBAAEigi/uB/9KYZoaPzXAznUT9l0irwZWL0rU1IIkC8d6
+moA0NEHJ9fWLHz1sdgBy+lWzLgNA+NS9pEdW1ZPMZWclvPBKalrxu5Gi2rvRw8nS
+QXXvNL4gwMDr6zMNSt9U7gR0PYVizG3pDnBIyjGTKc3WxSCcwZNXtf5z/ca7LEC1
+2rmFpWILN9IR0gVf3SJOECehBdmzVxCWZNt2xl/n7jzYpoAxbNyQSx4klHXStgwm
+025t2241/yNk/7Y4j4OEvtFDWzwNjHDlVxLwN+BT9bO70ikicYvdlYxIi4JRLj2e
+/LRgvNGiJRbQ0jT8UMVyZ2DNmvZgrXAbt4yt2DKv8s9c84byMuEpiQEcBBIBAgAG
+BQJTRXAaAAoJELKUvAbHVSp6cCQH/02OBMbjJ2n5w9UCF+/6TC0/TM1joeQfTnDA
+v1zDWRSxr3ybL99Bh4OR9ggawjOLkWiQXA3qlF/JPbM8aXBRo6OL0ndXlTjX4Cy+
+ji0c0WhlWD7bdi84CeZGMjPPidAyNx058HjXnWjPUflHWnJmd9wu2kKp3UW6a5XQ
+E/hV1+z47FlTkM3e1I7lIN8eGK61L4cgY36yuIcIp1TOPdWpknjVtaTkWlFmQKhn
+FOQsUtj4penpf821ndoTuxKYCkJ1PeN0qwFQq/aUekCAxlguhfMpNtgylw5/jYsV
+OXJaGVjRVSd14p7WXfZG2svZU+u959byQGMpe5++JK5oQAjVv+2JAhwEEAECAAYF
+AlNFlfgACgkQskE8Zt0sP+o8Iw//bdHc9vc6fxbSe1p2qMqfgACPGnYvCP+F00zR
+JVMvfyuIvPZmm+RiAMufMLZQXfPz0A6lhpjUHULiny62yrmlTB/12cc8Am2v2Lr/
+70MT+9rk8RxulQ6qLt+0dJgtitzi7GmI1YVyNjrfDCzuqgC7Cu4NSfVhRSxtq7nS
+y3kokDARgUp8azY5/XPlWLcqQS+ca5IQGH/L/i8+JMtKpEO+ZMEX9QmF5INurJDl
+6dRA457+z997QQqDV4wdjxZsCUmX6lMR+hIg0a+Ev18HU6ACvOBbqg+wAxmx1Gpd
+tRqzLnyjkytvcxm+c9dlHKFxZTwHZIeDdj9AIQtOTy8njWgdabiKLeU4Wb9yRsNQ
+uyQO9v3kiWAF5z3rdz6D/TEgwmLjZHxmVSWKwekqvb8TOA9jHnf0ZW70nAOnePxI
+/uHusdHKVnvxdWaqsY33ump8sKbzpT2QwbhRw5c2NRl1cSorW7HwC8SzIZOlH4G2
+IJdN0Qfw2JQdJSUA6yxOziFIxC02WzW8Hq8b7eUBmteta0FMeiTsFGQrlmzDJ6N3
+K0x8Mt7zYbMFmUlzhUmogV8FbNwEjwbFrPqmqVKk38fWZEOIMwOHYP/YEGgCqPsx
+UFxTJdn9qQu5wwaObKw3b9QEccl0wt1ONfZkA8fDD38oigR71GvzGwYh9G8JgIk4
+qmz8x+WJAhwEEAECAAYFAlNFnUUACgkQYrnc/2YkwaLF7xAA46nKLBTp8rKs+Cy/
+l8XF6Q8NFljR3wrrhArvuAsrbeqJeaCMW6ZPK0gYJeWz6Rx8oBeomt0W0PD1E81u
+vkVcHnuWpy0YMDqfJpCLssQfZkxkMOKOGBL9vQjvUdZzgQtAKIsEaSKPjSCoTnEU
+Ux1cVRl14eNcQJZ9hup02r3pmJsx/8eYZ9fO6UGitAdn+2QcZLRY44CnwfB7IrFZ
+SgrnvGq2hlKlioeFviOTjEbkUXeXpqRDpt8Ns1ZBXW1fZgOaRVhXUEw7atq7Ut7S
+3gDFBYtwPGLkrGpwoYABTQuXfjosetzw6lDdwdgAHDPIDMHVPAJ5v/ewLhcvjy7L
+06JUmWgQCPOPKK/JegKNHhwCvf05jel2RcmCIuoymhNoXBW3HhA6r8aCi4PxKhHF
+0f9/ZcoFdxinMfaWlY8vtz5Pp98RNB5QWKUbXy0R58eXkEdRYHe9mSRtNPNOO+tt
+VnImfC09wWdZmOUe4NsVIb5yIkM9ogI4DZmStG+UhiI0JSBI9y+Sg0UE68yeJdCh
+e6jU8/7yV/2ogufan9Gk8au28MA0FRX778cSQK2/pSRbd/WylE3/TNiDLwvhcFoa
+UW6Y0Qcdjl4Ln7KXfBWN4/CarUVr08Kk/CF0lXKN49Q3poNPRb2O9Xy7J7trTo3R
+zShrCzqoSMbCaHcqovwWcgAuCueJAhwEEAECAAYFAlOgK/EACgkQLXfFAsbszrpL
+Kg//YASpq3xn3+5ub1gIJV+dXFltl2AYQx08VrdwmWwyhCMaL/wNPaWhyO1vkgP0
+dfQXv2yJsC52Qo58CK1hyiVvYwC3UIvNl0lbewSsW1GvhT/t38275gGctp8/J8Xo
+uSmwSsnkcocc8//l3CGnYAhwHNXMnniAOvV+ZV9hveCnWAku15rKa6XcMSllIU9g
+DhtGheUJo8yOWZKduhhEBOTkvFFG17dt1oaTMfl4gFZDiXpfvSv4v0LeW58XLave
+UcNef/BIQh2ytRM0U/8BLEM07sOywmBW+n9bVf5BT5KN7XY+tCvzKwyeV+bbVPAz
+ivW16no5obTPN3DTgNwwSbNTp2z684W4dkGWnDN+knt7RTccvTDz6nWDlJg9kn/e
+IVMj4+JmAoJhjndD7eoQ1WGaa2NxX8VP5jJVgz1iDKPbmqxmEYaPYGI0qD4w2cFv
+ljPoEh/GN0s1jcxgTeYAHp3WZik/vE0nODlJ6LrPHRE/kRQwFPHh8I2Zd1u0Z5vI
+d9Kpm5ewsO5rFHJjaJNgCJPjcinPpijQ2kfUawjiVKqTfT0DqGEyxgdsZoSuXGNH
+cNnDrNUaFrVnj2D1eAGCrSF+NA+m3DY6qo6VMo2XNKjiMbu98rc1tNvlYd93UQk9
+FWUPwWrONaSlHjKyPWDy92o53Wh88gi5vwcC+J55BZiBbMeJAhwEEAECAAYFAlOg
+YIwACgkQC+njFXoeLGR8Qw/+PS5ZdfBU/O3LFFjVbuDhErbju522Is377Pi9Ociu
+rOjEz5U4AGPNvZiZC3w6RsHiNpPIPFfH/YSh/kW111wpXJ9FpanCJAPnSIa3W//G
+k7tZ/5Y2133bBi7abYdInoUwJXAnWkzqsj7VNrwLJgXAtSIo9V4GtuM99ifOpgoD
++9VNePWLqp4RZCu1nsbENEswCZfzooOeULaW6VGvk8HxAOslTGGZIo6tQC9Y7ykK
+UJ4nWQICzyaEo2h8GmoDlSVfNupX5j3a9KAb4VX6EMDq7WvYeGQoGPEzch2sJZYE
+Ml7R/kO92gAPrrmYjLJD91BR8UuF+1mXihGCqvZc0OcqZ6NgOCwWT0SwGO8hHZI/
+ONEjChptqGCpypGE7OsoLCUuhBsWef8e3uxbqZaHzzO2Ups7/RPmqpQ8/NkVSkIb
+nZKzFEXXLVVgcjbtVwHfK73dh6sFwScfP3N/L/lAqRjV8cQLerZ6k6Rxrd/CPF9z
+BzzU+pQhl4dqBAQMbkfJbmcglPXTzXqdB9WfV6tZ/fqq3nUs4ekP2/T/0gRal4lL
+T77i4kr7+jY4siQkSFqPs+r9VkCyS8uDvYwMgpppBR8rOCF8NNOAeoWvehLgFEJz
+l5ceAnspgOqkC5ykA0S49Ah8wZTPaNWyeC/9j2wBvdHE2B9JGZgdMVORGnerz7PV
+p2SJAhwEEgECAAYFAlPY69QACgkQ35hrqqoFA8SYvQ//WO4SRnA1/cBM7jWKzHbu
+i6zjUjNQXFCjjLlTzFi3EwOXkMEYGk+tPRRWrkpXxfzPR23VgDAK1x+rbkkzYiGV
+8EalB7WZauOmkc9TPiW5ZxfG6S6BVJ1K18U8w84yhOCvHA/D/BEkUL2Em7oVS1nc
+IXL/7u1+hHHkVneMlfJItNxZ/5/p7i/aR73TzZkedZdRf4CZcZ2kbXnhTuDLsbCm
++lb+eZKIgg4jeATEaTmgj+5v7oNmzCEfP32zQuBbC4LuqeHKM5gWR/N0HXew9Xv1
+rDbR1EPF+58XgPzFDrVj6QSLL6xzSfo+EBi0nqFDo4hvC6+4m1x4mw2LsvMJF0aG
+Zb1qynZRhgpZ6m2ipG5O/3+c0DrtqJjvENTUZcL39kqmjPbLmgcHbNwAcAKglrgp
+r8dQ4H3onLpPI/Fl9JUxqpquRiw6s94psRujF/pWpyF5EJR9Awuzbjbrxz+ngruR
+Zi7zXRTOadE2Rch9M8NUu+HLRG+HTNg4UN076t+2uNT/Y5/oyCGc31pjPzcTpFkd
+jEljx45JEiDlvf5rAfWydq0Bqop7R1rxNTTpn2hN42sDuCxtIalXAIN1o/iCG4de
+FB/3P0Uufw/1eBxlemY2lbP6XWwPCzGwgkh6utj5X0bkikQ0Ly9/zCFdrjdvzfX6
+u436P67gp5DoUFviPadma+2JAhwEEwECAAYFAlNFbKYACgkQce2Dccwp5SVxIw//
+WR0MqAmRQRCImluw4IHKTQwGF/DuB+BluFhe+vqKAP8pzmUhjFFdtv2Dx4fxe+Lq
+UKzK6HeIQTfG4L3pJfmumAAy3IRpgjq+fihqHr/zsp/MI9mWawcNfYRip73lRDrb
+Ip0x5HwrzVUMWtQBNn3vn6X2hKhnK9BpwTxfkx4RsNXKD2XdjiDg73tuNPy+gY8H
+xy0ZhByxDZWBWls+vosDlzzj8RLwwX+v8y4cSwdrjUpNlyImhkInJTvc4eAuksNL
+2M8H380gzRrHlHwYuNBMdq9m9YnYDhTcFk5/vHvP0IfhNRbo3PIRWAmqsbcIT9IC
+8tV9DTBIjsqc4LkvWt8Y6/W6DvjgXeC+PUXfbVxPNB7VC+WrZxFD16AJ7tFFlgCh
+ya7WP03ryUPr0/zuLcfawOYDyK6IjORoKCbXjz/hr8kovf1Y73dqkzZrvlOValCu
+LjSsm+x16/i/UQb5HvokzTu1RG66zb0wLBUwKfNJw4uNvv+YvACU6/E3GsYNXsId
+wZKEIlPI2V7KNfdtfU7Hrwte5F4Zhl7cIkmq/ifM35sNw3cgFLfy5OsJReo+LOef
+5vp8KkszSlXzbuXoEdWblGNd7HNIMAH/OVsH1wF+wKGh+MP2Lm00U+lKik3F+2AW
+YyiOIGfFoY/jTXHVudpAJvyD8Yku14vQRV6KCAaQ6EKJAhwEEwEIAAYFAlNFnMsA
+CgkQu/aPA+jzeUHlAA/7BGpPDutu3Qr7QrhVk0cvlY/hOTJcEV/AjEoHAUJRHy+V
+bvDPvteenJzX43D72icsS7v4zH7pPP4oQARSPcKVwu3pyqAevzXtlaIisoqUxgaq
+pt6GHASt0ip3Dk97P07n75TgcInGyVMKUPZ6x+eQY4sP3pjZpI+3NyNzrT/StUQN
+wPHTFpeYAHXTZhFzUuJ0Cos9tgz2l2OxJwvYGSweezl1Pab2+lgtgB1ojNXP7uPi
+HBBtmMuMDG9tnuK76WDxQ3gXpBUPRLClIFqczoTa+RN5so58MdImXJO5aaov3JIv
++73Q2bnCmmo2KYu+e+YYWYCwCZwiPKcThAMAAq2eCRlxN1Pj5wzJ4L3MZkiIQ1q1
+D84WqDdYyDJN5oDONoKrudwxoyc7UunGTuWsll8LHg/L0WG6B9Y5s4Scvnkq8EOG
+ek+sTdGE9k78jqtJaMigipsOiLnfzp65ACnMJ8+urNzwTtiOLejYOtAxb5Kes+bF
+D2MBJyK6ylY6Ga+CUe6IDR6kRMVFrHfsG15lGuX9PLskaJKuvrob5qlJeuesXNca
+c0D6407zUQvWuTgSD/xH018bfUDh7IefTk8NugHWV/60fn+SYrKl4b/CWLioUOxV
+NDB6Ju08xyyUGrmdqAO+NAkNYBJgHmAfBpjd4bD/jGYbenJCq5uRmXF2Bve/DoOJ
+AhwEEwEIAAYFAlOgFYQACgkQ4tzdkTJmm9baehAAn5mIFcjvgfuvJnYACAKMcZcT
+R4Pmd5vWGUwwFKkFx+5dI2IaGOHXt5yPG/inYpFwkq/FBvp+/gwLX4gim7XnOe6u
+TurnrJ7I5oI4rudR5HUo1z9qYdEJTwwCgZtvNXAXt26lpQC3vBA7wsQ0JIPZpb0B
+N6tA8pOFQntAo+zWkQlD5oaSMulA9x2LIEoZi9MzKxPD3GYXfy83QJR8Al3jhMCD
+nZR83F9HxnlmMelDEMI2FrMKR1zepE5HmC0SgI64lZOEJeOeYgSNizmEz1IXy0hb
+3wW0UkjYmRY/K6cM1XY1gPQ7f4KFuKdJSqEOPgtYnwtlW+w5sqwufSuCwbvVp8dv
+AbQEJmrM3aaBabOavp2583dqKlh4JeIhtEz3zNgx4bEpc/wQ9A1cn9lOesoF178L
+YPxhpGvJtXeIwd4jwzmx+2XCCQtmqey0Vz0wl56gfG93iIv22BxkQgLvm4o7lHbF
+NLT4L9HO1Oxkuhhnf/xcfSjtp4ccVTVORgmlQFULrXR0eIP2ib+NbMdwSXnMKY+E
+8B6njEb8kzd07r6SnnTwMwSlDYMZleWBs5oew2EHQqgIrdi7XXPWb/FTDBhNg+tg
+RLetcg1yXRSye/yKxw6WtKEfC5veOb/YkTPEZFkBbbhYxRWRTnaxZV9/fSymQxUx
+D/wgmA/bHzOCTrZfhM6JARwEEAEIAAYFAlSR6osACgkQ6kRcQQePqI2KTQf/WSfT
+OyocO02mG03zhYwQdBL3pEXdZCxSTyEtELFk7rI9DJVdNgiGDNqxF/jV3qpOeBsm
+eY1f+JevQQPdgKC5oLShjogw8icweosM4DRIcyOzpQ4pMT215ydUBfTSHb9hQB9v
+Zd8MFmX16HmOA7OnrIo5mfy3gRl+jQYcTApuplrT+0pg11ks5m0Z+9P3hB1KUDQE
+gYVNrHoVEhNPGk9EJc+ZZ0bAp5mTby0hRM71pyJPrDi+GPjMJ6hPiC4j0v7cJSFV
+aCIxDzpGnz2jTN9XqsD9E1XgsUlyqPg8y5cUpwF+sYSmK2wypZ1OjtNKq20HiBWG
+SglcrKvt+0Xzwf5XH4kBHAQTAQIABgUCVJHsIQAKCRDqRFxBB4+ojXMwB/9ja8CO
+BfcdXu+Zq0Hodf1K9EUOvmwYduSEqysjj6X3IY8RVtmjTjEhjdJE8BBXG5d1wCKD
+K1GExggEHkop0HgcC1lCgO+/BqYwRQSxkEQqrA6cdWtlFDjhwSzuVYpcJ3ih/ZP9
+rYUJ2gIGRZ2laKPoOgvkhpQei5JdAIA9hTGKVYayobjJWhJvRMGiVf5SPCUAO7OQ
+58JmyuVYMKDgWgrU+CUzE+rmHBeaBFLrFc8KapgYiCC0PWyHaDKTk8WW94UQhiIF
+Q5v/+Ju1Py98w7rTNYFn8b6eUHwfGtAaGhcwYvLqfR5huH64SiGEh5nZACyngZlz
+p2eSGo4LPrLVoGwriQIcBBABCAAGBQJUx2LvAAoJEO4v7zp9qOKJFy0QAJ764UWF
+A2It03PPMZ2aefBuNm0OlHJJ2SxtzcICRTH07ph1Io1+fFDd5pX5AVoOTzEOiNIU
+gncSmhDVN2gV+J/7m32NEBED7BxZj2wm3TImce+Zon0/nd1zeHSUVAYzWUTGTZUC
+vXuGzU7CVNlEj3sszAF/bJbmbpCYHdJ8iljuK6MVGzA+mq8yWgP8wVRE7Ca2c57m
+0p3A6xqisiyRfXCFzF6b5na2ztM0/qeTsW7+khTgZ8U7AkCCyZ6ymbgm8I4IU4bN
+fcIsEU8387xrMJ/yLYKxNRVGicSbvRfaQhGFBNiGzlK3My4P1L+tUSJA9ukFN1pz
+wVzLaSbd/4Y5umlkeKqgzpW2rkxXNFcJx4x1Ln5r9jmWTn4ucTdE7qNc/46Y4EoA
+2BqWw0jOilJzUSNcHISenO1lFoadpZuJ/EyDuC4XKymsDhJkvW5Qj3q1sWWi3Gih
+TyV5loo9QkDXNXH1gNFUYl8R1aeM1lgnon+aUdGz7ZGWXM5TGGu7psra62/cbHfa
+ZbCb2G07VPkVnsaq4LktxUJOXvu3/r2/k8/YfRSakZDF2FM6jSpIYKOxjOO72uHx
+KIC/3WMswmj2/z4QG8bFa1x0QDGCZGmedabF6BsooBiu3oTBMDpgKhapfLnfki8l
+EnzM1enG+8omMyk89GrbO8IzZtBFjhkDXjVViQIcBBABCgAGBQJVNT+CAAoJEBU/
+45iCHIOU7IIP/jho5CtW10128ttVs+Jpm2aOFScXBegKtRNitwFClOt+14U6UKZ6
+vb6MxQlc4OOM+mbMiqiU4WBWrlLcDOFHg3YSZBsraRau3oMj0tx306Lawj9kxa+b
+mVxSfz5kyYmkNcEquUYJk2LQESunJskaTID1dQmdvJFDNl83ZfI+WIMVo2JLH3TL
+n0QLnDRiBqeDHDIc7SOrB7SV4+L0IcMOX+qJ8o97H8GCgqHvg/777hk9payELggM
+JY0pkaVJaFfz3SLq14glVzwHvDXzGGHdWHkX9yz36pH9XB3BAWYws/yIGNnIE0Zb
+ilY1SQHnZFqnxtg13XJvbBMr01x+nXGVLNANaRfT119UFh3hn9n+/FPG11ClpIhf
+U4oCi74C5fYBdWDSVBSnVelAt/UFpbWgMr8rWetZwDdlO1cGGcv5d/fr5SWtZwUI
+30zKzY1cj4XLe6a2m0dTfGbQcOvodCNIQADn1Z2yT6JxbFWbUpN7Lj3ARhM/cp0C
+a/m1L/BEDA8iLnQGVtUVZjC+NwwIMGgWPHsI9IvSMxssuu0AOQf4FZs3Ia2J15lE
++YuC2kzVxNNAooCZPfhSrPpZ+/4l4GCmuw5x1/NDxqikrfNLxfE+6+JLet5y7WpB
+uxaehzkzZUe2r8wlA06irKFDL23IQCzVUgeA+ol7LN3zYC0Fz1IkbinktC5WaW5j
+ZW50IEJyZWl0bW9zZXIgKGdtYWlsKSA8dmFsb2RpbUBnbWFpbC5jb20+iQI4BBMB
+AgAiBQJTRDipAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRB70YMg3q36
+ER37D/wL7wLb/p4v86Kcmz38WdsNKFVCTED6v3jZBp2PHfGWvp0QeVDv1RsbALUA
+cUTntOozhfEvyzF581/tJHJOrY3epHLsvendpOEKtpQMc5OfCyFF5TScfIpNqLev
+LPzFY3Y7I5g+qWzQtxgC5peL2Bv23YWG828moVccWKqUf6+aU4UZ+Gm1s8+bZI24
+knVCGOQ+6V16oysSeafOzeuL9H4qn2XOXSGaiiI6r0Hr2TZv4OiKNfiwqwFK/j1L
+uvLNr+jRyKs1YE7Tprke0dC+aNMtuAiqnujztyICvNzZD5mAMDVtbl5P8zCi1bfv
+48QAtj33ovHuzc51AOsB/+zAn1HiFDunc25imH/RV9pa9sP6Tk+0taHfWEEZcmfd
++EcxOoyJcECeEzcBrmNTGUz6UozR7VWfVdghP5OgZgWRZl+YhT20OZoi/ufdJeoZ
+Op25mU7ynhS2di57cjfvnBrsx7wm+k2xyxe1QxLz4mWs4lLH2MjkKrsdrQPT6/1a
+ds/iqvuuVBprgIa6RhHrowyYsVBhXHsta1WMvfbUcQAOLFnlQ3MVH0wSdzN2w5Of
+HKT/5dZxJOSYJNht0bLSMwoO3a/FO+Nj7EvKvixxWD3fBuEnD4mDJr0RqLIMGaL2
+1nBcNnpxgvbHx9qlZquPmn1+tB+a1c6sRGYXMeCivSBEXOdOjYhGBBARAgAGBQJT
+RDjJAAoJEMQmvmh1FmHUuvUAoKB2i3CJJZhoyw9E7u0yTGwq24fGAKC2dLVNC+Tv
+5MEw33kvRTD+Zxb7lohGBBARAgAGBQJToF5vAAoJEP011LObicafA2QAnA+iUQTj
+M7siyEayLqaS296ilcuiAJ49P2yp5BPyhtJuuBlG/UsV5U8bVohGBBMRCAAGBQJT
+RZyPAAoJEIqDi+TYSA8u3VAAn3L2GbkGVHutwkNbwZYZYLeCvFXZAKDp1NDEs0gA
+eQOD5zswAI1CwSOofokBHAQQAQIABgUCU0futQAKCRDY0yD77yxJ8P5TB/95pqKn
+VKUu/cIpdPSbEHG+nqHb2cvCYdZMcDJudiXmN3Kn+88r8LRIo29O9JTQgjktk5Pu
+v4qzEEMvPBBIcOeF4mZdP7VQcfCYAp0N6D5NxCYFr2A9wdRAC967oepSO1v1VcBY
+Knbp5lFaHYEs8a5/8Cw/oJM3l0nPCvmNeU1Fp4U4j/RvL/sHhEDpSpejby+Q4i5l
+8aiU5+N0swoyrXjmHWLQpQpPNfyn65exjtQbMfBqdK4DTRbGDoThUEZSSv3LGmWd
+37CrKbAbqEwVEim8m2vgCI0mkjBQV3qf4xXK4QIIJCtz7iKrQzJbMXt4pES7NXik
+0z+Yj+6xETHlTz3giQEcBBABCAAGBQJUEWKEAAoJEHGMBwEAASKCUJUH/jzRvqez
+m49Kz0+FD5uuMDb2KkQdfgmU/uNO75lx5y2a2OQTtHymcf/7WWd4cQ3JKHvUmxqD
+De7afwQMZxaF6+9Cwqv/bQm5b6xewjdGnsV410YHvhUjnDuzklNEnc3IpYtVj81q
+Nyo0DwDaJed590NRHdJJ56V79SUzkB80VxDA6x4UT1IqouHD1NT1QjBzz3MTg5MH
+NnhDP/ytUncApYuMVKiIMB4N7AdPVLmMm7N/5Qxa1ts4V+yUP3tM3HgmirU3NaPq
+og/MOX/WtUTAj4hp4+5cpRhfdd0jeGVKb8pm/3qGpNba8lKbAhPOtVB+2IIo8NUf
+eZF/w6IYD3+fW0uJARwEEgECAAYFAlNFcBoACgkQspS8BsdVKnqLqQf+PUpFrbIa
+Wji/o98jn7kxfDsYCcgmyLCjvrVqWqNGiwdJWC4rvXPLIRZIPIoPV7g3tf4kWvoM
+T+Yk6UA6S6uuZstJYU/FQt+UEwEdB/07NIr2086iGMom/XITpoX8qBcdx8lWk5Yl
+Mlp6rEg6nRD5itKCp42TgCK3zDAfG7te1LM5dvwfZ9QWB5Ig8seF8EzNjnyy6CLd
+aw7NjQkwy9Ep9mll8xlXd6T8Oz48DLXs7Sj9wk18GNxztFcBHfRZFCJRv6k/LQVU
+t4ifIU8MlNhBNcV90ETu5CxmWtV3bLYFsQ1qOvflYk6xwC3hKwNgSQF4I2vk5ehW
+x+hzBqgBNlAfj4kCHAQQAQIABgUCU0WV+AAKCRCyQTxm3Sw/6n6fEACE9EL3W5su
+HyDnO6f5kWiRwsCBWiOvrEuFZUroY/9BJeMzykmh7WzfE2co+HcgYCYP9qmgiCgh
+72f9WN/XMid+h5liA2oc3mE67tKj0P8snWrT99FE2ohlxuIT23MXFAghqzdVNC82
+rXCKRu1V6gHjAM1diHgJ7At0SIq9PdtokenaZBjSGJoN4P/PCa0QnKEH/tRKd/Xw
+SdUTz034WgkMebo3pcRmZi6NqztQtDyu0e08irNqZkmYrbMafISTlX/+17tugGeX
+9j0/kYSFJs59BzA9xtoNY8h5u47purS2SNiD3JcPCHXHmIWpr8Wtb4iQjVjG21k3
+PtWbzjAtflx/xaKFekRqMuWi8hnsdzWtANXj128CIY+glnH9bmjgIHlkM6UBB6Pg
+T4UQlYkLrxdtlv53QmG+JnC107yXdUemBqVXlt/x5NH+9tQq2BYP7dn239qJMVmm
+5vZWmTWNp4VbGkPtWnbg05dSXX1SLhSfSBX0yk+i/LuXrVRJr9ZJSAwGiAGxako0
+xCWhb3JodBpvx7/AjE130Ss2V5DOs92h6/9CpX44hMpos43/Lq0cpsu8egAcxOCe
+xCq+z+AFu1G/oYSeSPc/NDAiLLq8OjtKX3uPUVGHjM9agdKmnjltq+tb35KSQVPk
+l7325R0Q/1TXCk2KGxyOpSrg+LS8pspGBIkCHAQQAQIABgUCU0WdRQAKCRBiudz/
+ZiTBor6qEADKRzq9EzhT/5ISQIvgZ9ly4L4rmF3YVSHLOH15fxfilaBpIowKVLJx
+42jEam6dY0PPFZAuwG5Wna5yWv6P0cHiISCexQ06Xq6RhyWNfoklwLK7S8A6QsOg
+8D40zSOXzn3sSxyM6fH+y7pspAEFl1QkKj5v0WVbavglMvwtYTWe6UKlcg1WJeNg
+NqYiFt0Juqs7ZzRRTwVom8OmyqKJXUW9BZpmcMnZdHAbZOq/5kDawe42UZKfh3Ur
+XNgSWr2sjaplm0CRosYVWBykA/SCn3anNw7wlByXF7C4m1ZGFDboOnWb+yjzDEhT
+byx37PAn9MucWVNkhidyCksz6POjy/6ercqwZph10Wo9/U0kI9+yuKas55Zn+Fze
+GYipjd6XghIg7EBCKvYGf4Qg/2HYQ7fEluNrpRAijJ8tM6nUGcWTJI+5d/gt/ulX
+EERjIf+9FQ9HcXDFZRr/PcHeYCtd2mA2c2F5nEc0H59xEuxIONyOMN3A2T508b39
+94q4ztyJ3S0PxFsMbrpEjb+E2gioUmLGH3iPqt6ijZpSY2gYz0lGjR05lzCU0W2Y
+Nj4VOKh6s2PI5QaRj1LavQhr+QVbuCx8ma48FFXspV7XezSl7US0YRd4mnwKW2uU
+8OZ/3AczUHJIHbtomIPTde45GQlNLB4VecbVnK2q18JDxXByKL/IO4kCHAQQAQIA
+BgUCU6Ar8QAKCRAtd8UCxuzOumf4D/wNjAJYsaHi8sOtZcohGKAROsfJIyl4frzp
+RTZPqyf22pGq5C685FgLikyZsqtmxW965jPs/mUAuVn6NOxwFM6wRdTc47zirK3o
+13lAHHtkTGz2j8epLTZrSm9/ruYRFJbyk7E3kY9wXLPuEdvlKLChnnBIan/itorv
++uLWB3rKmmCxbEadhBuqhZBz6ovCkce3I/+gCC1gru5ceSQVok5BtEGS0fpZI/yr
+LbK5IxcN5sEpc+RHW2nR0MZ9GKNxnk9TXrPaGX+GUPgWnSCi+7aCt6m77fJqs/8X
+9f/tbk+IpRgbCHMUuuvBVhQiWndkI1zADzgoXkAYSKI/bb+t7wrb2P3hRJgIGKJQ
+aiOJ0PjjRDQ8Z6zLRqZvAVz5zGH48ZcxufQIOdzTdOCt/AQnfeoXu1tfhoie3hwe
+RHbaHYRkLcEY84Bs7xv9jRtFmIGveupxZX3vd7C07k6PGYToyNZ/PIFfb3bAkx3l
+JkpJuDUzhxmB3ddXeeIDNtgzb0GQ5quwPkgMxTIB3aGe7yqSTglFD+fVsvwszf60
+O2gbIxdQNPFx3JO1uCUQtChTzlGm1gMlz0IJMuH7Y36WecbowOtN5udfK4S6Wuyo
+Ik91xbrvOydYLEYKIr1uAB363nT6l/eHtcFiqaQDMydz4jp0cG7mvfJIMJGCrU9E
+H2VoRt5qoIkCHAQQAQIABgUCU6BgjAAKCRAL6eMVeh4sZKOsD/95D4BFT6PgdgeH
+T5sP98s9fQq0UHeCdFT089Bj6rl4t6QxB5M3qxrVABfN/aqEKen1ArBtieLd6CKP
+x7GWFIztvVd1S7+bqqMxv0vWd+BmOGsqfWxlBRlJZijTBLnSARxpq7xabvaT/hAn
+mFk7cYAS7O7iEg0JAMP/B0Xxh8dx3P+NL1nCccKS2rtrJmhxKsrhIMqSHCA5hyTN
+Ze8yVxe+4uGc0+WQiwYZfRQAwjHQpjt4kx4eEHFOYmLhu4xgq1r4ECf+X+TNfqQd
+vxssCigiaLhZLhIq3DZNV7sPrlLBoSENowHVXlkWBYOdvQQt2pcgUswlh/ydsvaV
+OedMr97licYJSNPcu6nZA+rAbT63BGf4cFk7HksF4Clz4hPcmHC7cLE36U/nS2W/
+sb1nfRAH2Qzc/9iPHvoq+rnjohcQKTN8kjw/xkizveEP9DCCk1ZyegnuiU9k96sM
+6ACPbG5KPAt3eSBI+BdGt16q1I/zWhfFAVlv3AkyQqDBEERbyiS+bGWki4BerCiv
+QzT98Hz/NpvUlEJxON3VoqeUuYF3MqFEy9HxP9d3jwhn07pQIyKI/e3FzJEv1o2E
+2UHD2YDIneoDUz5iwXG5ceMrbmeoW4qqDuSAH4SI9BmD75RAEM7IeH019QvoSZnd
+rVAillaMQwarPByGk/fqc/kDyuhkyIkCHAQSAQIABgUCU9jr1AAKCRDfmGuqqgUD
+xKSfD/41AYvo3sURjdGerv7pHF0ZLW+NBtcCyy+o8NXvdH4jgOBRauDZJmzFFZNs
+/EQ59g15gVU9uSwrpeBJ+DK23FN+cXUr43VHBRLOyBK1L5Ga1VeVHlbEOrieIP77
+BO4PwEDVW/7gFTT73aDPAJHmtAFmWL91dWAm0pvxTty//1AFUPMcFmJcc61Dopwo
+OFslxRy9OhVspobfknlLOCV1+60z9hJKDvgSKKQNAAAekMqf+BWsB4Nda8zG7POt
+2uVDwGWH/IAV61wlpl8rfXfebow7C+1/YPs0zamXmzTdKW1vfC8qucLR49SJbSQA
+0nBa4NiOBOTdLj7SObANl3RX2Mru8v8PDgTJaC3pIxs8x9W4buS9ogLch844lMOl
+89BcM5aLliDRIiYvLhTHYmL8d4glDTgDtMwTQR7AIq0DD2riACtDi9fAprDKYZ00
+Y17JmlLbsRNVCP+qpkr7Q0sb2R8hFS1BA3/vZnPz4kzVNw1APW/QeyWgF/yF5p5U
+DoNghYV95mWhnUt1pLHmMKZOQv335UUHpKDJtpeW9aV2/Y4NUgfOUszpQaFDSLjm
+05sbQlzJ7z0m1eFbiSRID5yo3L/eqJYo89v5orLnDTkX7pGtqxBNr+mJtfg2x6FJ
+9Hprn6QgoEIsWEonisRGFHoxURMyakPxmz2/BVh/Uj7kLC/MbokCHAQTAQIABgUC
+U0VspgAKCRBx7YNxzCnlJWYVD/9oqTeutlGhiHAIYLePF4u24fYiG0MAmzuKihU2
+c8K56gfRwrEipyNKUYH+svT7OM7ISzdh2IGrrfIvxs9h/NlL7kL31AYI8k9YupNO
+Kwvr5nxEO/EY4GYGydJrqCgXfndXz3JQUSWWu9mOUS2UH6Am4MRpEQp7c3ByVJh8
+kRLCRyfw2ghcX1vxpyk2t7jpnFquoXmy+PvZK2ccWUK1Lkzk/2FX3LaYzvg8GOtJ
+CrDo8b9PIDRuIClIDgHJ6skd4mQ+34R2Ur+ycFoQK3At8VUk0OqMnCWIVHW4H03c
+RHjkoTco0MeuWijdhT+ZCd3aSsRpw8eXQfO57h6jaq9h+HWqQJlZxnAC4HbK7f+4
+WicnQCPO6i4+ckN6K0+rNf22LFuCJMYWArzAhlf0WrpSwb+NBbGoeUUKvbOi46BU
+rgTHHjKK0kcf7sqgk5v2YcbpvaxUY/I61pKYb0LB0NhgCkeomQj8ySjdJkBW3Mvs
+jNZYXUYWW6ITSEOTSVbivWw7YdOuY/nVi6K9SaNgA1fN8leYNOe30Tjkneyys63p
+yocaobGRuFRoiBRTeh77ZgM9C49cn8dImoq8++Mw3h0MLTB8iIOZqz33NIrOVcyA
+h8gvtul+yrggO3m3aeUsBlkhlrzcMu3ri/Cl48zeHXRyDw9oq8UosvmehvRdLfmz
+IE7WAokCHAQTAQgABgUCU0WcywAKCRC79o8D6PN5Qc2wEADQRyTNEUieGfyZcae7
+YAgVDZUiCA/emW1cc6xtDPCvNK9BgTt0k2nv9ROXp63uLdb6PguYSHlca6y+Fa/j
+iK8SpIvr7yCgci+HmPO0sH5QaUR9CBKMt9zZYnqwfYUAGt+N2bDLlqiDSEOKaVtk
+0bNvaufjkDDlJktUSqTn+L2Tz0PGkvPa79e4ci70iXSKSGsMlwvkYRzOSxnQq643
+oqKF0kfAABGgkPrn3uEBsj6AFtx53Kqj/ffyrsiEoXjnJlFH59Jjm5xa1LL9Bibz
+vzehiHaTfIth2CJkZ8+6ICzHe+Pm3xqzwawxfnEPLqTNRIfEdNBXlA2Wpyxj49Vh
+JK4yj4SyHgdXygWoFqt/+r5k1VMM7m5fgLuhtsQzsbQg9trTqLFzb4kuG9Gxbxre
+v4eQwRUMH6UoQv6S6b7/B09fxQg25ty2Idc9yb8EozYo8AbeLRNQSCyJWlKErN9T
+ZgP4Dg5TsC2gbakHUcUCyLq5pQ+j1PiQTO1Ed2zEJuPIBiZM7Og2Arq9/EYnYf49
+aWsnMuWJbqkmliAbWIdRbnz8sRS4SDU4XzZRq37HVSpI/V8/CGC57isXfP50TPa4
+tqUDpw0ezyuZfmh/AsoNIxTkD0Umt+gUDu44ewhbJC2N7N4V2zUWjYzqkk4B7Xd1
+5JyxF+D9ro6ehQztVhVh+1AlGIkCHAQTAQgABgUCU6AVhAAKCRDi3N2RMmab1kPD
+D/91g9Cya5eAA4ercGGYtWuk5EZVq3VnVT0GAZBX2Pi87KFMVV5v8yVlUjGosVkX
+/OxExnv5tMOoWOOjncPezzbXOE1MxX1xXG5Wy4Ky/CvIxmwrYT4aXD5I6rJWhPko
+6pKXiBgbR8+7nOCaa6EjKlNq9BssnOuRSJ5udjvZDDwlHH458M5Y2Sm5aTHVDZRj
+/Uv8I+P7Biyadtt1WJEAX9JlGe8L1OUS5xa1EDo+25PbwP3valGHdkpkonPeP8xs
+SxsItNYGd31mW3/AUZ/M/wQdyNsh4d12dyHCjnIcB/x508Q0xnElfCnpz3MFx9tH
+F+NAv+erc3YGcUFinlfOM8d4Y/gkIhrTzYCAQM+1LBkx1RkLWLqnL5MAtTQDMwBt
+3hyer3wlDOzW/QWlm5+YZYU5fNwTs7b6/qPxIp0k3m3Ve5xEvCYMs9bJBYVud1C4
+Fr2O6KMZ0mt99lBsr03NyZS3yNF3Q2pRRYOQiYSzwx5Dsq64cxR5ZeQF+g/kKl6e
+nYhXWP9VRa3dTNKOWTCOxNHVD2W3Z/e0/pSLKK1XNwuS+K1fg8WKwWRxYZvvYL2t
+OOuHukQB8HVkC1iBlnWKEKpHGO/NINn6zmzteNaDfzPyJVD/whu8yOazPUXp5LCE
+gnqAEI0yTTsMHb/QMlBTgs7vENsYmVu7vmKcRnx/24nqwokBHAQQAQgABgUCVJHq
+iwAKCRDqRFxBB4+ojdK7CACWMizZa6JvbKyyWt1NQFNkxoYyQT4BdKERbHknSclt
+RdKjdNqc1wbeGwJC99ieZJeAkWww8iyD2kdx63luvjzKjwevCndkOSXb6vlVn0IE
++GQ3RjKRbsOQ9vWXqzPIpnRctBXk+4Qgb6neZBLOl8clLME1LWhc7Fpt8JUHAstS
+0RGo8ZvXDK2HCJ7nifBw+eGA36/Gk5GoJHbngndp3kuHkBRgtE6b5SFoTjL4BkW3
+hycyuxulhVyvUWKmGWEIPSHUuMZHJCLtJ1DINgq3so1moBQEk9SpoJBrebtwxKMM
+NSoa8eJirpPlZlSGP6zbcUA/M+y6RYz/Fkb90ETZSj0liQEcBBMBAgAGBQJUkewh
+AAoJEOpEXEEHj6iNdooIALWdGnk2Qrm/uffnX+kVgCKH/KnecWU6+tlv45bQHG5x
+YcU5sS0eIxEd4VgWVNaV+kS4EdXfz+EpZYRcjiH1lo+94MXe0iV1ObmBO/BkN6av
+61PyLtL0Dijlrh/7rpIBiNbY/iobS15gNdeFJFIDefNnxZNmPg9+eSbY0w3Vpxou
+YdICBO3czUWDNJa6q2CFEtjfkXS3+DImurrPnPgRdpirQLqi5DgTc5EEHsqP5oaa
+bxnGRsBZ2E0U6i3OlcJD28zVyf1iZq0RK+18jOf7mpzE+c4IQ1N0PK0eeBe7Rl/G
+tGDYbKXLR7NLF6r18c+Fz6TilRAe1DcSlrspb6F2JpuJAhwEEAEIAAYFAlTHYu8A
+CgkQ7i/vOn2o4onUHhAAlbOx1724tYihm6X+9E3+qOBmq+qKSpZkeOt6Ey5/LHoF
+I394LOqGfqtYrUAJT/QDJdVU64LAJsto79R7WVeq1xvDW6WBwxyMqL3HGWvOhoER
+sAO16X2o86nrmUoanZJkmUQI+LKvCUdU0tNuGML5VCLqLdHUZbvl1VG2jxS2kp3k
+Fpc4EtbePVjm38SuIIeeWgk3Jjz23e5o2YdoMV+3VmDhg8UvSCa3RxjTLrsWowLa
+zkHSajSc5EXteQfX50y3SojAqFJRzvK/t6sjKOl1tMXi1IbEPYSPCupv+lAc+YyQ
+bP2FmyC+7f9AgVzURpcHscrQON19GD5IA2ghOHzx6JVQVF6y0W8XssUZBl8wrJoR
+IP1h3cIberCElL0bbArOf1ULViE8ww6fjDOe0R2XhYgavW5jm9YqMO93EfcoEwi8
+c/w9c2tiJczZSH+TFzoJmR85lmGHsH62Ru/IqkQT1xO82tiDfPGIUFKFRMnXNLSv
+cjjkd5GGOOjeqOyovKoPuRQU6G4XWFbFbsBRBW9kVmsY1HCf5ZnNsdH8OjH/3OAR
+QgMnYX03IJjfI2HhZ7OWhCs4Ed2LsH7DXyP/aMDciJM7nCfWgRLNGsOLzQipF+Z4
+jFHYbRKhsfNACCs98SARit09Re9ofvKNkOJfn0mEdbDbDtqwxKBoUqasNixtSYyJ
+AhwEEAEKAAYFAlU1P4IACgkQFT/jmIIcg5REDg/6AqAnZmE+0nwl85mnv/T4qpcH
+hOLTbE07k4Aj3XeGgxS9cbu3Iv3pR22HcKPqS3MvOFs0++cTjrgRPZ+jx9o85uJo
+sYor2WxfYBUoiVTO+PF/c573TjvO4XS19InDKdFpY2Djwoobqq87XpzwIdUGiwfH
+koczpDsgJ6hRHNfWumNXNMEnlyBluYSNVGmMn9jOZUg7n+PmAyDTHPKt+iAM42t6
+sr4clnaz+qQ5MeOejYgjkZAiSRw04qYsfCzwdn4PMYM/5gCjlInmtnKt5S1S/TLT
+wXyc2Qt+e78Qtk991ZB1ZvSBeMZkD5Ie4+Cs2VllBZnO3/fiGvbWGKnyejQg3oE+
+lb945vyO+PT8rVmRgCQfgMFLE/rSx/HEV9Dqhz1l5cvLjUDH4U/uF6ZY4Tkl5aRx
+fFUI6Vzg9tqqvhxftQJ1QcQs5z/7gjif4ZohsSUobzIx1fKq2SkUZp9t6AEcshCy
+Tg0W9VohkSMHH9ZRXvkeNsFoj24017G3fj6LH0X5hECEBXBO8LUQECKdcYkdPVZo
+PNVtpqgwPLxUI4u/xKhzNpAH3IpFpXbszah2CFbJxwP9P1PEHPCtuX0fUllv6EU8
+c/IuPCRG5CONgvSrY3wy6sKSKFkdJBuU7JJNVpVX2H6IUutOJmcFbDmPOIqx6dME
+IDkUS4c/ZwYgEshLMia0KVZpbmNlbnQgQnJlaXRtb3NlciA8bG9va0BteS5hbWF6
+aW4uaG9yc2U+iQI4BBMBAgAiBQJVM1maAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIe
+AQIXgAAKCRB70YMg3q36EdxWD/9p6KKM2qxDemNsVCfdW80PuE18m+tVwtn5TiQ7
+Nc8XzXxpV06cPI1fITXu8EMNy9pnvfSwKxkhFkncqFQn6c+9220qNQgYsBGbH6kD
+cmXcgi/a4u05vcjRMy7J8gYlUYX6rXT8J4MwpWMqWbI5WtHSimR6X4oSg3tUurcd
+/fM8oQ07UcGCksd4/fW2Vv1yCz67ijbAutV72bIdu4UFbsSNZtK93PbOWLBuwV4V
+Usw1xasoBTQaSSIfr1KCfCZdRGSEaZrgvbsYu2Qce5+zF9tw+o+2a0K22wZlTgSa
+uCEZSpXIM7GefxmrJvMutF+mKf44Ihf5ocnPmvYvyq2s/Quis0xfcail3bq9+E2+
+r/CrzGT75tbVHkWaRpSHH2H4OE7bJtyDun8TwkdzzOta/5tvfSj2WSs5pTwbFeuC
+1vVyO1tv4zEW+ObQSXDsxJkMbuwX8A8VTpzC4gD01qL84YBQJK39bZ5xLxmjFxZZ
+sZISIO+O0Ry2SmYeN9W4agvfj3ro6WbjVV0f42OChJhQkrHgy3LwNFxnF3KheWXz
+gu8OQQRgpmo4gAKwl/wIMW9SSaFdz6agKPXljG2Pf9GyVFUSI5TKukPAHvbNZxkb
+xifOjIbGFxEhz+AV6M7/JIjxvMKlieB65Nvk2MUb4KEaIicdtBwV4cGVoe0qLaj8
+HlrEg4kCHAQQAQoABgUCVTU/ggAKCRAVP+OYghyDlOTHD/0b4j3RERiMW9kXvfgg
+wFAClQNxCjlSOicxwwEkfIAlGyZj3v4qPoTNPmg9on8zzVdUdaFrHyXBJkqsvWbV
+GVPo5BA7J6p0q+V2BOJ9mjpuXMkrot/ojMYOKhY4FbPBQ2WOJzT/K2tPwg5H12rA
+b6fcGlb8LQR44yflcZFS1jYap+IHpmqQVJiL780YWZnOmc32phmjLKaUGvko3E9t
+J7qX7KphjYgapxBcb1MqeA82cMRUnGC7pJoHL0dOjsVmJ2keTszHBBbchqyEVKtl
+2cc/mecy3zlFInS7V2c3P/1K3yDXM5mXOykCcy1ltL55vjkO+ltB4txGYK/a274j
+aQjMlgWyk0/aqdomokQ8SAmWk2R2TULBguA0tqNnAyi4tu8RkzkvTPfqV6IA6aW6
+fINTCVCAjOXqQ2xe1LShBa61mq+jwwVvuijlZNnmHzYiKAKdmJigwMZOoc4C6DOd
+XxdIJEqXybVRwMZYy+N0DFxBCMbTTtSM+viLPXKxdI1t0QXw5gixeUDnXmZM/Pfl
+nN+iX+95RmMZXm/QVibnR5o98psoKcY1h7A0WBoeGkw221XqkrKvorvcAyo+RR5a
+r0Iytx83s85Uy/gWsbH5ueQaExTDdaVpN+KZtKigCtgyRrQINWMFUx1E1BK7II9z
+0lkrWcPqrpFE21rc3kJ9v6zVjtFJSGVvcGVucGdwaWQrY29va2llOkBodHRwczov
+L3R3aXR0ZXIuY29tL1ZhbG9kaW0vc3RhdHVzLzU5MjY3NTAwNjg4MDAyMjUyOYkC
+HAQTAQgABgWCVT4zRgAKCRB70YMg3q36ETtwD/4kSlgyX+Hycm1tXHfRyCtTslLV
+GdGoMRjkWboHlutO/jajPRDU4GtNUXUdW9TkF0hozAzXwl0FXX/ZDbzr3kR+jwAn
+WA0vN043UEjWsshNyMTW5lYEL8Y0jB6vqAGB3bEknW9IO+91vgeWhOVa0H7nrFSx
+VbrB88mRpIhdb+9ifgxTZVAVEmoIr0epOblrJ9LOxfrjcsad91noabMvUYyKHoS3
+LT3Ur6scgK98N6GcrcKQrqOk6rhO7HfaSr57A9CFKBNBJxzWxaOelEbDtrKQI9/m
+OvBXIeWkNhjVpinRs8QXBrKiMimzQQs0aXbuMT3qaVDMqQWjskLyykxAR0VC8KT8
+WrmWJ+vdLtY3vGVx04aAMkckWNy409guDklSYeBVkHg7ZVTYQWQM1VjUV4OPW7p5
+/n3KfytHS3P7tkdkoHVvzF/P+zCGrhOlfEfizA1/OCchVWGzpj7VIDNJtt6EQU80
+o04cG3VmLHh+j+7sln8kda10syntoIuZZz6jbyE65snx6dow5hLghrY3ZX4MG+Nw
+ExRa+BxswXvfbIBE/sV5+JmwHMk6C1rlGLNpk2O5hTZUa2zcT8BOyVncFMKt7q+z
+Y3Wrv/4OUV+w0gg6hCOJ+bh07ePKsSB4EPMWRaG+p8Juwbn899wqFOS8z3Sw06Zo
+IB9NuV6ZK3AC6qhWarkBDQRTRDfTAQgAohXQEAMegmvXV/NJrsrJD3dPHYBShCVV
+Hr6vKPgMfRtK/f2ofZ+kKMndBzx4Pi65sOtiQ2vc37oBmmzRU+xykRQU92jTJ13q
+xSAqTc4IwIAmrdkv45WO/JdIOOW9e/SFniO90IJY8ssT/DRy7kLF6kRJXu/8AmXY
+/v4uaOjYaNJIiLho1+S6MUeC1RuGWLSQgeYMM4pguW+zWY0WtLgsTtO6FaBfo1fu
+0YT5pa8SBw8VtCOUo51S6+XuZaXqVqS73AktuCsY4fTIISuw8ZbqDel4VlgdOIgv
+Jg9xg+65CHJ5UMFwgK5msPv8qLqkQESEm5SimoN72pUTxmP9/I4pTwARAQABiQIf
+BCgBAgAJBQJVM1n8Ah0AAAoJEHvRgyDerfoRiIMP/1pg6gobXj24mTA2hxyhkwvW
+yVHWfUUiuAWDlj5sWwT7YAwfa7Qo2elUBOOX5vH3M7UM+6xGrpjkjddpUv5Ks92y
+vEJSDBOZF+SXuYe2WAR2dmCZaWJN/Cn1NIK7jAR5ibYLwvMseMPHbujIHt3qvGfX
+0JpHYcAo6FuYLUiqTOMKQyFrbTQJgLZXOOm5YtnjL8KARtC+PmEpk+fWoDnEBgA1
+9/aWYEDH4FvVgFNW8akwxq4UrxDNt0Cb9JClgdNleUzP20+vhtQAwUSpWOwMj9/e
+3bn66gGs9FCqhmLD8n/mfj8jp3hsOeNpDgdE6qhRJ0U0bjtlmmFgDt7qjDfOyPMa
+D7Jj4o5sJcCe8RuqWF9EdSD5Z3WMxo/InohhkFE8wunOVFkvgkS/FfuJcWloRQPU
+9dVNQmJtMA/ORRwfoWmDVaF4Nd88D46cXImBc8KZgigzUfgqAjwii/S1EKxNm65W
+Rc5D7IqKTaABSVKHTrNJkwzfvRMQCYuWPEPoBKXXtD26UujnH1SDOjmm0hYyo9nz
+XJzwf84MeAzpxi8F9ySB2KASMvTv7ePeXE9fo0NMb7bwMisjArgAhOzhZZ5szOc0
+UPRI113E4SnpjTTbrQMTFTHcXBfE3tvafAWmwpojSLuKAuHpjke8K3z3xhH8sa4E
+pP+axcirOAINuceSnhS2iQIlBBgBAgAPBQJTRDfTAhsMBQkDwmcAAAoJEHvRgyDe
+rfoR+zIQAJ7iY+iY35LOrowdBo0ShYBwjD7sYmvPdZqGWp8ismFbOBTREIqKqig9
+uar1GGf4ZOnuriaWYZGV7CGnYBeNPeINY2oczzj7yIbS5qtZO89It1T86bZf/1MI
+NLT4ivlesYtcBX109khLoqCmhR9NaaHeDy+o4WtEdBj+gNL7h2h9a4wM8RYxpKld
+OGmspxGF6LFZAvXjV6Leinm45/ZTFkA8Z42J6tUDEgQIZlCEflwxi0YaaNUqVDx8
+0+siHNhW1NZdPSofriXNDzw6KogmvupjkwX71lFv8sJAjgKuvsZ8J0k/OtLSuDl+
+vH1R3GS/uulbglYeQ9BJqwPLs4IosbBg1MT92m3IK2PEPrxsBqaIGEVyH+OZMQZX
+MscPEWKPgi2J9cgbWBQtmnwEnsB9g3S5boMhQmIaAx1Fx9n5MDYgShAcsw5f7Jud
+5ZFZ9jg5RKQjbBxgRsJ3KfgNGOPI+q1fN1zlThU71tr3VAcPpVitIwTpblNfiib2
+p24SpQxdAwYLu7B8Ic3Txxt+q+HOLRtmDE8lfGRduNWo6NCBqASqyzk36tB5Devc
+f7jWyJg8Bm0Embe482bG5TUFVbd04jJH/45nQ0MzuqdSb3+K2S+EnG63VVbFuL2p
+6B5c/j+MBSMQit8EUwXoljJQfPR/dkKoeGolIL4lB2sR2VenBeVZuQINBFQ7zoEB
+EACyW8v87ZYk6BhLtZbJb1kFEOrXimVVEO4cvWzODfNxtABi2rVAR7WOj5CQXuF5
+CMN7Cuy6BQldluBkreM1qfZfpBt/yfDsOxBYzdBew6woNDNJTIPXUM/Y2EqAUfLJ
+Gh9wUcwqvQCiFSlfeKaC/xuudvPS+8ygx6GZ58hamwbXnWGYq9EmcfpIqLmdap5s
+UT2mJh9n5Lorr2BAMsywy2DPBeYEIXWTOGFbbr0iAmD3rdM+Ag4iV/72Zg4twM86
+BFWingDP9B/X/TYueOydvvHWUpZYE6wPn6tGjCVUnADmyMUwELRKtA6DQcz6Va5l
+tdG+G5oHj6wLzZIn6kq+UrcpkgYML6HluhqyYsjeF4aCzmnWc8II6oqEdZ18ObV7
+a6+bGHZGdOm9Vlh5MbbtYRU3Swu0WDcWNO8rSzTPgtpaZQsAsJVge0cCPQS9Q4a3
+uJL/Lmml9hpLGqvZg3/TEg1qkNEyf0QIsDBpha8wRGCNSdjsnrIYv3aluPzomQLp
+DiJcTp7ervM1Nii+kI8aqp2tKE20uGb930XWJLlAi+ggxCXZLkQIlvhsPpSnDI16
+gKRKosYWg5KAf5MLbvtoCBU8wMTH8BTdWLTXNJCWVpxZBiFjK68E3DsABc5Rc+8J
+/xMjBbYPhEXKjF9PqJVw8DPDDgyNvgc7pT7hAxLXgsNPYQARAQABiQIfBBgBAgAJ
+BQJUO86BAhsMAAoJEHvRgyDerfoRY7kP/1EMoOPX/E8rVGEGgW/EdFHyPioUBbNT
+bM9s/Gt9Ag9THohx2FnQHKJlb05IOIOS252B1Q64/12oGNxTfwCBPe7rBW7Exvq7
+FgDi4uzdnTdnoVmet+JDL64Mgvbk4jnZMN9fgfCuTKcskoA/HukgM0X4rtOv23ED
+lMiYn8PB7naY/W8uLshoUh/pB7uGn66TnblIoIW7BhbXLkcMEP8P6EpOvF7z1IIj
+kFjGYf6VaF3Rchb67f4YAKwovHX8uVDyufJ5e4z57vyLkF6yedJzQBSCMT1XqNvw
+fNygJxGT1PqlmbHW2tFfhSCEEFYsy7UBTLiSZOP1EwDnydrQaUPTwA1+v7HKgxO7
+p1zVncXB04yuyplo1wvAsoRjsfFf62+yGpeJmaOHExA+kjZTu3VdV2MJ0nLS9hwm
+DTN0VHuHieyVrru/zW3+soqdnquwUCmfYQB/7HvzkmTWLBJGeKcVHPFuVN7gGSJl
+MAui++EjNIwWSLg5kITmF+rGWerqyJ/zGJY+P4akgRUuL0yu510S5ra+/uU+VObK
+1bHlImHPpfabvf1w/MxiLvjNsRHeNTcD+OBg0E0JtGxZY0b+X0m5cJouemBzxUFC
+IdkWTV95DiYHskN3p8ObmPu6HCkVT9/IaZ335Yi5SXDuk7R1pxJUt6QAm00DNERy
+aMd86lN6uit3
+=nxfb
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/OpenKeychain/src/androidTest/assets/x.sec.asc b/OpenKeychain/src/androidTest/assets/x.sec.asc
new file mode 100644
index 000000000..57a204cfc
--- /dev/null
+++ b/OpenKeychain/src/androidTest/assets/x.sec.asc
@@ -0,0 +1,156 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBFTxJOIBEACpXpXjz5ivyR/uWJexJXZrLHrvBRcivQSxzvy5Owdun8MGOxzo
+YTPAJzskZH4Gg9MIQj9puUke8A59o9KtNMAd3Szp6aQs3lv/eGOw0DUe6g8a6kJ+
+oQsHMYYLnyuYrOvQrBewKhh4FynkryvUZCL25dgrMHeTeMAeslCscdT6JYd2gxgi
+o0+Bn8R6BonS7hTZStfAOYB0SO7X+7R0CpPsMiHtNZASzLRkk53dB9AO6m81CurP
+qh1oPjc7j1QIiwCszSD8IxuR3fH6nyUlMtqx/mRmKNlbIYnr3sN5VAMp7THq/Qjt
+9XDs1XHBE+0H+yg7K4fPamSJsxy9zPq/rA9vy4pQIMzxugxeWEn4OPWtguRSYtI1
+WA+Ki93QJ+DVRlpMvz/MA71ZkvWUHpFnIssU52NdHZrIZ21KCo7PiPRY9tQkVhz5
+S39a8npSeCIwZ9bH3UGKrRyPkhal59rjPqhZky1X+O2iTfbbFrPE+I4b46pWX9XU
+UgXDnUh8Qeymhh0f1DTcbquW2nwU/R1quKjw/TCZQxKnkAzCXsVFk2QoIG1qDVrU
+YlKXTXoYaRfzhMSYSvh7JHqdldB+MyKAbXpLtZsYIlLkuqe/KI0IPfO69ajDxLOr
++wTtuA9T1SICGmQkjYkQiqVy4y7vDW4Zw143sPXreZhiBbLBoWAIJ/JcbQARAQAB
+tAVYIDx4PokCNwQTAQoAIQULCQgHAwYVCgkLCAMEFgIDAQIZAQWCVPEk4wKeAQKb
+AQAKCRCdYE0vMQcWo/TGD/9SEfE7LxWw4pPNTpVTECDztpLGrj7kCr0anO6XnCqL
+3OYoZz5vZvKBmunoPT7VqerQIZCnhB0b0ZOI+l51J03Xwvfy1+MIQCnZz++JY94B
+h5dv3QAj8WUJJ6KfKrAxoXGU67mNhW5oe3mXQd3/a0JjmjV2yzFFOS4edyWPQal9
+lY6MpOKfEIiD6uRqh8A1A9jAstJ9c5XHVtuBv265DEK5tMyAU3cmtm0pEzgmvPoG
+kxo1vDAhP9GKb+DcBrB6XuZ7Kahl+kDpEhmuI6drxJBhez169aE43dK7G3X1W78f
+OyO0C4jWAy1kVj4aYT4qgJj8TwRoAwlzX5RqmK4RW17sX3nOlff/FQclepawOrU+
+LO5ZgbQ4qG2yDJSJ+tcS2fIO1MoI5vPa7DIVEpMM7DbrPYVy0Ix/xv80MwKQhnWs
+P5tLRGuJ7JbKPzqvBtG7xWow5isOMkqeBkU0yUr59tAtHwM6c3Vi2at9YBiraqBY
+3mgEukIuNpSuhFSBhniVUovVGgj8LCLDL+mpuc9+HUzYWJKGks+eGY0Od+dJjgh+
+wXQk0rTTkY80kKpIjREDOVuPRhsw5OYb63fbiaYwormPx4pXv2mitOYNAXy3YNpR
+Xl5MvObYLQugpqtyjpijyyANbsHKWwClkL/vxnbcfRXF307NQGSwhs3gKpSuovVE
+edEkI2QBI0VncGdwaWQ6QGRuczp0ZXN0Lm11Z2VuZ3VpbGQuY29tiQIcBBMBCgAG
+BYJU9KuzAAoJEJ1gTS8xBxajnPEP+gL3MasL9GcXt0c93QkQsRay8IMCspM+Qt/X
+rfoUSJb41V4nqeyumX/kRmY7/eMZJnxIJgn8aWmRjiPhasUMBfeXmm4RAmwHEkFP
+6nw288dHIlKgRaAMwfs44thKxPKTkscLiSqbmrRhLULW840pIZkgtOLmhH2tiaa7
+w/hgDCMgHfwZaSlQkEDfvAef7i61itNdBlL0e0CelzNL0Uc5P6b/Nvn5uWlrLIHZ
+evOx8dswhmwz25D7KZQPh91JpAqoLk5hj3/DAbVxeUFK/Zt9o/bu+ij57qSfRif3
+HlLydH98FFcetqLbGDt1/Eiea3tpqjK5Xgf+HOppQo2MpOCP64xNejGWnKx6M7yF
+A/yiI4Ahe4uP9+SXar0FPLGXAfyc/pSrRN94dY+iXpWPCSlNRNAOyr1mxPCvw9MM
+C5wYnfLLSWUMPYv9rQyr1pvYyxe2r0Fla6JAkRy5nMnR1RdorwafeMcyQi3nllBS
+S0wzmagus1W2qqRbbOR8J5CXkkjbw9vV0vXFnCHq93ms9Ebnhr5rNzVndKnxVCmK
+hftW7gFz8Qz3tG66eSWA/6VrRvfUNfR5cUBpVOARqxqYz7OPrGg0Lkfw+JPQX8v/
+WMIfM6Jsj/koJmOb2y/h0rJ+iTBofPCoGONKIzlOK5c8bhSmU/27KX1xBacTgN9Q
+hlulIbZs0SsqZAAS1odwZ3BpZCtjb29raWU6QGRuczp0ZXN0Lm11Z2VuZ3VpbGQu
+Y29tiQIcBBMBCgAGBYJU901MAAoJEJ1gTS8xBxajHKAP/3WDeUfBkuq50v3E8SBx
+qDHXomPncNOccRPS1/DYwxVp5JYE8NsFW2rlmeJiy4Rhm4MnptUFUTPMBfYOcHvT
+oWr/3Gz0y5ik5oiPC5hAXWCPrNkdtgBugBByL+0+w2PWKQqwBIc9ef+7aIh28dKn
+6zr71Au+DXJKqYz/jOqY3IJGzBkydxOPSy83WNPm9OR/zfhCOoCTDAy+0TGxYgjW
+Idi4k4yDRWhAeF+GqJRuDf1RKZxPL1MjwhX0Lv4ndR+V3sECRVh6HRIBGYYti8kU
+0Q9jdbYjU+guestyyvsWQBOaGRP+gGQY+fYdHb2YUK6jMcndPxbRonjaRY5Bcc0J
+NAYAqFv/WyR7hd4SWXI8Zg3u+GV7x8ZbnUOwfvXBVvZohbycpoT7WVOPRhQ2CQ5P
+uS2E5QaoA6fUfH2qUAaxlvnfMRPL9E1svIEFXMtf1GqO6hzzJWV6Qw59HODTy568
+StloM6IVnlolJfxf7+aEQx+KrmxJqVVWlbZ5u5gzQa+Mak0N5F6m8w4VftyHT0W6
+RinNh3IGbk64+uXYzAUhJ6badfXrrxTP0wkjPPPjNC/BKfPCzYhZnyGLCuFwlOqE
+o1Nq6qMzSHOlxR08ycX0MLNEfWsqu2CV8YGLYThejOr8JSjyXM3ANHNB+AZ5/eeY
+AGUc5XgxTbq/MpnytiBzaZfw0Tw7ZAAS1odwZ3BpZCtjb29raWU6Z2VuZXJpY0Bo
+dHRwczovL211Z2VuZ3VpbGQuY29tL3BncGtleS50eHSJAhwEEwEIAAYFglT4KOIA
+CgkQnWBNLzEHFqOZJQ//UL+/ZfikmVA8DDIbbd8beIgJ0uRZgQ4aVuxRNiWhxQbl
+5KPQaiztrwr3ESgyf3HjjUyGhg5/UP+ObvloCqcwCphSrBNxHiEp31jLin7QfWLp
+vzPOjtMJNEeq3yeXq+YpHw2VZ+nod+XD785YtLBqq1SipNSZEifRYnsLhqQOj6cB
+t0F2RLBm5afLbd4KjelT7W7229lYlGJkoQEwrmVYASiMfeBDBnHJ1Xgp1P0dm1gS
+ERVSzV2qW6w0/psOHq4cnq4XWSn/IRW3UYfHIDDZJG125keYoaJAbBnzC+OO4eV2
+KheY5dkmaeT0RmgzpEyJq5PRDNpnoXvSYuC9KGiNTbCiQ3liJnUJslbzDwvTyTfa
+wxUba4dXhpcSE8mjXa/9/vMJER5yW0zzkSa+fjIIZcL0PQqlTeDBzFMSA9Ut9Y+m
+6vdGwelIeyb38dWvsDC1NUUR2pydTsRtFM1o7jUtGoovQlLh5/9x/YDh8vqpzip3
+L5SSvN4RdXN4P+EZor4xfi0PVbHoDZusxyPfKEFlCnX6k9qEaWplacLbl8M8mtpv
+++3vYVW6hA3rK/6XdftFDJpbmj+zTXvrATE/lvPcJHiswiuMLx9BPoEiv1MGXTyE
+6TxB5H7L1eCQWfbZYnkEWiuXlfDyofvDfufiGTsBAQzN3ePuhYUNznUB+1syD4E=
+=Sxs8
+-----END PGP PUBLIC KEY BLOCK-----
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+
+lQdGBFTxJOIBEACpXpXjz5ivyR/uWJexJXZrLHrvBRcivQSxzvy5Owdun8MGOxzo
+YTPAJzskZH4Gg9MIQj9puUke8A59o9KtNMAd3Szp6aQs3lv/eGOw0DUe6g8a6kJ+
+oQsHMYYLnyuYrOvQrBewKhh4FynkryvUZCL25dgrMHeTeMAeslCscdT6JYd2gxgi
+o0+Bn8R6BonS7hTZStfAOYB0SO7X+7R0CpPsMiHtNZASzLRkk53dB9AO6m81CurP
+qh1oPjc7j1QIiwCszSD8IxuR3fH6nyUlMtqx/mRmKNlbIYnr3sN5VAMp7THq/Qjt
+9XDs1XHBE+0H+yg7K4fPamSJsxy9zPq/rA9vy4pQIMzxugxeWEn4OPWtguRSYtI1
+WA+Ki93QJ+DVRlpMvz/MA71ZkvWUHpFnIssU52NdHZrIZ21KCo7PiPRY9tQkVhz5
+S39a8npSeCIwZ9bH3UGKrRyPkhal59rjPqhZky1X+O2iTfbbFrPE+I4b46pWX9XU
+UgXDnUh8Qeymhh0f1DTcbquW2nwU/R1quKjw/TCZQxKnkAzCXsVFk2QoIG1qDVrU
+YlKXTXoYaRfzhMSYSvh7JHqdldB+MyKAbXpLtZsYIlLkuqe/KI0IPfO69ajDxLOr
++wTtuA9T1SICGmQkjYkQiqVy4y7vDW4Zw143sPXreZhiBbLBoWAIJ/JcbQARAQAB
+/gkDCMorb5SPPoHekALY3vvpC5rmKLs5ULOzs9BaI0qq/D5kQjSw4WnQIJwLTgI8
+iwRYL+pUn7t8txDAEWKCSI1kVUaNocWYeUpiEYo9WWWHwGpf2ZZXwS/cQYi1APD5
+U8uN/fKwFyRzLEXi++b6Lp4GA3l6f3sEwpSu1Wz/jRKDAweBENrlrCPEbn+VNkF8
+X4aIhFNr95WFmTmjNp1AP+Ons4uny4sBVJqkbzoosLNSBHN05+mWsrWepTVGI0VP
+SPmm/PY2st+zwwRPjGbWvEcFHUCnZMGrF1bhl7nQ+tZh5KTgCe/gcPVlf3ZQtEqG
+RdzlWA1jsDF4h1/vpynOyONpZ5Hx0QWe2z1C7VeR4SsgASdbtX4EWj4Squpya0rf
+X4k/EoY7vYX/OoOfi5aExkM79vHzN2EM89KI9ecoCfHy5x6Ovlhep7mL/p48zJZU
+Q5pDfT5aLL6XV6T2gOWRFnowdETEBHWt6qYRhW47mvkdAiEBBpPi3sQnYM7M1hvD
+2/yjogrgCUgAfCvh4vJbu2LPiGHkqFoS2iEEDexAcQ8REw+ePi4cg818R23zAJDV
+xdLqpKig71YDTDxxkCuNoJeHOdFa2I9J9hjlTgYEBU6oJ+9TlsIueEhDnEGiScmn
+SrhsFOcNyi+TJQg9KMx1AcX3RsCpOsRMxlqY0+zOh6YCh5MGXk/QQGGLnEFEax/V
+ia2nCj91xA8BwPwjAOmb1Z9rdAkdLuPybLEAhlfu3ZFTDy8uQnL0LvC4grKUlYcm
+soeWSaOihxZ6Q6ZxxWOysQX/kYBz5TCn/liSKr2qJZVxur487RYGIrWDKSJtPotc
+Ke1gdUGG+EZJji6ik8blTfwtbuwCRX1WuiZiJidG5RtD0v84fm0U9uolY5HX4GFO
+y8LCJVASyOvD0Af1gf6MIwAQTt+X3UIQr7zyN/QN5aBpVnI5wN5FmGoojz+MBDE2
+/35kLYMQiQ3IspZR5apVpWJ2OPD/i0OunQU6t39zKR+0N5YPGLq2ZcTgRPGkSffz
+DX0jxta8Gl55ysY9aKp3LvbZqvAyHBttnOuNzkw3KgTl1jAsOj3pjeVF+kPWwvAE
+fjsSZjkXTFRZC7VG7bawzJK80Ux7EoQEwi7ixup5UK3DFwlp4eK0KGFJKaIKYj7V
+8CHQE0muDx2s5KmSysYs+r2888r4MEAlEz1WyXdDoyQA0dIGfpvnts5AJCZqceal
+akI5rQXwyD2K4X0N5IA54E1iJnYRwebXGsn8ldD2w+WEfWq3g87e3enmHSe48DuD
+k3I0mf7V7AMEmrwuB+xVN035/QwScQeWE+tV6AdHleu4Ceo9b9VlmE/PYE3P+xKM
+8ZZIe/wAeHhsocSlFL8c8hwXUNQ4TXOrsgfDF+3DYAXvQiwM1WxdPV8zSPU2Mu/2
+4Q0Ba0ZepcKQNU+CPV88XRbzfb8NZ/mTmnHYX/P7vIWyg8i0w+z2Pm+/GFr1I7Gb
+ily11beRweFUs97xnz21g+bJ2NrJkAP6ewFLO3FTWJoK4l/wFNRp9PcwvqGsqSxR
+OdxrYN9RCmhiemACiJbYxoQN7wTCmmaHdtXcfqkYJjNsKydUwPV/Kaaf+PlWBOVp
+yTOVh7MMPN8fjBPFYcFZ5xs/z/gpI7dFu6sXjEw8F6Pe+hPgp+IeWmQsUTgoQ8bD
+HoK/MjjsuD88rWdDXdbfc/PpIC/cGqPpu89sOS8hSQJVgKn48TKbkIA+FrWogeuw
+Ofr/IXYT2qlcwAmlrdRjLswyTsLM5eQU3VH5/IlVilQQjFZBbiqBjOW0BVggPHg+
+iQI3BBMBCgAhBQsJCAcDBhUKCQsIAwQWAgMBAhkBBYJU8STjAp4BApsBAAoJEJ1g
+TS8xBxaj9MYP/1IR8TsvFbDik81OlVMQIPO2ksauPuQKvRqc7pecKovc5ihnPm9m
+8oGa6eg9PtWp6tAhkKeEHRvRk4j6XnUnTdfC9/LX4whAKdnP74lj3gGHl2/dACPx
+ZQknop8qsDGhcZTruY2Fbmh7eZdB3f9rQmOaNXbLMUU5Lh53JY9BqX2Vjoyk4p8Q
+iIPq5GqHwDUD2MCy0n1zlcdW24G/brkMQrm0zIBTdya2bSkTOCa8+gaTGjW8MCE/
+0Ypv4NwGsHpe5nspqGX6QOkSGa4jp2vEkGF7PXr1oTjd0rsbdfVbvx87I7QLiNYD
+LWRWPhphPiqAmPxPBGgDCXNflGqYrhFbXuxfec6V9/8VByV6lrA6tT4s7lmBtDio
+bbIMlIn61xLZ8g7Uygjm89rsMhUSkwzsNus9hXLQjH/G/zQzApCGdaw/m0tEa4ns
+lso/Oq8G0bvFajDmKw4ySp4GRTTJSvn20C0fAzpzdWLZq31gGKtqoFjeaAS6Qi42
+lK6EVIGGeJVSi9UaCPwsIsMv6am5z34dTNhYkoaSz54ZjQ5350mOCH7BdCTStNOR
+jzSQqkiNEQM5W49GGzDk5hvrd9uJpjCiuY/Hile/aaK05g0BfLdg2lFeXky85tgt
+C6Cmq3KOmKPLIA1uwcpbAKWQv+/Gdtx9FcXfTs1AZLCGzeAqlK6i9UR50SQjZAEj
+RWdwZ3BpZDpAZG5zOnRlc3QubXVnZW5ndWlsZC5jb22JAhwEEwEKAAYFglT0q7MA
+CgkQnWBNLzEHFqOc8Q/6Avcxqwv0Zxe3Rz3dCRCxFrLwgwKykz5C39et+hRIlvjV
+Xiep7K6Zf+RGZjv94xkmfEgmCfxpaZGOI+FqxQwF95eabhECbAcSQU/qfDbzx0ci
+UqBFoAzB+zji2ErE8pOSxwuJKpuatGEtQtbzjSkhmSC04uaEfa2JprvD+GAMIyAd
+/BlpKVCQQN+8B5/uLrWK010GUvR7QJ6XM0vRRzk/pv82+fm5aWssgdl687Hx2zCG
+bDPbkPsplA+H3UmkCqguTmGPf8MBtXF5QUr9m32j9u76KPnupJ9GJ/ceUvJ0f3wU
+Vx62otsYO3X8SJ5re2mqMrleB/4c6mlCjYyk4I/rjE16MZacrHozvIUD/KIjgCF7
+i4/35JdqvQU8sZcB/Jz+lKtE33h1j6JelY8JKU1E0A7KvWbE8K/D0wwLnBid8stJ
+ZQw9i/2tDKvWm9jLF7avQWVrokCRHLmcydHVF2ivBp94xzJCLeeWUFJLTDOZqC6z
+VbaqpFts5HwnkJeSSNvD29XS9cWcIer3eaz0RueGvms3NWd0qfFUKYqF+1buAXPx
+DPe0brp5JYD/pWtG99Q19HlxQGlU4BGrGpjPs4+saDQuR/D4k9Bfy/9Ywh8zomyP
++SgmY5vbL+HSsn6JMGh88KgY40ojOU4rlzxuFKZT/bspfXEFpxOA31CGW6UhtmzR
+KypkABLWh3BncGlkK2Nvb2tpZTpAZG5zOnRlc3QubXVnZW5ndWlsZC5jb22JAhwE
+EwEKAAYFglT3TUwACgkQnWBNLzEHFqMcoA//dYN5R8GS6rnS/cTxIHGoMdeiY+dw
+05xxE9LX8NjDFWnklgTw2wVbauWZ4mLLhGGbgyem1QVRM8wF9g5we9Ohav/cbPTL
+mKTmiI8LmEBdYI+s2R22AG6AEHIv7T7DY9YpCrAEhz15/7toiHbx0qfrOvvUC74N
+ckqpjP+M6pjcgkbMGTJ3E49LLzdY0+b05H/N+EI6gJMMDL7RMbFiCNYh2LiTjINF
+aEB4X4aolG4N/VEpnE8vUyPCFfQu/id1H5XewQJFWHodEgEZhi2LyRTRD2N1tiNT
+6C56y3LK+xZAE5oZE/6AZBj59h0dvZhQrqMxyd0/FtGieNpFjkFxzQk0BgCoW/9b
+JHuF3hJZcjxmDe74ZXvHxludQ7B+9cFW9miFvJymhPtZU49GFDYJDk+5LYTlBqgD
+p9R8fapQBrGW+d8xE8v0TWy8gQVcy1/Uao7qHPMlZXpDDn0c4NPLnrxK2WgzohWe
+WiUl/F/v5oRDH4qubEmpVVaVtnm7mDNBr4xqTQ3kXqbzDhV+3IdPRbpGKc2HcgZu
+Trj65djMBSEnptp19euvFM/TCSM88+M0L8Ep88LNiFmfIYsK4XCU6oSjU2rqozNI
+c6XFHTzJxfQws0R9ayq7YJXxgYthOF6M6vwlKPJczcA0c0H4Bnn955gAZRzleDFN
+ur8ymfK2IHNpl/DRPDtkABLWh3BncGlkK2Nvb2tpZTpnZW5lcmljQGh0dHBzOi8v
+bXVnZW5ndWlsZC5jb20vcGdwa2V5LnR4dIkCHAQTAQgABgWCVPgo4gAKCRCdYE0v
+MQcWo5klD/9Qv79l+KSZUDwMMhtt3xt4iAnS5FmBDhpW7FE2JaHFBuXko9BqLO2v
+CvcRKDJ/ceONTIaGDn9Q/45u+WgKpzAKmFKsE3EeISnfWMuKftB9Yum/M86O0wk0
+R6rfJ5er5ikfDZVn6eh35cPvzli0sGqrVKKk1JkSJ9FiewuGpA6PpwG3QXZEsGbl
+p8tt3gqN6VPtbvbb2ViUYmShATCuZVgBKIx94EMGccnVeCnU/R2bWBIRFVLNXapb
+rDT+mw4erhyerhdZKf8hFbdRh8cgMNkkbXbmR5ihokBsGfML447h5XYqF5jl2SZp
+5PRGaDOkTImrk9EM2mehe9Ji4L0oaI1NsKJDeWImdQmyVvMPC9PJN9rDFRtrh1eG
+lxITyaNdr/3+8wkRHnJbTPORJr5+MghlwvQ9CqVN4MHMUxID1S31j6bq90bB6Uh7
+Jvfx1a+wMLU1RRHanJ1OxG0UzWjuNS0aii9CUuHn/3H9gOHy+qnOKncvlJK83hF1
+c3g/4RmivjF+LQ9VsegNm6zHI98oQWUKdfqT2oRpamVpwtuXwzya2m/77e9hVbqE
+Desr/pd1+0UMmluaP7NNe+sBMT+W89wkeKzCK4wvH0E+gSK/UwZdPITpPEHkfsvV
+4JBZ9tlieQRaK5eV8PKh+8N+5+IZOwEBDM3d4+6FhQ3OdQH7WzIPgQ==
+=AYKY
+-----END PGP PRIVATE KEY BLOCK-----
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/JacocoWorkaroundJUnitRunner.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/JacocoWorkaroundJUnitRunner.java
new file mode 100644
index 000000000..b310ed5b8
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/JacocoWorkaroundJUnitRunner.java
@@ -0,0 +1,29 @@
+package org.sufficientlysecure.keychain;
+
+
+import java.lang.reflect.Method;
+
+import android.os.Bundle;
+import android.support.test.runner.AndroidJUnitRunner;
+
+
+public class JacocoWorkaroundJUnitRunner extends AndroidJUnitRunner {
+ static {
+ System.setProperty("jacoco-agent.destfile", "/data/data/"
+ + BuildConfig.APPLICATION_ID + "/coverage.ec");
+ }
+
+ @Override
+ public void finish(int resultCode, Bundle results) {
+ try {
+ Class rt = Class.forName("org.jacoco.agent.rt.RT");
+ Method getAgent = rt.getMethod("getAgent");
+ Method dump = getAgent.getReturnType().getMethod("dump", boolean.class);
+ Object agent = getAgent.invoke(null);
+ dump.invoke(agent, false);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ super.finish(resultCode, results);
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/TestHelpers.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/TestHelpers.java
new file mode 100644
index 000000000..c651d3a8c
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/TestHelpers.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
+ *
+ * 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;
+
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Random;
+
+import android.content.Context;
+import android.support.annotation.StringRes;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.base.DefaultFailureHandler;
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.view.View;
+
+import com.nispok.snackbar.Snackbar;
+import com.tokenautocomplete.TokenCompleteTextView;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.Matcher;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.CoreMatchers.endsWith;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withSnackbarLineColor;
+
+
+public class TestHelpers {
+
+ public static void dismissSnackbar() {
+ onView(withClassName(endsWith("Snackbar")))
+ .perform(new ViewAction() {
+ @Override
+ public Matcher<View> getConstraints() {
+ return ViewMatchers.isAssignableFrom(Snackbar.class);
+ }
+
+ @Override
+ public String getDescription() {
+ return "dismiss snackbar";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ ((Snackbar) view).dismiss();
+ }
+ });
+ }
+
+ public static void checkSnackbar(Style style, @StringRes Integer text) {
+
+ onView(withClassName(endsWith("Snackbar")))
+ .check(matches(withSnackbarLineColor(style.mLineColor)));
+
+ if (text != null) {
+ onView(withClassName(endsWith("Snackbar")))
+ .check(matches(hasDescendant(withText(text))));
+ }
+
+ }
+
+ public static void checkAndDismissSnackbar(Style style, @StringRes Integer text) {
+ checkSnackbar(style, text);
+ dismissSnackbar();
+ }
+
+ public static void importKeysFromResource(Context context, String name) throws Exception {
+ IteratorWithIOThrow<UncachedKeyRing> stream = UncachedKeyRing.fromStream(
+ getInstrumentation().getContext().getAssets().open(name));
+
+ ProviderHelper helper = new ProviderHelper(context);
+ while(stream.hasNext()) {
+ UncachedKeyRing ring = stream.next();
+ if (ring.isSecret()) {
+ helper.saveSecretKeyRing(ring, new ProgressScaler());
+ } else {
+ helper.savePublicKeyRing(ring, new ProgressScaler());
+ }
+ }
+
+ }
+
+ public static void copyFiles() throws IOException {
+ File cacheDir = getInstrumentation().getTargetContext().getFilesDir();
+ byte[] buf = new byte[256];
+ for (String filename : FILES) {
+ File outFile = new File(cacheDir, filename);
+ if (outFile.exists()) {
+ continue;
+ }
+ InputStream in = new BufferedInputStream(getInstrumentation().getContext().getAssets().open(filename));
+ OutputStream out = new BufferedOutputStream(new FileOutputStream(outFile));
+ int len;
+ while( (len = in.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ }
+ }
+ }
+
+ public static final String[] FILES = new String[] { "pa.png", "re.png", "ci.png" };
+ public static File[] getImageNames() {
+ File cacheDir = getInstrumentation().getTargetContext().getFilesDir();
+ File[] ret = new File[FILES.length];
+ for (int i = 0; i < ret.length; i++) {
+ ret[i] = new File(cacheDir, FILES[i]);
+ }
+ return ret;
+ }
+
+ public static <T> T pickRandom(T[] haystack) {
+ return haystack[new Random().nextInt(haystack.length)];
+ }
+
+ public static String randomString(int min, int max) {
+ String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789!@#$%^&*()-_=";
+ Random r = new Random();
+ StringBuilder passbuilder = new StringBuilder();
+ // 5% chance for an empty string
+ for(int i = 0, j = r.nextInt(max)+min; i < j; i++) {
+ passbuilder.append(chars.charAt(r.nextInt(chars.length())));
+ }
+ return passbuilder.toString();
+ }
+
+ public static void cleanupForTests(Context context) throws Exception {
+
+ new KeychainDatabase(context).clearDatabase();
+
+ // import these two, make sure they're there
+ importKeysFromResource(context, "x.sec.asc");
+
+ // make sure no passphrases are cached
+ PassphraseCacheService.clearCachedPassphrases(context);
+
+ }
+
+}
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/actions/CustomActions.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/actions/CustomActions.java
new file mode 100644
index 000000000..75197ac9e
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/actions/CustomActions.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
+ *
+ * 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.actions;
+
+
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.view.View;
+
+import com.tokenautocomplete.TokenCompleteTextView;
+import org.hamcrest.Matcher;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
+
+import static android.support.test.InstrumentationRegistry.getTargetContext;
+
+
+public abstract class CustomActions {
+
+ public static ViewAction tokenEncryptViewAddToken(long keyId) throws Exception {
+ CanonicalizedPublicKeyRing ring =
+ new ProviderHelper(getTargetContext()).getCanonicalizedPublicKeyRing(keyId);
+ final Object item = new KeyAdapter.KeyItem(ring);
+
+ return new ViewAction() {
+ @Override
+ public Matcher<View> getConstraints() {
+ return ViewMatchers.isAssignableFrom(TokenCompleteTextView.class);
+ }
+
+ @Override
+ public String getDescription() {
+ return "add completion token";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ ((TokenCompleteTextView) view).addObject(item);
+ }
+ };
+ }
+
+ public static ViewAction tokenViewAddToken(final Object item) {
+ return new ViewAction() {
+ @Override
+ public Matcher<View> getConstraints() {
+ return ViewMatchers.isAssignableFrom(TokenCompleteTextView.class);
+ }
+
+ @Override
+ public String getDescription() {
+ return "add completion token";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ ((TokenCompleteTextView) view).addObject(item);
+ }
+ };
+ }
+
+} \ No newline at end of file
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/actions/OrientationChangeAction.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/actions/OrientationChangeAction.java
new file mode 100644
index 000000000..cdded7d7f
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/actions/OrientationChangeAction.java
@@ -0,0 +1,74 @@
+package org.sufficientlysecure.keychain.actions;
+
+
+import java.util.Collection;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ActivityInfo;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
+import android.support.test.runner.lifecycle.Stage;
+import android.view.View;
+
+import org.hamcrest.Matcher;
+
+import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
+
+public class OrientationChangeAction implements ViewAction {
+ private final int orientation;
+
+ private OrientationChangeAction(int orientation) {
+ this.orientation = orientation;
+ }
+
+ @Override
+ public Matcher<View> getConstraints() {
+ return isRoot();
+ }
+
+ @Override
+ public String getDescription() {
+ return "change orientation to " + orientation;
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ uiController.loopMainThreadUntilIdle();
+
+ final Activity activity = findActivity(view.getContext());
+ if (activity == null){
+ throw new IllegalStateException("Could not find the current activity");
+ }
+
+ activity.setRequestedOrientation(orientation);
+
+ Collection<Activity> resumedActivities = ActivityLifecycleMonitorRegistry
+ .getInstance().getActivitiesInStage(Stage.RESUMED);
+
+ if (resumedActivities.isEmpty()) {
+ throw new RuntimeException("Could not change orientation");
+ }
+ }
+
+ private static Activity findActivity(Context context) {
+ if (context == null)
+ return null;
+ else if (context instanceof Activity)
+ return (Activity) context;
+ else if (context instanceof ContextWrapper)
+ return findActivity(((ContextWrapper) context).getBaseContext());
+
+ return null;
+ }
+
+ public static ViewAction orientationLandscape() {
+ return new OrientationChangeAction(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ }
+
+ public static ViewAction orientationPortrait() {
+ return new OrientationChangeAction(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/BitmapMatcher.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/BitmapMatcher.java
new file mode 100644
index 000000000..c08847065
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/BitmapMatcher.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
+ *
+ * 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/>.
+ *
+ * From the droidcon anroid espresso repository.
+ * https://github.com/xrigau/droidcon-android-espresso/
+ *
+ */
+
+package org.sufficientlysecure.keychain.matcher;
+
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.ImageView;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+
+
+public class BitmapMatcher extends TypeSafeMatcher<View> {
+
+ private final Bitmap mBitmap;
+
+ public BitmapMatcher(Bitmap bitmap) {
+ super(View.class);
+ mBitmap = bitmap;
+ }
+
+ @Override
+ public boolean matchesSafely(View view) {
+ if ( !(view instanceof ImageView) ) {
+ return false;
+ }
+ Drawable drawable = ((ImageView) view).getDrawable();
+ return drawable != null && (drawable instanceof BitmapDrawable)
+ && ((BitmapDrawable) drawable).getBitmap().sameAs(mBitmap);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("with equivalent specified bitmap");
+ }
+
+ public static BitmapMatcher withBitmap(Bitmap bitmap) {
+ return new BitmapMatcher(bitmap);
+ }
+
+}
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/CustomMatchers.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/CustomMatchers.java
new file mode 100644
index 000000000..6713cd237
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/CustomMatchers.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
+ *
+ * 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.matcher;
+
+
+import android.support.annotation.ColorRes;
+import android.support.annotation.IdRes;
+import android.support.test.espresso.matcher.BoundedMatcher;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.ViewAnimator;
+
+import com.nispok.snackbar.Snackbar;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter.KeyItem;
+import org.sufficientlysecure.keychain.ui.widget.EncryptKeyCompletionView;
+
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withParent;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.sufficientlysecure.keychain.matcher.DrawableMatcher.withDrawable;
+
+
+public abstract class CustomMatchers {
+
+ public static Matcher<View> withDisplayedChild(final int child) {
+ return new BoundedMatcher<View, ViewAnimator>(ViewAnimator.class) {
+ public void describeTo(Description description) {
+ description.appendText("with displayed child: " + child);
+ }
+
+ @Override
+ public boolean matchesSafely(ViewAnimator viewAnimator) {
+ return viewAnimator.getDisplayedChild() == child;
+ }
+ };
+ }
+
+ public static Matcher<View> withSnackbarLineColor(@ColorRes final int colorRes) {
+ return new BoundedMatcher<View, Snackbar>(Snackbar.class) {
+ public void describeTo(Description description) {
+ description.appendText("with color resource id: " + colorRes);
+ }
+
+ @Override
+ public boolean matchesSafely(Snackbar snackbar) {
+ return snackbar.getResources().getColor(colorRes) == snackbar.getLineColor();
+ }
+ };
+ }
+
+ public static Matcher<Object> withKeyItemId(final long keyId) {
+ return new BoundedMatcher<Object, KeyItem>(KeyItem.class) {
+ @Override
+ public boolean matchesSafely(KeyItem item) {
+ return item.mKeyId == keyId;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("with key id: " + keyId);
+ }
+ };
+ }
+
+ public static Matcher<View> withKeyToken(@ColorRes final long keyId) {
+ return new BoundedMatcher<View, EncryptKeyCompletionView>(EncryptKeyCompletionView.class) {
+ public void describeTo(Description description) {
+ description.appendText("with key id token: " + keyId);
+ }
+
+ @Override
+ public boolean matchesSafely(EncryptKeyCompletionView tokenView) {
+ for (Object object : tokenView.getObjects()) {
+ if (object instanceof KeyItem && ((KeyItem) object).mKeyId == keyId) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ }
+
+ public static Matcher<View> withRecyclerView(@IdRes int viewId) {
+ return allOf(isAssignableFrom(RecyclerView.class), withId(viewId));
+ }
+
+ public static Matcher<View> isRecyclerItemView(@IdRes int recyclerId, Matcher<View> specificChildMatcher) {
+ return allOf(withParent(withRecyclerView(recyclerId)), specificChildMatcher);
+ }
+
+ public static Matcher<View> withEncryptionStatus(boolean encrypted) {
+
+ if (encrypted) {
+ return allOf(
+ hasDescendant(allOf(
+ withId(R.id.result_encryption_text), withText(R.string.decrypt_result_encrypted))),
+ hasDescendant(allOf(
+ withId(R.id.result_encryption_icon), withDrawable(R.drawable.status_lock_closed_24dp, true)))
+ );
+ } else {
+ return allOf(
+ hasDescendant(allOf(
+ withId(R.id.result_encryption_text), withText(R.string.decrypt_result_not_encrypted))),
+ hasDescendant(allOf(
+ withId(R.id.result_encryption_icon), withDrawable(R.drawable.status_lock_open_24dp, true)))
+ );
+ }
+ }
+
+ public static Matcher<View> withSignatureNone() {
+
+ return allOf(
+ hasDescendant(allOf(
+ withId(R.id.result_signature_text), withText(R.string.decrypt_result_no_signature))),
+ hasDescendant(allOf(
+ withId(R.id.result_signature_icon), withDrawable(R.drawable.status_signature_invalid_cutout_24dp, true))),
+ hasDescendant(allOf(
+ withId(R.id.result_signature_layout), not(isDisplayed())))
+ );
+
+ }
+
+ public static Matcher<View> withSignatureMyKey() {
+
+ return allOf(
+ hasDescendant(allOf(
+ withId(R.id.result_signature_text), withText(R.string.decrypt_result_signature_certified))),
+ hasDescendant(allOf(
+ withId(R.id.result_signature_icon), withDrawable(R.drawable.status_signature_verified_cutout_24dp, true))),
+ hasDescendant(allOf(
+ withId(R.id.result_signature_layout), isDisplayed()))
+ );
+
+ }
+
+}
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/DrawableMatcher.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/DrawableMatcher.java
new file mode 100644
index 000000000..da2ff87d9
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/DrawableMatcher.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 Xavi Rigau <xrigau@gmail.com>
+ * Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
+ *
+ * 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/>.
+ *
+ * From the droidcon anroid espresso repository.
+ * https://github.com/xrigau/droidcon-android-espresso/
+ *
+ */
+
+package org.sufficientlysecure.keychain.matcher;
+
+
+import android.content.res.Resources;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+
+
+public class DrawableMatcher extends TypeSafeMatcher<View> {
+
+ private final int mResourceId;
+ private final boolean mIgnoreFilters;
+
+ public DrawableMatcher(int resourceId, boolean ignoreFilters) {
+ super(View.class);
+ mResourceId = resourceId;
+ mIgnoreFilters = ignoreFilters;
+ }
+
+ private String resourceName = null;
+ private Drawable expectedDrawable = null;
+
+ @Override
+ public boolean matchesSafely(View target) {
+ if (expectedDrawable == null) {
+ loadDrawableFromResources(target.getResources());
+ }
+ if (invalidExpectedDrawable()) {
+ return false;
+ }
+
+ if (target instanceof ImageView) {
+ return hasImage((ImageView) target) || hasBackground(target);
+ }
+ if (target instanceof TextView) {
+ return hasCompoundDrawable((TextView) target) || hasBackground(target);
+ }
+ return hasBackground(target);
+ }
+
+ private void loadDrawableFromResources(Resources resources) {
+ try {
+ expectedDrawable = resources.getDrawable(mResourceId);
+ resourceName = resources.getResourceEntryName(mResourceId);
+ } catch (Resources.NotFoundException ignored) {
+ // view could be from a context unaware of the resource id.
+ }
+ }
+
+ private boolean invalidExpectedDrawable() {
+ return expectedDrawable == null;
+ }
+
+ private boolean hasImage(ImageView target) {
+ return isSameDrawable(target.getDrawable());
+ }
+
+ private boolean hasCompoundDrawable(TextView target) {
+ for (Drawable drawable : target.getCompoundDrawables()) {
+ if (isSameDrawable(drawable)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean hasBackground(View target) {
+ return isSameDrawable(target.getBackground());
+ }
+
+ private boolean isSameDrawable(Drawable drawable) {
+ if (drawable == null) {
+ return false;
+ }
+ // if those are both bitmap drawables, compare their bitmaps (ignores color filters, which is what we want!)
+ if (mIgnoreFilters && drawable instanceof BitmapDrawable && expectedDrawable instanceof BitmapDrawable) {
+ return ((BitmapDrawable) drawable).getBitmap().sameAs((((BitmapDrawable) expectedDrawable).getBitmap()));
+ }
+ return expectedDrawable.getConstantState().equals(drawable.getConstantState());
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("with drawable from resource id: ");
+ description.appendValue(mResourceId);
+ if (resourceName != null) {
+ description.appendText("[");
+ description.appendText(resourceName);
+ description.appendText("]");
+ }
+ }
+
+ public static DrawableMatcher withDrawable(int resourceId, boolean ignoreFilters) {
+ return new DrawableMatcher(resourceId, ignoreFilters);
+ }
+ public static DrawableMatcher withDrawable(int resourceId) {
+ return new DrawableMatcher(resourceId, true);
+ }
+
+}
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/EditTextMatchers.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/EditTextMatchers.java
index 7f2a7953b..a1df7912f 100644
--- a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/EditTextMatchers.java
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/matcher/EditTextMatchers.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
*
* 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
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceTest.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceTest.java
new file mode 100644
index 000000000..aaf7499b7
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceTest.java
@@ -0,0 +1,171 @@
+package org.sufficientlysecure.keychain.remote;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.IBinder;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ServiceTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.widget.AdapterView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openintents.openpgp.IOpenPgpService;
+import org.openintents.openpgp.util.OpenPgpApi;
+import org.sufficientlysecure.keychain.R;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.typeText;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.sufficientlysecure.keychain.TestHelpers.cleanupForTests;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withKeyItemId;
+
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class OpenPgpServiceTest {
+
+ @Rule
+ public final ServiceTestRule mServiceRule = new ServiceTestRule();
+
+ OpenPgpApi mApi;
+
+ @Before
+ public void setUp() throws Exception {
+
+ cleanupForTests(InstrumentationRegistry.getTargetContext());
+
+ Intent serviceIntent = new Intent(InstrumentationRegistry.getTargetContext(), OpenPgpService.class);
+ IBinder binder = mServiceRule.bindService(serviceIntent);
+
+ mApi = new OpenPgpApi(InstrumentationRegistry.getTargetContext(),
+ IOpenPgpService.Stub.asInterface(binder));
+
+ }
+
+ @Test
+ public void testStuff() throws Exception {
+
+ // TODO why does this not ask for general usage permissions?!
+
+ {
+ Intent intent = new Intent();
+ intent.setAction(OpenPgpApi.ACTION_ENCRYPT);
+ intent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
+ intent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, new long[] { 0x9D604D2F310716A3L });
+
+ ByteArrayInputStream is = new ByteArrayInputStream("swag".getBytes());
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ Intent result = mApi.executeApi(intent, is, os);
+
+ assertThat("result is pending accept",
+ result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR),
+ is(OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED));
+
+ PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
+ pi.send();
+
+ onView(withText(R.string.api_register_allow)).perform(click());
+
+ }
+
+ byte[] ciphertext;
+ {
+ Intent intent = new Intent();
+ intent.setAction(OpenPgpApi.ACTION_ENCRYPT);
+ intent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
+ intent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, new long[] { 0x9D604D2F310716A3L });
+
+ ByteArrayInputStream is = new ByteArrayInputStream("swag".getBytes());
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ Intent result = mApi.executeApi(intent, is, os);
+
+ assertThat("result is ok",
+ result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR),
+ is(OpenPgpApi.RESULT_CODE_SUCCESS));
+
+ ciphertext = os.toByteArray();
+ }
+
+ { // decrypt
+ Intent intent = new Intent();
+ intent.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
+
+ ByteArrayInputStream is = new ByteArrayInputStream(ciphertext);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ Intent result = mApi.executeApi(intent, is, os);
+
+ assertThat("result is pending input",
+ result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR),
+ is(OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED));
+
+ PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
+ pi.send();
+
+ onData(withKeyItemId(0x9D604D2F310716A3L))
+ .inAdapterView(isAssignableFrom(AdapterView.class))
+ .perform(click());
+
+ onView(withText(R.string.api_settings_save)).perform(click());
+
+ // unfortunately, getting the activity result from the
+
+ }
+
+ { // decrypt again, this time pending passphrase
+ Intent intent = new Intent();
+ intent.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
+
+ ByteArrayInputStream is = new ByteArrayInputStream(ciphertext);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ Intent result = mApi.executeApi(intent, is, os);
+
+ assertThat("result is pending passphrase",
+ result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR),
+ is(OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED));
+
+ PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
+ pi.send();
+
+ onView(withId(R.id.passphrase_passphrase)).perform(typeText("x"));
+ onView(withText(R.string.btn_unlock)).perform(click());
+ }
+
+ { // decrypt again, NOW it should work with passphrase cached =)
+ Intent intent = new Intent();
+ intent.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
+
+ ByteArrayInputStream is = new ByteArrayInputStream(ciphertext);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ Intent result = mApi.executeApi(intent, is, os);
+
+ assertThat("result is pending passphrase",
+ result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR),
+ is(OpenPgpApi.RESULT_CODE_SUCCESS));
+
+ byte[] plaintext = os.toByteArray();
+ assertThat("decrypted plaintext matches plaintext", new String(plaintext), is("swag"));
+
+ }
+
+ }
+
+}
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/AsymmetricFileOperationTests.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/AsymmetricFileOperationTests.java
new file mode 100644
index 000000000..5570b627f
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/AsymmetricFileOperationTests.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+
+import java.io.File;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.Instrumentation.ActivityResult;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.support.test.espresso.intent.Intents;
+import android.support.test.espresso.intent.rule.IntentsTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.widget.AdapterView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.TestHelpers;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
+import static android.support.test.espresso.Espresso.pressBack;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.typeText;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasCategories;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasExtra;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasExtraWithKey;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasType;
+import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.is;
+import static org.sufficientlysecure.keychain.TestHelpers.checkSnackbar;
+import static org.sufficientlysecure.keychain.TestHelpers.getImageNames;
+import static org.sufficientlysecure.keychain.TestHelpers.importKeysFromResource;
+import static org.sufficientlysecure.keychain.TestHelpers.pickRandom;
+import static org.sufficientlysecure.keychain.TestHelpers.randomString;
+import static org.sufficientlysecure.keychain.actions.CustomActions.tokenEncryptViewAddToken;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.isRecyclerItemView;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withDisplayedChild;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withEncryptionStatus;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withKeyItemId;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withSignatureMyKey;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withSignatureNone;
+
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class AsymmetricFileOperationTests {
+
+ @Rule
+ public final IntentsTestRule<MainActivity> mActivity
+ = new IntentsTestRule<MainActivity>(MainActivity.class) {
+ @Override
+ protected Intent getActivityIntent() {
+ Intent intent = super.getActivityIntent();
+ intent.putExtra(MainActivity.EXTRA_SKIP_FIRST_TIME, true);
+ intent.putExtra(MainActivity.EXTRA_INIT_FRAG, MainActivity.ID_ENCRYPT_DECRYPT);
+ return intent;
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ Activity activity = mActivity.getActivity();
+
+ TestHelpers.copyFiles();
+
+ // import these two, make sure they're there
+ importKeysFromResource(activity, "x.sec.asc");
+
+ // make sure no passphrases are cached
+ PassphraseCacheService.clearCachedPassphrases(activity);
+ }
+
+ @Test
+ public void testFileSaveEncryptDecrypt() throws Exception {
+
+ // navigate to 'encrypt text'
+ onView(withId(R.id.encrypt_files)).perform(click());
+
+ File file = pickRandom(getImageNames());
+ File outputFile = new File(getInstrumentation().getTargetContext().getFilesDir(), "output-token.gpg");
+
+ { // encrypt
+
+ // the EncryptKeyCompletionView is tested individually
+ onView(withId(R.id.recipient_list)).perform(tokenEncryptViewAddToken(0x9D604D2F310716A3L));
+
+ handleAddFileIntent(file);
+ onView(withId(R.id.file_list_entry_add)).perform(click());
+
+ handleSaveEncryptedFileIntent(outputFile);
+ onView(withId(R.id.encrypt_save)).perform(click());
+
+ assertThat("output file has been written", true, is(outputFile.exists()));
+
+ }
+
+ // go to decrypt from clipboard view
+ pressBack();
+
+ handleOpenFileIntentKitKat(outputFile);
+ onView(withId(R.id.decrypt_files)).perform(click());
+
+ { // decrypt
+ onView(withId(R.id.passphrase_passphrase)).perform(typeText("x"));
+ onView(withText(R.string.btn_unlock)).perform(click());
+
+ onView(isRecyclerItemView(R.id.decrypted_files_list,
+ hasDescendant(withText(file.getName()))))
+ .check(matches(allOf(withEncryptionStatus(true), withSignatureNone())));
+ }
+
+ { // delete original file
+
+ // open context menu
+ onView(allOf(isDescendantOfA(isRecyclerItemView(R.id.decrypted_files_list,
+ hasDescendant(withText(file.getName())))),
+ withId(R.id.context_menu))).perform(click());
+
+ // delete file
+ onView(withText(R.string.btn_delete_original)).perform(click());
+
+ checkSnackbar(Style.OK, R.string.file_delete_ok);
+ assertThat("output file has been deleted", false, is(outputFile.exists()));
+
+ // open context menu
+ onView(allOf(isDescendantOfA(isRecyclerItemView(R.id.decrypted_files_list,
+ hasDescendant(withText(file.getName())))),
+ withId(R.id.context_menu))).perform(click());
+
+ // delete file
+ onView(withText(R.string.btn_delete_original)).perform(click());
+
+ checkSnackbar(Style.WARN, R.string.file_delete_none);
+
+ }
+
+ { // save file (*after* deletion~)
+
+ // open context menu
+ onView(allOf(isDescendantOfA(isRecyclerItemView(R.id.decrypted_files_list,
+ hasDescendant(withText(file.getName())))),
+ withId(R.id.context_menu))).perform(click());
+
+ File savedFile =
+ new File(getInstrumentation().getTargetContext().getFilesDir(), "vo.png");
+ handleSaveDecryptedFileIntent(savedFile, file.getName());
+
+ // save decrypted content
+ onView(withText(R.string.btn_save_file)).perform(click());
+
+ checkSnackbar(Style.OK, R.string.file_saved);
+ assertThat("decrypted file has been saved", true, is(savedFile.exists()));
+
+ // cleanup
+ // noinspection ResultOfMethodCallIgnored
+ file.delete();
+
+ }
+
+ }
+
+ private void handleAddFileIntent(File file) {
+ if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
+ handleAddFileIntentKitKat(file);
+ } else {
+ handleAddFileIntentOlder(file);
+ }
+ }
+
+ @TargetApi(VERSION_CODES.KITKAT)
+ private void handleAddFileIntentKitKat(File file) {
+ Intent data = new Intent();
+ data.setData(Uri.fromFile(file));
+
+ Intents.intending(allOf(
+ hasAction(Intent.ACTION_OPEN_DOCUMENT),
+ hasType("*/*"),
+ hasCategories(hasItem(Intent.CATEGORY_OPENABLE)),
+ hasExtraWithKey(Intent.EXTRA_ALLOW_MULTIPLE)
+ )).respondWith(
+ new ActivityResult(Activity.RESULT_OK, data)
+ );
+ }
+
+ private void handleAddFileIntentOlder(File file) {
+ Intent data = new Intent();
+ data.setData(Uri.fromFile(file));
+
+ Intents.intending(allOf(
+ hasAction(Intent.ACTION_GET_CONTENT),
+ hasType("*/*"),
+ hasCategories(hasItem(Intent.CATEGORY_OPENABLE))
+ )).respondWith(
+ new ActivityResult(Activity.RESULT_OK, data)
+ );
+ }
+
+ @TargetApi(VERSION_CODES.KITKAT)
+ private void handleSaveDecryptedFileIntent(File file, String expectedTitle) {
+ Intent data = new Intent();
+ data.setData(Uri.fromFile(file));
+
+ Intents.intending(allOf(
+ hasAction(Intent.ACTION_CREATE_DOCUMENT),
+ hasExtra("android.content.extra.SHOW_ADVANCED", true),
+ hasExtra(Intent.EXTRA_TITLE, expectedTitle),
+ hasCategories(hasItem(Intent.CATEGORY_OPENABLE))
+ )).respondWith(
+ new ActivityResult(Activity.RESULT_OK, data)
+ );
+ }
+
+ @TargetApi(VERSION_CODES.KITKAT)
+ private void handleSaveEncryptedFileIntent(File file) {
+
+ try {
+ //noinspection ResultOfMethodCallIgnored
+ file.delete();
+ } catch (Exception e) {
+ // nvm
+ }
+
+ Intent data = new Intent();
+ data.setData(Uri.fromFile(file));
+
+ Intents.intending(allOf(
+ hasAction(Intent.ACTION_CREATE_DOCUMENT),
+ hasType("*/*"),
+ hasExtra("android.content.extra.SHOW_ADVANCED", true),
+ hasCategories(hasItem(Intent.CATEGORY_OPENABLE))
+ )).respondWith(
+ new ActivityResult(Activity.RESULT_OK, data)
+ );
+ }
+
+ @TargetApi(VERSION_CODES.KITKAT)
+ private void handleOpenFileIntentKitKat(File file) {
+ Intent data = new Intent();
+ data.setData(Uri.fromFile(file));
+
+ Intents.intending(allOf(
+ hasAction(Intent.ACTION_OPEN_DOCUMENT),
+ hasType("*/*"),
+ hasCategories(hasItem(Intent.CATEGORY_OPENABLE))
+ // hasExtraWithKey(Intent.EXTRA_ALLOW_MULTIPLE)
+ )).respondWith(
+ new ActivityResult(Activity.RESULT_OK, data)
+ );
+ }
+
+ @Test
+ public void testSignVerify() throws Exception {
+
+ String cleartext = randomString(10, 30);
+
+ // navigate to 'encrypt text'
+ onView(withId(R.id.encrypt_text)).perform(click());
+
+ { // sign
+
+ onView(withId(R.id.encrypt_copy)).perform(click());
+ checkSnackbar(Style.ERROR, R.string.error_empty_text);
+
+ onView(withId(R.id.result_signature_icon)).check(matches(withDisplayedChild(0)));
+ onView(withId(R.id.sign)).perform(click());
+ onData(withKeyItemId(0x9D604D2F310716A3L))
+ .inAdapterView(isAssignableFrom(AdapterView.class))
+ .perform(click());
+ onView(withId(R.id.result_signature_icon)).check(matches(withDisplayedChild(1)));
+
+ onView(withId(R.id.encrypt_text_text)).perform(typeText(cleartext));
+
+ onView(withId(R.id.encrypt_copy)).perform(click());
+
+ onView(withId(R.id.passphrase_passphrase)).perform(typeText("x"));
+ onView(withText(R.string.btn_unlock)).perform(click());
+
+ checkSnackbar(Style.OK, R.string.msg_se_success);
+
+ }
+
+ // go to decrypt from clipboard view
+ pressBack();
+
+ onView(withId(R.id.decrypt_from_clipboard)).perform(click());
+
+ { // decrypt
+
+ onView(isRecyclerItemView(R.id.decrypted_files_list,
+ hasDescendant(withText(R.string.filename_unknown))))
+ .check(matches(allOf(withEncryptionStatus(false), withSignatureMyKey())));
+
+ // open context menu
+ onView(allOf(isDescendantOfA(isRecyclerItemView(R.id.decrypted_files_list,
+ hasDescendant(withText(R.string.filename_unknown)))),
+ withId(R.id.context_menu))).perform(click());
+
+ // check if log looks ok
+ onView(withText(R.string.snackbar_details)).perform(click());
+ onView(withText(R.string.msg_dc_clear_signature_ok)).check(matches(isDisplayed()));
+ pressBack();
+
+ }
+
+ }
+
+ @Test
+ public void testGeneralErrorHandling() throws Exception {
+
+ // navigate to encrypt files fragment
+ onView(withId(R.id.encrypt_files)).perform(click());
+
+ File[] files = getImageNames();
+
+ { // encrypt screen
+
+ onView(withId(R.id.encrypt_share)).perform(click());
+ checkSnackbar(Style.ERROR, R.string.error_no_file_selected);
+
+ handleAddFileIntent(files[0]);
+ onView(withId(R.id.file_list_entry_add)).perform(click());
+
+ handleAddFileIntent(files[1]);
+ onView(withId(R.id.file_list_entry_add)).perform(click());
+
+ onView(withId(R.id.encrypt_share)).perform(click());
+ checkSnackbar(Style.ERROR, R.string.select_encryption_key);
+
+ onView(withId(R.id.sign)).perform(click());
+ onData(withKeyItemId(0x9D604D2F310716A3L))
+ .inAdapterView(isAssignableFrom(AdapterView.class))
+ .perform(click());
+
+ onView(withId(R.id.encrypt_share)).perform(click());
+ checkSnackbar(Style.ERROR, R.string.error_detached_signature);
+
+ // the EncryptKeyCompletionView is tested individually
+ onView(withId(R.id.recipient_list)).perform(tokenEncryptViewAddToken(0x9D604D2F310716A3L));
+
+ onView(withId(R.id.encrypt_save)).perform(click());
+ checkSnackbar(Style.ERROR, R.string.error_multi_files);
+
+ openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext());
+ onView(withText(R.string.btn_copy_encrypted_signed)).perform(click());
+ checkSnackbar(Style.ERROR, R.string.error_multi_clipboard);
+
+ }
+
+ }
+
+}
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/AsymmetricTextOperationTests.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/AsymmetricTextOperationTests.java
new file mode 100644
index 000000000..cb3d2cb17
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/AsymmetricTextOperationTests.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+
+import android.app.Activity;
+import android.content.Intent;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.widget.AdapterView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
+import org.sufficientlysecure.keychain.util.FileHelper;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.Espresso.pressBack;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.typeText;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.sufficientlysecure.keychain.TestHelpers.checkSnackbar;
+import static org.sufficientlysecure.keychain.TestHelpers.importKeysFromResource;
+import static org.sufficientlysecure.keychain.TestHelpers.randomString;
+import static org.sufficientlysecure.keychain.actions.CustomActions.tokenEncryptViewAddToken;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.isRecyclerItemView;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withDisplayedChild;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withEncryptionStatus;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withKeyItemId;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withSignatureMyKey;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withSignatureNone;
+
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class AsymmetricTextOperationTests {
+
+ @Rule
+ public final ActivityTestRule<MainActivity> mActivity
+ = new ActivityTestRule<MainActivity>(MainActivity.class) {
+ @Override
+ protected Intent getActivityIntent() {
+ Intent intent = super.getActivityIntent();
+ intent.putExtra(MainActivity.EXTRA_SKIP_FIRST_TIME, true);
+ intent.putExtra(MainActivity.EXTRA_INIT_FRAG, MainActivity.ID_ENCRYPT_DECRYPT);
+ return intent;
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ Activity activity = mActivity.getActivity();
+
+ // import these two, make sure they're there
+ importKeysFromResource(activity, "x.sec.asc");
+
+ // make sure no passphrases are cached
+ PassphraseCacheService.clearCachedPassphrases(activity);
+ }
+
+ @Test
+ public void testTextEncryptDecryptFromToken() throws Exception {
+
+ // navigate to 'encrypt text'
+ onView(withId(R.id.encrypt_text)).perform(click());
+
+ String cleartext = randomString(10, 30);
+
+ { // encrypt
+
+ // the EncryptKeyCompletionView is tested individually
+ onView(withId(R.id.result_encryption_icon)).check(matches(withDisplayedChild(0)));
+ onView(withId(R.id.recipient_list)).perform(tokenEncryptViewAddToken(0x9D604D2F310716A3L));
+ onView(withId(R.id.result_encryption_icon)).check(matches(withDisplayedChild(1)));
+
+ onView(withId(R.id.encrypt_text_text)).perform(typeText(cleartext));
+
+ onView(withId(R.id.encrypt_copy)).perform(click());
+ }
+
+ // go to decrypt from clipboard view
+ pressBack();
+ onView(withId(R.id.decrypt_from_clipboard)).perform(click());
+
+ { // decrypt
+ onView(withId(R.id.passphrase_passphrase)).perform(typeText("x"));
+ onView(withText(R.string.btn_unlock)).perform(click());
+
+ onView(isRecyclerItemView(R.id.decrypted_files_list,
+ hasDescendant(withText(R.string.filename_unknown_text))))
+ .check(matches(allOf(
+ hasDescendant(withText(FileHelper.readableFileSize(cleartext.length()))),
+ withEncryptionStatus(true),
+ withSignatureNone()
+ )));
+
+ }
+
+ }
+
+ @Test
+ public void testSignVerify() throws Exception {
+
+ String cleartext = randomString(10, 30);
+
+ // navigate to 'encrypt text'
+ onView(withId(R.id.encrypt_text)).perform(click());
+
+ { // sign
+
+ onView(withId(R.id.encrypt_copy)).perform(click());
+ checkSnackbar(Style.ERROR, R.string.error_empty_text);
+
+ onView(withId(R.id.result_signature_icon)).check(matches(withDisplayedChild(0)));
+ onView(withId(R.id.sign)).perform(click());
+ onData(withKeyItemId(0x9D604D2F310716A3L))
+ .inAdapterView(isAssignableFrom(AdapterView.class))
+ .perform(click());
+ onView(withId(R.id.result_signature_icon)).check(matches(withDisplayedChild(1)));
+
+ onView(withId(R.id.encrypt_text_text)).perform(typeText(cleartext));
+
+ onView(withId(R.id.encrypt_copy)).perform(click());
+
+ onView(withId(R.id.passphrase_passphrase)).perform(typeText("x"));
+ onView(withText(R.string.btn_unlock)).perform(click());
+
+ checkSnackbar(Style.OK, R.string.msg_se_success);
+
+ }
+
+ // go to decrypt from clipboard view
+ pressBack();
+
+ onView(withId(R.id.decrypt_from_clipboard)).perform(click());
+
+ { // decrypt
+
+ onView(isRecyclerItemView(R.id.decrypted_files_list,
+ hasDescendant(withText(R.string.filename_unknown))))
+ .check(matches(allOf(withEncryptionStatus(false), withSignatureMyKey())));
+
+ // open context menu
+ onView(allOf(isDescendantOfA(isRecyclerItemView(R.id.decrypted_files_list,
+ hasDescendant(withText(R.string.filename_unknown)))),
+ withId(R.id.context_menu))).perform(click());
+
+ // check if log looks ok
+ onView(withText(R.string.snackbar_details)).perform(click());
+ onView(withText(R.string.msg_dc_clear_signature_ok)).check(matches(isDisplayed()));
+ pressBack();
+
+ }
+
+ }
+
+}
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/CreateKeyActivityTest.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/CreateKeyActivityTest.java
index c3741fdef..cf8e7ae12 100644
--- a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/CreateKeyActivityTest.java
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/CreateKeyActivityTest.java
@@ -15,17 +15,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain;
+package org.sufficientlysecure.keychain.ui;
+
+import android.support.test.espresso.matcher.ViewMatchers;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
import android.text.method.HideReturnsTransformationMethod;
import android.text.method.PasswordTransformationMethod;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
+import org.sufficientlysecure.keychain.R;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
@@ -44,6 +47,7 @@ import static org.sufficientlysecure.keychain.matcher.EditTextMatchers.withError
import static org.sufficientlysecure.keychain.matcher.EditTextMatchers.withTransformationMethod;
@RunWith(AndroidJUnit4.class)
+@LargeTest
public class CreateKeyActivityTest {
public static final String SAMPLE_NAME = "Sample Name";
@@ -52,12 +56,16 @@ public class CreateKeyActivityTest {
public static final String SAMPLE_PASSWORD = "sample_password";
@Rule
- public ActivityTestRule<CreateKeyActivity> mActivityRule = new ActivityTestRule<>(CreateKeyActivity.class);
+ public final ActivityTestRule<CreateKeyActivity> mActivity
+ = new ActivityTestRule<>(CreateKeyActivity.class);
@Test
public void testCreateMyKey() {
+
+ mActivity.getActivity();
+
// Clicks create my key
- onView(withId(R.id.create_key_create_key_button))
+ onView(ViewMatchers.withId(R.id.create_key_create_key_button))
.perform(click());
// Clicks next with empty name
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/EditKeyTest.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/EditKeyTest.java
new file mode 100644
index 000000000..13583818d
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/EditKeyTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+
+import android.app.Activity;
+import android.content.Intent;
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.widget.AdapterView;
+
+import org.junit.FixMethodOrder;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.sufficientlysecure.keychain.TestHelpers.checkSnackbar;
+import static org.sufficientlysecure.keychain.TestHelpers.importKeysFromResource;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withKeyItemId;
+
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class EditKeyTest {
+
+ @Rule
+ public final ActivityTestRule<MainActivity> mActivity
+ = new ActivityTestRule<MainActivity>(MainActivity.class) {
+ @Override
+ protected Intent getActivityIntent() {
+ Intent intent = super.getActivityIntent();
+ intent.putExtra(MainActivity.EXTRA_SKIP_FIRST_TIME, true);
+ return intent;
+ }
+ };
+
+ @Test
+ public void test01Edit() throws Exception {
+ Activity activity = mActivity.getActivity();
+
+ new KeychainDatabase(activity).clearDatabase();
+
+ // import key for testing, get a stable initial state
+ importKeysFromResource(activity, "x.sec.asc");
+
+ // navigate to edit key dialog
+ onData(withKeyItemId(0x9D604D2F310716A3L))
+ .inAdapterView(allOf(isAssignableFrom(AdapterView.class),
+ isDescendantOfA(ViewMatchers.withId(R.id.key_list_list))))
+ .perform(click());
+ onView(withId(R.id.menu_key_view_edit)).perform(click());
+
+ // no-op should yield snackbar
+ onView(withText(R.string.btn_save)).perform(click());
+ checkSnackbar(Style.ERROR, R.string.msg_mf_error_noop);
+
+ }
+
+
+}
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/MiscCryptOperationTests.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/MiscCryptOperationTests.java
new file mode 100644
index 000000000..9b26dfb15
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/MiscCryptOperationTests.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+
+import java.io.File;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.Instrumentation.ActivityResult;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.support.test.espresso.intent.Intents;
+import android.support.test.espresso.intent.rule.IntentsTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.widget.AdapterView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.TestHelpers;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
+import org.sufficientlysecure.keychain.util.Preferences;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
+import static android.support.test.espresso.Espresso.pressBack;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasCategories;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasType;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.hasSibling;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isChecked;
+import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static android.support.test.espresso.matcher.ViewMatchers.isNotChecked;
+import static android.support.test.espresso.matcher.ViewMatchers.withChild;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.sufficientlysecure.keychain.TestHelpers.checkSnackbar;
+import static org.sufficientlysecure.keychain.TestHelpers.dismissSnackbar;
+import static org.sufficientlysecure.keychain.TestHelpers.getImageNames;
+import static org.sufficientlysecure.keychain.TestHelpers.importKeysFromResource;
+import static org.sufficientlysecure.keychain.TestHelpers.pickRandom;
+import static org.sufficientlysecure.keychain.TestHelpers.randomString;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.isRecyclerItemView;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withDisplayedChild;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withKeyItemId;
+import static org.sufficientlysecure.keychain.matcher.DrawableMatcher.withDrawable;
+
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class MiscCryptOperationTests {
+
+ @Rule
+ public final IntentsTestRule<MainActivity> mActivityRule
+ = new IntentsTestRule<MainActivity>(MainActivity.class) {
+ @Override
+ protected Intent getActivityIntent() {
+ Intent intent = super.getActivityIntent();
+ intent.putExtra(MainActivity.EXTRA_SKIP_FIRST_TIME, true);
+ intent.putExtra(MainActivity.EXTRA_INIT_FRAG, MainActivity.ID_ENCRYPT_DECRYPT);
+ return intent;
+ }
+ };
+ private Activity mActivity;
+
+ @Before
+ public void setUp() throws Exception {
+ // clear dis shit
+ Preferences.getPreferences(getInstrumentation().getTargetContext()).clear();
+
+ mActivity = mActivityRule.getActivity();
+
+ TestHelpers.copyFiles();
+
+ // import these two, make sure they're there
+ importKeysFromResource(mActivity, "x.sec.asc");
+
+ // make sure no passphrases are cached
+ PassphraseCacheService.clearCachedPassphrases(mActivity);
+ }
+
+ @Test
+ public void testDecryptNonPgpFile() throws Exception {
+
+ // decrypt any non-pgp file
+ File file = pickRandom(getImageNames());
+ handleOpenFileIntentKitKat(file);
+ onView(withId(R.id.decrypt_files)).perform(click());
+
+ { // decrypt
+
+ // open context menu
+ onView(allOf(isDescendantOfA(isRecyclerItemView(R.id.decrypted_files_list,
+ hasDescendant(allOf(
+ hasDescendant(withDrawable(R.drawable.status_signature_invalid_cutout_24dp, true)),
+ hasDescendant(withText(R.string.msg_dc_error_invalid_data)))))),
+ withId(R.id.result_error_log))).perform(click());
+
+ }
+
+ }
+
+ @Test
+ public void testDecryptEmptySelection() throws Exception {
+
+ // decrypt any non-pgp file
+ handleOpenFileEmptyKitKat();
+ onView(withId(R.id.decrypt_files)).perform(click());
+
+ checkSnackbar(Style.ERROR, R.string.no_file_selected);
+
+ }
+
+ @Test
+ public void testDecryptEmptyClipboard() throws Exception {
+
+ // decrypt any non-pgp file
+ ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE);
+ clipboard.setPrimaryClip(ClipData.newPlainText("", ""));
+
+ onView(withId(R.id.decrypt_from_clipboard)).perform(click());
+ checkSnackbar(Style.ERROR, R.string.error_clipboard_empty);
+
+ }
+
+ @Test
+ public void testDecryptNonPgpClipboard() throws Exception {
+
+ // decrypt any non-pgp file
+ ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clip = ClipData.newPlainText(Constants.CLIPBOARD_LABEL, randomString(0, 50));
+ clipboard.setPrimaryClip(clip);
+ onView(withId(R.id.decrypt_from_clipboard)).perform(click());
+
+ { // decrypt
+
+ // open context menu
+ onView(allOf(isDescendantOfA(isRecyclerItemView(R.id.decrypted_files_list,
+ hasDescendant(allOf(
+ hasDescendant(withDrawable(R.drawable.status_signature_invalid_cutout_24dp, true)),
+ hasDescendant(withText(R.string.msg_dc_error_invalid_data)))))),
+ withId(R.id.result_error_log))).perform(click());
+
+ }
+
+ }
+
+ @TargetApi(VERSION_CODES.KITKAT)
+ private void handleOpenFileEmptyKitKat() {
+ Intent data = new Intent();
+ data.setData(null);
+
+ Intents.intending(allOf(
+ hasAction(Intent.ACTION_OPEN_DOCUMENT),
+ hasType("*/*"),
+ hasCategories(hasItem(Intent.CATEGORY_OPENABLE))
+ // hasExtraWithKey(Intent.EXTRA_ALLOW_MULTIPLE)
+ )).respondWith(
+ new ActivityResult(Activity.RESULT_OK, data)
+ );
+ }
+
+ @TargetApi(VERSION_CODES.KITKAT)
+ private void handleOpenFileIntentKitKat(File file) {
+ Intent data = new Intent();
+ data.setData(Uri.fromFile(file));
+
+ Intents.intending(allOf(
+ hasAction(Intent.ACTION_OPEN_DOCUMENT),
+ hasType("*/*"),
+ hasCategories(hasItem(Intent.CATEGORY_OPENABLE))
+ // hasExtraWithKey(Intent.EXTRA_ALLOW_MULTIPLE)
+ )).respondWith(
+ new ActivityResult(Activity.RESULT_OK, data)
+ );
+ }
+
+ @Test
+ public void testEncryptTokenFromKeyView() throws Exception {
+
+ // navigate to edit key dialog
+ onData(withKeyItemId(0x9D604D2F310716A3L))
+ .inAdapterView(allOf(isAssignableFrom(AdapterView.class),
+ isDescendantOfA(withId(R.id.key_list_list))))
+ .perform(click());
+ onView(withId(R.id.view_key_action_encrypt_text)).perform(click());
+
+ // make sure the encrypt is correctly set
+ onView(withId(R.id.result_encryption_icon)).check(matches(withDisplayedChild(1)));
+ // TODO check token id
+
+ }
+
+ @Test
+ public void testMenuSaveDefault() throws Exception {
+
+ onView(withId(R.id.encrypt_files)).perform(click());
+
+ { // save checked options
+
+ openActionBarOverflowOrOptionsMenu(mActivity);
+
+ // check initial button states
+ onView(allOf(withId(R.id.checkbox),
+ hasSibling(withChild(withText(R.string.label_delete_after_encryption)))))
+ .check(matches(isNotChecked()));
+ onView(allOf(withId(R.id.checkbox), hasSibling(withChild(withText(R.string.label_enable_compression)))))
+ .check(matches(isChecked()));
+ onView(allOf(withId(R.id.checkbox), hasSibling(withChild(withText(R.string.label_encrypt_filenames)))))
+ .check(matches(isChecked()));
+ onView(allOf(withId(R.id.checkbox), hasSibling(withChild(withText(R.string.label_file_ascii_armor)))))
+ .check(matches(isNotChecked()));
+
+ // press some buttons
+
+ onView(withText(R.string.label_enable_compression)).perform(click());
+ checkSnackbar(Style.OK, R.string.snack_compression_off);
+ onView(withText(R.string.btn_save_default)).perform(click());
+ checkSnackbar(Style.OK, R.string.btn_saved);
+ dismissSnackbar();
+
+ openActionBarOverflowOrOptionsMenu(mActivity);
+ onView(withText(R.string.label_encrypt_filenames)).perform(click());
+ checkSnackbar(Style.OK, R.string.snack_encrypt_filenames_off);
+ onView(withText(R.string.btn_save_default)).perform(click());
+ checkSnackbar(Style.OK, R.string.btn_saved);
+ dismissSnackbar();
+
+ openActionBarOverflowOrOptionsMenu(mActivity);
+ onView(withText(R.string.label_file_ascii_armor)).perform(click());
+ checkSnackbar(Style.OK, R.string.snack_armor_on);
+ onView(withText(R.string.btn_save_default)).perform(click());
+ checkSnackbar(Style.OK, R.string.btn_saved);
+ dismissSnackbar();
+
+ }
+
+ pressBack();
+ onView(withId(R.id.encrypt_files)).perform(click());
+
+ { // save checked options
+
+ openActionBarOverflowOrOptionsMenu(mActivity);
+
+ // check initial button states (as saved from before!)
+ onView(allOf(withId(R.id.checkbox),
+ hasSibling(withChild(withText(R.string.label_delete_after_encryption)))))
+ .check(matches(isNotChecked()));
+ onView(allOf(withId(R.id.checkbox), hasSibling(withChild(withText(R.string.label_enable_compression)))))
+ .check(matches(isNotChecked()));
+ onView(allOf(withId(R.id.checkbox), hasSibling(withChild(withText(R.string.label_encrypt_filenames)))))
+ .check(matches(isNotChecked()));
+ onView(allOf(withId(R.id.checkbox), hasSibling(withChild(withText(R.string.label_file_ascii_armor)))))
+ .check(matches(isChecked()));
+
+ }
+
+ }
+
+}
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/SymmetricTextOperationTests.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/SymmetricTextOperationTests.java
new file mode 100644
index 000000000..3a34f15be
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/SymmetricTextOperationTests.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+
+import android.app.Activity;
+import android.app.Instrumentation.ActivityResult;
+import android.content.Intent;
+import android.support.test.espresso.intent.rule.IntentsTestRule;
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.FixMethodOrder;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
+import static android.support.test.espresso.Espresso.pressBack;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.typeText;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.contrib.DrawerActions.openDrawer;
+import static android.support.test.espresso.intent.Intents.intended;
+import static android.support.test.espresso.intent.Intents.intending;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasData;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasExtra;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasExtraWithKey;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasFlags;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasType;
+import static android.support.test.espresso.intent.matcher.UriMatchers.hasHost;
+import static android.support.test.espresso.intent.matcher.UriMatchers.hasScheme;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.Matchers.equalTo;
+import static org.sufficientlysecure.keychain.TestHelpers.checkSnackbar;
+import static org.sufficientlysecure.keychain.TestHelpers.randomString;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.isRecyclerItemView;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withEncryptionStatus;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withSignatureNone;
+
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class SymmetricTextOperationTests {
+
+ public static final String PASSPHRASE = randomString(5, 20);
+
+ @Rule
+ public final IntentsTestRule<MainActivity> mActivity
+ = new IntentsTestRule<MainActivity>(MainActivity.class) {
+ @Override
+ protected Intent getActivityIntent() {
+ Intent intent = super.getActivityIntent();
+ intent.putExtra(MainActivity.EXTRA_SKIP_FIRST_TIME, true);
+ intent.putExtra(MainActivity.EXTRA_INIT_FRAG, MainActivity.ID_ENCRYPT_DECRYPT);
+ return intent;
+ }
+ };
+
+ @Test
+ public void testSymmetricCryptClipboard() throws Exception {
+
+ mActivity.getActivity();
+
+ String text = randomString(10, 30);
+
+ // navigate to encrypt/decrypt
+ onView(withId(R.id.encrypt_text)).perform(click());
+
+ {
+ onView(withId(R.id.encrypt_text_text)).perform(typeText(text));
+
+ openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext());
+ onView(withText(R.string.label_symmetric)).perform(click());
+
+ onView(withId(R.id.passphrase)).perform(typeText(PASSPHRASE));
+
+ onView(withId(R.id.encrypt_copy)).perform(click());
+
+ checkSnackbar(Style.ERROR, R.string.passphrases_do_not_match);
+
+ onView(withId(R.id.passphraseAgain)).perform(typeText(PASSPHRASE));
+
+ onView(withId(R.id.encrypt_text_text)).check(matches(withText(text)));
+
+ onView(withId(R.id.encrypt_copy)).perform(click());
+
+ checkSnackbar(Style.OK, R.string.msg_se_success);
+ }
+
+ // go to decrypt from clipboard view
+ pressBack();
+ onView(withId(R.id.decrypt_from_clipboard)).perform(click());
+
+ {
+ onView(withId(R.id.passphrase_passphrase)).perform(typeText(PASSPHRASE));
+ onView(withText(R.string.btn_unlock)).perform(click());
+
+ onView(isRecyclerItemView(R.id.decrypted_files_list,
+ hasDescendant(withText(R.string.filename_unknown_text))))
+ .check(matches(allOf(withEncryptionStatus(true), withSignatureNone())));
+
+ intending(allOf(
+ hasAction("android.intent.action.CHOOSER"),
+ hasExtra(equalTo(Intent.EXTRA_INTENT), allOf(
+ hasAction(Intent.ACTION_VIEW),
+ hasFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION),
+ hasData(allOf(hasScheme("content"), hasHost(TemporaryStorageProvider.CONTENT_AUTHORITY))),
+ hasType("text/plain")
+ ))
+ )).respondWith(new ActivityResult(Activity.RESULT_OK, null));
+
+ onView(allOf(isDescendantOfA(isRecyclerItemView(R.id.decrypted_files_list,
+ hasDescendant(withText(R.string.filename_unknown_text)))),
+ withId(R.id.file))).perform(click());
+
+ }
+
+ }
+
+ @Test
+ public void testSymmetricCryptShare() throws Exception {
+
+ mActivity.getActivity();
+
+ String text = randomString(10, 30);
+
+ // navigate to encrypt/decrypt
+ onView(withId(R.id.encrypt_text)).perform(click());
+
+ {
+ onView(withId(R.id.encrypt_text_text)).perform(typeText(text));
+
+ openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext());
+ onView(withText(R.string.label_symmetric)).perform(click());
+
+ onView(withId(R.id.passphrase)).perform(typeText(PASSPHRASE));
+
+ onView(withId(R.id.passphraseAgain)).perform(typeText(PASSPHRASE));
+
+ onView(withId(R.id.encrypt_text_text)).check(matches(withText(text)));
+
+ intending(allOf(
+ hasAction("android.intent.action.CHOOSER"),
+ hasExtra(equalTo(Intent.EXTRA_INTENT), allOf(
+ hasAction(Intent.ACTION_SEND),
+ hasFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION),
+ hasExtraWithKey(Intent.EXTRA_TEXT),
+ hasType("text/plain")
+ ))
+ )).respondWith(new ActivityResult(Activity.RESULT_OK, null));
+
+ onView(withId(R.id.encrypt_share)).perform(click());
+
+ }
+
+ }
+
+
+}
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareTest.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareTest.java
new file mode 100644
index 000000000..1e6a3f69e
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+
+import android.app.Activity;
+import android.app.Instrumentation.ActivityResult;
+import android.content.Intent;
+import android.support.test.espresso.intent.rule.IntentsTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.intent.Intents.intending;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasExtra;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasType;
+import static android.support.test.espresso.intent.matcher.UriMatchers.hasHost;
+import static android.support.test.espresso.intent.matcher.UriMatchers.hasScheme;
+import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.sufficientlysecure.keychain.TestHelpers.checkAndDismissSnackbar;
+import static org.sufficientlysecure.keychain.TestHelpers.cleanupForTests;
+
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ViewKeyAdvShareTest {
+
+ @Rule
+ public final IntentsTestRule<ViewKeyAdvActivity> mActivityRule
+ = new IntentsTestRule<ViewKeyAdvActivity>(ViewKeyAdvActivity.class) {
+ @Override
+ protected Intent getActivityIntent() {
+ Intent intent = super.getActivityIntent();
+ intent.setData(KeyRings.buildGenericKeyRingUri(0x9D604D2F310716A3L));
+ intent.putExtra(ViewKeyAdvActivity.EXTRA_SELECTED_TAB, ViewKeyAdvActivity.TAB_SHARE);
+ return intent;
+ }
+ };
+ private Activity mActivity;
+
+ @Before
+ public void setUp() throws Exception {
+ mActivity = mActivityRule.getActivity();
+
+ cleanupForTests(mActivity);
+ }
+
+ @Test
+ public void testShareOperations() throws Exception {
+
+ // no-op should yield snackbar
+ onView(withId(R.id.view_key_action_fingerprint_clipboard)).perform(click());
+ checkAndDismissSnackbar(Style.OK, R.string.fingerprint_copied_to_clipboard);
+ assertThat("clipboard data is fingerprint", ClipboardReflection.getClipboardText(mActivity),
+ is("c619d53f7a5f96f391a84ca79d604d2f310716a3"));
+
+ intending(allOf(
+ hasAction("android.intent.action.CHOOSER"),
+ hasExtra(equalTo(Intent.EXTRA_INTENT), allOf(
+ hasAction(Intent.ACTION_SEND),
+ hasType("text/plain"),
+ hasExtra(is(Intent.EXTRA_TEXT), is("openpgp4fpr:c619d53f7a5f96f391a84ca79d604d2f310716a3")),
+ hasExtra(is(Intent.EXTRA_STREAM),
+ allOf(hasScheme("content"), hasHost(TemporaryStorageProvider.CONTENT_AUTHORITY)))
+ ))
+ )).respondWith(new ActivityResult(Activity.RESULT_OK, null));
+ onView(withId(R.id.view_key_action_fingerprint_share)).perform(click());
+
+ onView(withId(R.id.view_key_action_key_clipboard)).perform(click());
+ checkAndDismissSnackbar(Style.OK, R.string.key_copied_to_clipboard);
+ assertThat("clipboard data is key",
+ ClipboardReflection.getClipboardText(mActivity), startsWith("----"));
+
+ intending(allOf(
+ hasAction("android.intent.action.CHOOSER"),
+ hasExtra(equalTo(Intent.EXTRA_INTENT), allOf(
+ hasAction(Intent.ACTION_SEND),
+ hasType("text/plain"),
+ hasExtra(is(Intent.EXTRA_TEXT), startsWith("----")),
+ hasExtra(is(Intent.EXTRA_STREAM),
+ allOf(hasScheme("content"), hasHost(TemporaryStorageProvider.CONTENT_AUTHORITY)))
+ ))
+ )).respondWith(new ActivityResult(Activity.RESULT_OK, null));
+ onView(withId(R.id.view_key_action_key_share)).perform(click());
+
+ }
+
+
+}
diff --git a/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionViewTest.java b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionViewTest.java
new file mode 100644
index 000000000..8618a0a07
--- /dev/null
+++ b/OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionViewTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.widget;
+
+
+import android.app.Activity;
+import android.content.Intent;
+import android.support.test.espresso.action.ViewActions;
+import android.support.test.espresso.matcher.RootMatchers;
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.KeyEvent;
+import android.widget.AdapterView;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.EncryptTextActivity;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.typeText;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.sufficientlysecure.keychain.TestHelpers.importKeysFromResource;
+import static org.sufficientlysecure.keychain.actions.CustomActions.tokenEncryptViewAddToken;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withKeyItemId;
+import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withKeyToken;
+
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class EncryptKeyCompletionViewTest {
+
+ @Rule
+ public final ActivityTestRule<EncryptTextActivity> mActivity
+ = new ActivityTestRule<>(EncryptTextActivity.class);
+
+ @Test
+ public void testTextEncryptDecryptFromToken() throws Exception {
+
+ Intent intent = new Intent();
+ intent.putExtra(EncryptTextActivity.EXTRA_ENCRYPTION_KEY_IDS, new long[] { 0x9D604D2F310716A3L });
+ Activity activity = mActivity.launchActivity(intent);
+
+ // import these two, make sure they're there
+ importKeysFromResource(activity, "x.sec.asc");
+
+ // check if the element passed in from intent
+ onView(ViewMatchers.withId(R.id.recipient_list)).check(matches(withKeyToken(0x9D604D2F310716A3L)));
+ onView(withId(R.id.recipient_list)).perform(ViewActions.pressKey(KeyEvent.KEYCODE_DEL));
+
+ // type X, select from list, check if it's there
+ onView(withId(R.id.recipient_list)).perform(typeText("x"));
+ onData(withKeyItemId(0x9D604D2F310716A3L)).inRoot(RootMatchers.isPlatformPopup())
+ .inAdapterView(allOf(isAssignableFrom(AdapterView.class),
+ hasDescendant(withId(R.id.key_list_item_name)))).perform(click());
+ onView(withId(R.id.recipient_list)).check(matches(withKeyToken(0x9D604D2F310716A3L)));
+ onView(withId(R.id.recipient_list)).perform(ViewActions.pressKey(KeyEvent.KEYCODE_DEL));
+ onView(withId(R.id.recipient_list)).perform(ViewActions.pressKey(KeyEvent.KEYCODE_DEL));
+
+ // add directly, check if it's there
+ onView(withId(R.id.recipient_list)).perform(tokenEncryptViewAddToken(0x9D604D2F310716A3L));
+ onView(withId(R.id.recipient_list)).check(matches(withKeyToken(0x9D604D2F310716A3L)));
+ onView(withId(R.id.recipient_list)).perform(ViewActions.pressKey(KeyEvent.KEYCODE_DEL));
+
+ }
+
+}
diff --git a/OpenKeychain/src/debug/res/drawable-hdpi/ic_launcher.png b/OpenKeychain/src/debug/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..bb5104aec
--- /dev/null
+++ b/OpenKeychain/src/debug/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/OpenKeychain/src/debug/res/drawable-mdpi/ic_launcher.png b/OpenKeychain/src/debug/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..e709f735d
--- /dev/null
+++ b/OpenKeychain/src/debug/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/OpenKeychain/src/debug/res/drawable-xhdpi/ic_launcher.png b/OpenKeychain/src/debug/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..fb4f2737a
--- /dev/null
+++ b/OpenKeychain/src/debug/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/OpenKeychain/src/debug/res/drawable-xxhdpi/ic_launcher.png b/OpenKeychain/src/debug/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..37d0958ff
--- /dev/null
+++ b/OpenKeychain/src/debug/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/OpenKeychain/src/debug/res/drawable-xxxhdpi/ic_launcher.png b/OpenKeychain/src/debug/res/drawable-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..e5183fb05
--- /dev/null
+++ b/OpenKeychain/src/debug/res/drawable-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/OpenKeychain/src/debug/res/values/strings.xml b/OpenKeychain/src/debug/res/values/strings.xml
new file mode 100644
index 000000000..d954e05bb
--- /dev/null
+++ b/OpenKeychain/src/debug/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">"OpenKeychain (Debug)"</string>
+</resources>
diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml
index f63cf8823..00c861ccc 100644
--- a/OpenKeychain/src/main/AndroidManifest.xml
+++ b/OpenKeychain/src/main/AndroidManifest.xml
@@ -2,24 +2,22 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.sufficientlysecure.keychain"
- android:installLocation="auto"
- android:versionCode="32100"
- android:versionName="3.2.1">
+ android:installLocation="auto">
<!--
General remarks
===============
- Last APG 1 version was 10900 (1.0.9 beta 00)
- Keychain starting with versionCode 20000!
-
+
Association of file types to Keychain
=====================================
General remarks about file ending conventions:
- *.gpg,*.pgp for binary files
- *.asc for ascii armored files The actual content can be anything.
-
+
The file ending only shows if it is binary or ascii encoded.
-
+
Remarks about the ugly android:pathPattern:
- We are matching all files with a specific file ending.
This is done in an ugly way because of Android limitations.
@@ -50,9 +48,10 @@
android:name="android.hardware.screen.portrait"
android:required="false" />
- <permission android:name="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE" />
-
- <uses-permission android:name="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE" />
+ <!-- TemporaryStorageProvider should be writable by OpenKeychain only, thus signature-level permission -->
+ <permission
+ android:name="${applicationId}.WRITE_TEMPORARY_STORAGE"
+ android:protectionLevel="signature" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@@ -74,7 +73,7 @@
android:hardwareAccelerated="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
- android:theme="@style/KeychainTheme">
+ android:theme="@style/Theme.Keychain.Light">
<activity
android:name=".ui.MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
@@ -96,6 +95,15 @@
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.MainActivity" />
+ <!-- Connect with YubiKeys. This Activity will automatically show/import/create YubiKeys -->
+ <intent-filter android:label="@string/app_name">
+ <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data
+ android:scheme="https"
+ android:host="my.yubico.com"
+ android:pathPrefix="/neo"/>
+ </intent-filter>
</activity>
<activity
android:name=".ui.EditKeyActivity"
@@ -201,7 +209,7 @@
</intent-filter>
</activity>
<activity
- android:name=".ui.DecryptTextActivity"
+ android:name=".ui.DisplayTextActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_decrypt"
android:parentActivityName=".ui.MainActivity"
@@ -209,25 +217,9 @@
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.MainActivity" />
-
- <!-- DECRYPT_TEXT with text as extra -->
- <intent-filter>
- <action android:name="org.sufficientlysecure.keychain.action.DECRYPT_TEXT" />
-
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- <!-- Android's Send Action -->
- <intent-filter android:label="@string/intent_send_decrypt">
- <action android:name="android.intent.action.SEND" />
-
- <category android:name="android.intent.category.DEFAULT" />
-
- <data android:mimeType="text/*" />
- <data android:mimeType="message/*" />
- </intent-filter>
</activity>
<activity
- android:name=".ui.DecryptFilesActivity"
+ android:name=".ui.DecryptActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_decrypt"
android:parentActivityName=".ui.MainActivity"
@@ -272,13 +264,23 @@
<data android:scheme="file" />
<data android:scheme="content" />
</intent-filter>
- <!-- Android's Send Action -->
+
+ <!-- DECRYPT_TEXT -->
+ <intent-filter>
+ <action android:name="org.sufficientlysecure.keychain.action.DECRYPT_TEXT" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+
+ <!-- Android's Send and Multi-Send Actions -->
<intent-filter android:label="@string/intent_send_decrypt">
<action android:name="android.intent.action.SEND" />
+ <action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
- <!-- everything except text/* and message/* -->
+ <data android:mimeType="text/*" />
+ <data android:mimeType="message/*" />
<data android:mimeType="image/*" />
<data android:mimeType="audio/*" />
<data android:mimeType="video/*" />
@@ -684,7 +686,15 @@
<activity
android:name=".ui.PassphraseDialogActivity"
android:theme="@android:style/Theme.NoDisplay" />
- <activity android:name=".ui.PassphraseWizardActivity" />
+ <activity
+ android:name=".ui.RetryUploadDialogActivity"
+ android:theme="@android:style/Theme.NoDisplay" />
+ <activity
+ android:name=".ui.DeleteKeyDialogActivity"
+ android:theme="@android:style/Theme.NoDisplay" />
+ <activity
+ android:name=".ui.OrbotRequiredDialogActivity"
+ android:theme="@android:style/Theme.NoDisplay" />
<!--
NOTE: singleTop is set to get NFC foreground dispatch to work.
Then, all NFC intents will be broadcasted to onNewIntent() of this activity!
@@ -694,21 +704,11 @@
-->
<activity
android:name=".ui.NfcOperationActivity"
+ android:theme="@style/Theme.Keychain.Light.Dialog"
android:allowTaskReparenting="true"
android:launchMode="singleTop"
android:taskAffinity=":Nfc" />
- <!--<activity-->
- <!--android:name=".ui.NfcIntentActivity"-->
- <!--android:launchMode="singleTop">-->
- <!--<intent-filter>-->
- <!--<action android:name="android.nfc.action.NDEF_DISCOVERED" />-->
-
- <!--<category android:name="android.intent.category.DEFAULT" />-->
- <!--<data android:host="my.yubico.com" android:scheme="https"/>-->
- <!--</intent-filter>-->
- <!--</activity>-->
-
<activity
android:name=".ui.HelpActivity"
android:label="@string/title_help" />
@@ -723,16 +723,16 @@
android:exported="false"
android:process=":remote_api" />
<service
- android:name=".service.KeychainIntentService"
- android:exported="false" />
- <service
- android:name=".service.CloudImportService"
+ android:name=".service.KeychainService"
android:exported="false" />
+ <!-- label is made to be "Keyserver Sync" since that is the only context in which
+ the user will see it-->
<provider
android:name=".provider.KeychainProvider"
- android:authorities="org.sufficientlysecure.keychain.provider"
- android:exported="false" />
+ android:authorities="${applicationId}.provider"
+ android:exported="false"
+ android:label="@string/keyserver_sync_settings_title"/>
<!-- Internal classes of the remote APIs (not exported) -->
<activity
@@ -806,12 +806,26 @@
android:resource="@xml/custom_pgp_contacts_structure" />
</service>
+ <service
+ android:name=".service.KeyserverSyncAdapterService"
+ android:exported="true"
+ android:process=":sync"
+ tools:ignore="ExportedService">
+ <intent-filter>
+ <action android:name="android.content.SyncAdapter" />
+ </intent-filter>
+
+ <meta-data
+ android:name="android.content.SyncAdapter"
+ android:resource="@xml/keyserver_sync_adapter_desc" />
+ </service>
+
<!-- Storage Provider for temporary decrypted files -->
<provider
android:name=".provider.TemporaryStorageProvider"
- android:authorities="org.sufficientlysecure.keychain.tempstorage"
+ android:authorities="${applicationId}.tempstorage"
android:exported="true"
- android:writePermission="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE" />
+ android:writePermission="${applicationId}.WRITE_TEMPORARY_STORAGE" />
</application>
diff --git a/OpenKeychain/src/main/assets/word_confirm_list.txt b/OpenKeychain/src/main/assets/word_confirm_list.txt
new file mode 100644
index 000000000..080c7ce81
--- /dev/null
+++ b/OpenKeychain/src/main/assets/word_confirm_list.txt
@@ -0,0 +1,12032 @@
+you
+the
+a
+and
+that
+what
+this
+for
+your
+have
+don
+are
+not
+was
+can
+know
+with
+all
+but
+here
+there
+they
+get
+just
+like
+come
+right
+she
+him
+out
+now
+one
+about
+how
+want
+got
+her
+will
+well
+see
+good
+let
+yes
+think
+who
+why
+yeah
+did
+from
+his
+when
+going
+man
+take
+where
+time
+them
+back
+look
+would
+say
+were
+been
+then
+had
+tell
+some
+our
+okay
+too
+down
+could
+hey
+never
+way
+very
+more
+really
+has
+make
+over
+please
+only
+love
+give
+little
+need
+people
+off
+two
+said
+sorry
+thank
+sir
+should
+mean
+any
+much
+sure
+even
+doing
+must
+these
+thing
+help
+god
+day
+first
+won
+life
+again
+away
+stop
+wait
+night
+find
+into
+work
+still
+put
+home
+call
+before
+better
+their
+other
+talk
+after
+maybe
+great
+than
+those
+always
+long
+money
+old
+leave
+keep
+new
+told
+things
+name
+last
+father
+around
+years
+does
+hello
+ever
+place
+big
+nice
+feel
+girl
+stay
+thanks
+made
+mother
+listen
+three
+may
+guy
+hear
+coming
+world
+enough
+left
+fine
+every
+house
+course
+done
+boy
+wrong
+bad
+which
+woman
+lot
+kind
+wanted
+guys
+came
+ask
+son
+today
+show
+own
+care
+mind
+try
+being
+same
+car
+might
+dad
+miss
+else
+many
+men
+friend
+baby
+next
+move
+huh
+live
+hold
+real
+saw
+went
+seen
+room
+best
+found
+wife
+job
+once
+such
+matter
+head
+most
+heard
+alone
+ready
+haven
+happy
+days
+run
+play
+door
+bring
+mom
+myself
+open
+yet
+trying
+knew
+whole
+meet
+excuse
+family
+used
+while
+use
+start
+took
+pretty
+gone
+called
+idea
+since
+watch
+turn
+hope
+year
+guess
+end
+sit
+hard
+says
+hand
+bit
+school
+both
+worry
+minute
+true
+face
+soon
+lost
+forget
+bye
+young
+five
+killed
+heart
+few
+wants
+later
+eat
+drink
+ago
+shut
+pay
+police
+each
+water
+anyone
+dear
+also
+shall
+looks
+saying
+until
+crazy
+late
+phone
+eyes
+easy
+sleep
+mine
+afraid
+doctor
+nobody
+four
+under
+second
+music
+change
+far
+hands
+kids
+knows
+hit
+case
+its
+gave
+read
+times
+speak
+anyway
+stand
+part
+wish
+word
+having
+cut
+stuff
+comes
+war
+number
+happen
+hurry
+quite
+rest
+close
+check
+inside
+hurt
+half
+moment
+girls
+makes
+lady
+women
+asked
+set
+boys
+taking
+story
+town
+chance
+child
+yours
+lord
+point
+deal
+sister
+goes
+party
+week
+walk
+means
+honey
+dog
+shot
+high
+bed
+game
+person
+body
+break
+free
+making
+side
+fun
+almost
+buy
+least
+truth
+six
+along
+met
+city
+behind
+send
+though
+hours
+blood
+light
+stupid
+gets
+funny
+ahead
+answer
+full
+line
+front
+hate
+white
+poor
+hot
+order
+reason
+king
+tried
+seems
+either
+couple
+trust
+months
+alive
+hour
+pick
+able
+save
+clear
+office
+john
+become
+book
+living
+food
+daddy
+cool
+dance
+red
+news
+lose
+cold
+touch
+power
+scared
+boss
+fact
+dinner
+jack
+master
+uncle
+small
+quiet
+write
+taken
+ten
+luck
+sent
+cannot
+air
+earth
+glad
+law
+till
+needs
+dream
+street
+drive
+hair
+sort
+others
+bet
+lives
+follow
+whoa
+fast
+sweet
+sound
+catch
+words
+human
+safe
+hang
+beat
+rather
+top
+plan
+seem
+known
+coffee
+ladies
+wow
+lucky
+win
+past
+calm
+pull
+lie
+sign
+return
+fall
+team
+longer
+kiss
+asking
+tired
+feet
+learn
+drop
+quick
+wake
+marry
+train
+throw
+loved
+road
+sounds
+land
+felt
+step
+eye
+piece
+weeks
+secret
+sense
+takes
+pass
+voice
+clean
+looked
+wonder
+song
+fault
+state
+seven
+born
+less
+film
+ride
+joe
+class
+act
+none
+given
+fool
+early
+worth
+ones
+future
+strong
+army
+mouth
+moving
+george
+frank
+sing
+sun
+chief
+worked
+horse
+report
+sell
+turned
+dark
+peace
+month
+movie
+lovely
+boat
+blue
+seeing
+hotel
+eight
+york
+ship
+rock
+sam
+age
+christ
+murder
+finish
+letter
+court
+works
+swear
+expect
+bill
+giving
+near
+worse
+busy
+pain
+kept
+ball
+floor
+laughs
+wear
+sea
+fly
+count
+gold
+forgot
+radio
+french
+caught
+short
+soul
+figure
+paid
+simple
+bag
+tom
+fish
+date
+rich
+paul
+broke
+miles
+during
+ring
+choice
+bank
+david
+relax
+except
+attack
+join
+table
+across
+mary
+paper
+star
+dude
+stick
+meant
+round
+honor
+fair
+hungry
+middle
+thinks
+buddy
+lying
+unless
+spend
+major
+charge
+needed
+deep
+hide
+handle
+bought
+key
+cry
+trip
+lead
+window
+enjoy
+system
+sake
+fell
+cover
+ran
+church
+carry
+tea
+smart
+force
+teach
+paris
+box
+holy
+often
+plane
+dress
+lunch
+smell
+third
+ground
+crying
+talked
+ice
+tough
+heaven
+proud
+laugh
+sad
+sighs
+lived
+art
+harry
+mark
+single
+dare
+group
+record
+wind
+cops
+fix
+club
+upon
+mike
+mess
+quit
+entire
+wine
+normal
+visit
+offer
+public
+missed
+prison
+smoke
+agree
+saved
+river
+broken
+madam
+weird
+green
+arms
+evil
+south
+bob
+wall
+bar
+judge
+orders
+seat
+bear
+wrote
+queen
+slow
+cause
+dreams
+loves
+cop
+liked
+north
+glass
+accept
+cross
+scene
+double
+share
+honest
+moon
+joke
+beer
+space
+jump
+whose
+jail
+area
+tree
+foot
+test
+cat
+dying
+within
+ought
+brain
+nine
+peter
+field
+bother
+bus
+lots
+doubt
+camera
+became
+german
+books
+gives
+shoes
+truck
+ben
+kick
+card
+cash
+push
+moved
+likes
+calls
+cute
+max
+park
+store
+grow
+owe
+aunt
+reach
+guard
+spent
+summer
+rules
+duty
+island
+johnny
+eating
+smile
+silly
+san
+folks
+knock
+pardon
+crowd
+henry
+beg
+stuck
+action
+upset
+begin
+prove
+feels
+grand
+using
+guns
+legs
+nose
+rid
+mum
+list
+sky
+shop
+arm
+escape
+mommy
+gas
+low
+jimmy
+self
+hat
+ray
+bell
+price
+cell
+rain
+warm
+west
+passed
+board
+london
+nor
+lawyer
+lay
+closed
+gift
+favor
+tony
+jim
+empty
+prince
+papa
+suit
+press
+asleep
+type
+grab
+spirit
+arrest
+papers
+band
+indeed
+pop
+played
+dogs
+agent
+above
+wild
+nick
+race
+mrs
+spot
+fellow
+whom
+leg
+awful
+killer
+fit
+aye
+beauty
+twenty
+bird
+named
+lock
+blame
+heavy
+choose
+allow
+twice
+magic
+waste
+plenty
+raise
+flying
+notice
+gasps
+doc
+decide
+taste
+flight
+picked
+billy
+fresh
+cost
+nature
+hero
+james
+places
+planet
+search
+battle
+steal
+lights
+neck
+mate
+hardly
+sold
+study
+famous
+simply
+keys
+memory
+names
+worst
+throat
+guilty
+ways
+shame
+stone
+bottle
+states
+mister
+pray
+bunch
+camp
+starts
+seemed
+form
+stars
+stage
+animal
+roll
+east
+faster
+locked
+van
+final
+apart
+match
+admit
+tight
+engine
+build
+treat
+sight
+closer
+huge
+beach
+bigger
+keeps
+stole
+united
+among
+hall
+danger
+itself
+cup
+milk
+fired
+pants
+built
+lies
+cousin
+steve
+main
+cook
+meat
+ill
+angel
+risk
+plans
+tape
+taught
+sword
+extra
+corner
+beyond
+hiding
+belong
+sarah
+wash
+drugs
+cars
+skin
+large
+leaves
+driver
+health
+ugly
+helped
+note
+bridge
+martin
+rose
+pig
+acting
+horses
+level
+nuts
+sunday
+track
+plays
+anna
+heads
+walked
+strike
+rule
+turns
+common
+madame
+grunts
+roger
+losing
+weil
+speed
+tells
+due
+serve
+teeth
+began
+lee
+shirt
+rings
+robert
+spoke
+size
+feed
+held
+bobby
+coach
+dry
+pack
+ideas
+grace
+prefer
+bottom
+movies
+view
+breath
+ticket
+energy
+code
+hurts
+cheers
+remain
+pal
+usual
+groans
+showed
+weapon
+brown
+center
+coat
+ours
+copy
+market
+jerry
+noise
+birds
+fill
+advice
+rome
+cream
+ghost
+exist
+oil
+jake
+tie
+leader
+draw
+loud
+lift
+nearly
+post
+travel
+surely
+bread
+cards
+older
+heat
+games
+crew
+cake
+bucks
+pity
+brave
+chair
+gotten
+bless
+wet
+priest
+shows
+thy
+agreed
+truly
+gate
+nurse
+paying
+slowly
+thou
+local
+style
+soft
+spring
+grown
+pieces
+dig
+shake
+settle
+maria
+ends
+larry
+lonely
+garden
+stayed
+enter
+weak
+fate
+split
+flat
+proof
+jane
+loose
+gang
+trial
+speaks
+afford
+snow
+bright
+trees
+friday
+guest
+ears
+santa
+career
+threw
+color
+bodies
+mayor
+manage
+victim
+finger
+opened
+roof
+file
+lake
+plus
+sees
+spare
+farm
+rent
+repeat
+sugar
+alarm
+winter
+insane
+base
+total
+stops
+thief
+hill
+fancy
+stolen
+female
+tongue
+sooner
+liar
+target
+joy
+mail
+client
+easier
+cheap
+brings
+pulled
+doors
+credit
+lines
+harm
+castle
+male
+birth
+spread
+storm
+arthur
+paint
+slept
+lover
+hoping
+bath
+cares
+taxi
+trade
+lately
+video
+weight
+thomas
+lied
+square
+tears
+march
+block
+thee
+eggs
+onto
+forest
+europe
+pool
+tiny
+jean
+super
+walter
+nights
+pete
+signal
+signed
+rat
+mercy
+yellow
+dan
+desk
+shower
+simon
+scare
+regret
+remind
+swim
+merry
+pocket
+path
+ended
+speech
+aware
+drinks
+pair
+ocean
+waited
+page
+andy
+runs
+rise
+fake
+amen
+ate
+pure
+bullet
+photo
+social
+genius
+desert
+forced
+player
+grave
+map
+staff
+vote
+headed
+easily
+monkey
+rough
+create
+scott
+wise
+harder
+guests
+sudden
+silver
+higher
+fought
+pounds
+fixed
+add
+lower
+desire
+bro
+failed
+monday
+louis
+badly
+below
+clock
+member
+piano
+lesson
+kate
+gods
+rush
+lips
+unit
+tall
+safety
+fred
+adam
+season
+points
+thirty
+odd
+wood
+clever
+royal
+parts
+artist
+suffer
+dawn
+rights
+grew
+union
+nation
+theory
+mood
+filled
+soup
+model
+hung
+period
+opens
+ear
+golden
+hearts
+cheese
+plant
+proper
+shape
+ruin
+legal
+faces
+ruined
+issue
+refuse
+per
+bride
+awake
+cow
+load
+forces
+smith
+based
+grant
+woods
+scream
+freak
+temple
+decent
+hidden
+joking
+arrive
+sin
+jacket
+invite
+boring
+bang
+wolf
+mirror
+lucy
+dies
+alice
+walls
+annie
+saint
+avoid
+bike
+carl
+sat
+talent
+fifty
+chest
+buying
+tear
+jobs
+guards
+image
+indian
+finds
+brian
+trap
+burned
+eve
+shock
+iron
+wide
+marie
+hunt
+poison
+flower
+anne
+bones
+matt
+dollar
+rooms
+owner
+brains
+glory
+cab
+riding
+meal
+freeze
+damned
+candy
+shots
+raised
+tower
+stood
+bravo
+dust
+alan
+warn
+toast
+virgin
+bags
+appear
+caused
+pride
+switch
+pilot
+damage
+helen
+led
+loving
+rice
+knees
+coast
+songs
+babies
+duke
+silent
+tour
+sons
+daniel
+fourth
+actor
+victor
+armed
+moves
+palace
+troops
+rate
+writer
+hired
+junior
+phil
+amount
+false
+affair
+rob
+susan
+falls
+tied
+bull
+fallen
+saving
+snake
+loss
+mighty
+youth
+smells
+lab
+border
+kicked
+cast
+drove
+rescue
+sharp
+zero
+borrow
+humans
+eric
+flesh
+row
+jason
+modern
+score
+pills
+gee
+studio
+fail
+tip
+defend
+eaten
+steps
+cases
+climb
+role
+event
+egg
+joey
+effect
+seek
+chase
+wave
+houses
+blows
+rocks
+stairs
+rope
+dragon
+bow
+winner
+barely
+aboard
+hated
+mass
+grade
+leo
+aside
+bone
+wire
+ted
+yard
+button
+jews
+plate
+dump
+tail
+bored
+salt
+metal
+talks
+sand
+joseph
+films
+fruit
+gosh
+claim
+goal
+penny
+clears
+shadow
+firm
+powers
+rachel
+prime
+punch
+wishes
+non
+steady
+tiger
+giant
+edge
+woke
+chuck
+loser
+assume
+behave
+direct
+jones
+blew
+pen
+chose
+wheel
+drag
+sexual
+maid
+county
+chick
+remove
+jenny
+nope
+spell
+terms
+focus
+stock
+valley
+duck
+chosen
+likely
+wound
+object
+sale
+spoken
+buck
+mummy
+tank
+pie
+notes
+shout
+guts
+bound
+scary
+hop
+value
+slip
+result
+amy
+los
+photos
+shine
+mostly
+rare
+hitler
+witch
+served
+texas
+jury
+intend
+wings
+albert
+kinds
+com
+pink
+cruel
+bury
+doll
+terry
+facts
+range
+brand
+shy
+kong
+screen
+rabbit
+apple
+swing
+prize
+marks
+deeply
+laws
+wing
+mile
+coward
+site
+thus
+closes
+june
+mixed
+earn
+hong
+rude
+begins
+linda
+kitty
+curse
+chain
+cure
+spy
+noble
+juice
+source
+circle
+plain
+sue
+ann
+twelve
+rotten
+lad
+thrown
+minds
+grass
+kelly
+kissed
+squad
+jungle
+bond
+effort
+series
+expert
+filthy
+belt
+edward
+lets
+thin
+berlin
+commit
+boots
+punk
+stands
+cheer
+sheep
+guitar
+access
+ships
+bust
+flag
+panic
+pizza
+morgan
+deny
+sally
+ali
+bush
+asks
+deck
+tokyo
+term
+hunter
+former
+tim
+insist
+estate
+martha
+assure
+hates
+navy
+debt
+fever
+luke
+joined
+reward
+karen
+ease
+fetch
+garage
+jumped
+signs
+shoe
+threat
+zone
+solve
+title
+policy
+ages
+crown
+leads
+lion
+hire
+guide
+bay
+demand
+trash
+tone
+vision
+supper
+voices
+hip
+route
+lack
+port
+cats
+dozen
+dirt
+helps
+media
+bruce
+puts
+exact
+secure
+stays
+april
+lousy
+deaf
+caesar
+rip
+fox
+urgent
+maggie
+russia
+tricks
+mask
+gather
+warned
+betty
+highly
+useful
+tires
+forty
+gentle
+suits
+carter
+jesse
+solid
+wipe
+launch
+emily
+hank
+sweat
+steel
+tests
+alert
+fifth
+script
+lend
+costs
+toward
+aim
+latest
+tune
+agency
+sacred
+empire
+events
+vice
+argue
+rick
+vegas
+mental
+loaded
+anger
+clark
+breaks
+sonny
+museum
+tax
+miller
+flash
+pushed
+data
+dean
+lily
+gray
+brad
+sports
+pierre
+senior
+roman
+reckon
+singer
+cap
+stones
+firing
+handed
+beside
+walks
+daily
+fully
+clue
+wilson
+souls
+kills
+banks
+circus
+shift
+clouds
+closet
+hits
+forth
+jackie
+prayer
+loan
+fools
+fields
+trace
+civil
+orange
+wayne
+carol
+nerve
+outfit
+degree
+flies
+andrew
+bills
+cheat
+idiots
+chat
+ending
+noon
+cabin
+detail
+hug
+planes
+ghosts
+hills
+alien
+pigs
+mix
+hail
+supply
+struck
+bat
+files
+cents
+sides
+opera
+bug
+jokes
+angels
+design
+ken
+pour
+cave
+gear
+dating
+beaten
+herr
+legend
+nephew
+belly
+dancer
+thick
+ignore
+fuel
+pat
+cried
+lane
+ellen
+bowl
+butter
+bombs
+francs
+net
+wasted
+defeat
+bars
+bells
+toy
+sport
+wallet
+trunk
+homes
+wore
+coke
+ouch
+obey
+fort
+norman
+trail
+league
+shore
+task
+doug
+wins
+rats
+motion
+blast
+mouse
+merely
+lads
+busted
+anti
+monk
+wives
+agents
+seats
+inform
+waves
+gary
+moscow
+accent
+beings
+attend
+shown
+josh
+robin
+tunnel
+drew
+recall
+drank
+greek
+poetry
+swell
+knight
+earl
+units
+chill
+harold
+mars
+holes
+jay
+rifle
+bishop
+beth
+robbed
+hoped
+fond
+rocky
+unique
+goods
+pound
+louise
+crush
+nancy
+golf
+fence
+woo
+patrol
+sire
+resist
+stan
+cloud
+habit
+pays
+blown
+von
+lovers
+novel
+august
+wagon
+packed
+widow
+boo
+siren
+ruth
+exit
+beef
+exists
+tracks
+bud
+actors
+nail
+ugh
+skip
+july
+passes
+crisis
+sins
+washed
+sink
+sail
+dough
+baron
+pet
+wicked
+flow
+halt
+aid
+charm
+cattle
+owns
+worker
+hood
+nerves
+jam
+purse
+smooth
+divine
+seal
+hopes
+chop
+heroes
+equal
+virus
+sec
+gates
+powder
+mickey
+mud
+wrap
+sang
+beeps
+begun
+couch
+flew
+jacob
+skills
+arts
+lewis
+tries
+print
+pole
+ward
+theme
+fans
+soap
+string
+farmer
+kung
+gain
+poem
+issues
+inch
+greg
+teddy
+burns
+polish
+beats
+chef
+frozen
+pro
+yell
+waters
+device
+davis
+bend
+basic
+slap
+earned
+cowboy
+boston
+wee
+praise
+cable
+moral
+permit
+behalf
+messed
+plants
+knee
+holds
+clown
+shouts
+placed
+worthy
+cinema
+cage
+finest
+waiter
+junk
+strip
+guilt
+buzz
+kit
+deputy
+granny
+hers
+shark
+eagle
+poet
+oscar
+cliff
+boats
+tyler
+counts
+parker
+holmes
+softly
+snap
+sets
+nina
+bail
+beard
+hut
+yen
+tale
+heck
+chip
+crimes
+steven
+bitter
+juan
+uses
+hid
+eleven
+scoffs
+mount
+dealer
+limit
+liquor
+sauce
+miami
+odds
+combat
+baker
+corn
+images
+cries
+yards
+shed
+insult
+pitch
+beans
+salad
+corpse
+gym
+lazy
+nearby
+landed
+roses
+goat
+foul
+beep
+meters
+yells
+acts
+frame
+cancel
+link
+pin
+daisy
+absurd
+slaves
+hammer
+admire
+tastes
+benny
+motor
+boxes
+gifts
+ace
+loyal
+cities
+blonde
+punish
+clan
+native
+cells
+dumped
+skull
+stroke
+fights
+pearl
+avenue
+tooth
+timing
+rocket
+grief
+brush
+sucker
+chicks
+alley
+dennis
+judy
+brief
+robot
+angle
+ail
+mobile
+drives
+sends
+pope
+status
+profit
+wounds
+actual
+nut
+tent
+host
+barn
+stress
+broad
+chips
+sack
+cooper
+stink
+oxygen
+raped
+ranch
+whip
+coughs
+labor
+relief
+soviet
+wars
+oliver
+blade
+upper
+sings
+scale
+alike
+drown
+spoil
+salary
+jazz
+harris
+palm
+fleet
+pump
+unable
+clinic
+sailor
+horror
+eats
+clay
+forms
+stinks
+dame
+jet
+polite
+deeper
+dishes
+blah
+drama
+minor
+editor
+wheels
+roads
+ford
+sandy
+humble
+throne
+backup
+shave
+sore
+driven
+pit
+socks
+bunny
+delay
+colors
+sneak
+sung
+rub
+tap
+walker
+vodka
+diet
+wisdom
+ripped
+holly
+statue
+louder
+tracy
+grows
+gently
+manner
+recent
+groups
+spin
+ralph
+lamb
+humor
+pages
+anyhow
+pale
+pile
+grey
+sid
+jan
+khan
+chaos
+soda
+fits
+claims
+jersey
+apply
+kisses
+skinny
+torn
+cotton
+kings
+champ
+burnt
+spider
+twins
+gloves
+bears
+dutch
+misery
+facing
+sales
+pan
+drawn
+joan
+remote
+exam
+phones
+cows
+allen
+brandy
+cherry
+diego
+maya
+bid
+donna
+toys
+bee
+plot
+cared
+parade
+crane
+advise
+patch
+dna
+envy
+option
+hush
+casino
+shell
+nicely
+drill
+twist
+temper
+min
+steak
+ham
+blocks
+sorrow
+rear
+coin
+janet
+ashes
+clicks
+format
+drops
+safely
+wealth
+jess
+medal
+coffin
+que
+goose
+gloria
+appeal
+pill
+sub
+towel
+slide
+ups
+tragic
+claus
+fires
+leon
+faced
+core
+motel
+worn
+romeo
+global
+rhythm
+nest
+soccer
+fried
+betray
+terror
+stable
+liver
+solar
+wreck
+howdy
+wears
+dull
+checks
+dish
+rap
+basis
+unlike
+kindly
+fog
+sticks
+bugs
+zoo
+pulse
+lamp
+taxes
+abroad
+dates
+burden
+oath
+award
+marine
+felix
+tools
+rusty
+irish
+cooked
+rising
+rubber
+hunger
+scenes
+faint
+cellar
+troy
+item
+niece
+sarge
+badge
+frog
+smash
+shove
+sunny
+toes
+sixth
+reveal
+tennis
+mob
+gene
+proved
+poker
+nails
+ram
+symbol
+pork
+inner
+makeup
+fanny
+bits
+soil
+vain
+sum
+hatred
+kenny
+scout
+boot
+album
+buddha
+senses
+tender
+plates
+lap
+deadly
+tanks
+entry
+puppy
+watson
+warren
+shared
+tin
+philip
+warden
+inches
+rosa
+fuss
+stiff
+folk
+cent
+asses
+cart
+liz
+scotch
+tend
+pistol
+del
+cookie
+flip
+bucket
+booze
+inn
+math
+tons
+acted
+bets
+bare
+vast
+crawl
+ruby
+forbid
+owned
+sis
+basket
+rank
+branch
+active
+depend
+eyed
+chi
+lit
+gum
+barrel
+drum
+sheets
+connie
+stare
+potato
+causes
+needle
+darn
+heal
+wished
+muscle
+cuts
+rage
+raid
+leak
+blank
+breast
+flame
+feast
+sorts
+unfair
+areas
+chap
+porter
+tense
+tire
+marked
+fooled
+neat
+nelson
+duties
+impact
+ideal
+diary
+steam
+text
+rumors
+limits
+bait
+fist
+carpet
+dining
+safer
+toe
+teams
+burst
+bore
+fame
+lean
+review
+rounds
+bureau
+foster
+jewels
+horns
+sobs
+trains
+dime
+pre
+raw
+cursed
+lick
+donkey
+method
+sits
+bump
+worm
+silk
+hooked
+rod
+upside
+sealed
+alpha
+comedy
+dong
+prints
+retire
+lands
+tattoo
+weed
+worlds
+donald
+sample
+column
+suite
+skirt
+bingo
+hears
+skies
+sheet
+denied
+apples
+korea
+alfred
+ladder
+booth
+ding
+plague
+phase
+sunset
+mason
+trucks
+snakes
+toss
+fridge
+unto
+pops
+danced
+racing
+brick
+region
+growls
+gal
+picnic
+clerk
+rio
+mall
+sleepy
+psycho
+mere
+yang
+scar
+sober
+sweep
+handy
+root
+budget
+shirts
+cigar
+whale
+height
+log
+wolves
+drawer
+creep
+spite
+rot
+graham
+drums
+boxing
+harbor
+las
+tomb
+repair
+fund
+writes
+stuart
+collar
+lemon
+villa
+affect
+brakes
+purple
+tapes
+tube
+solved
+murphy
+radar
+error
+coal
+cease
+mortal
+cannon
+tag
+demons
+winds
+shield
+fears
+turner
+motive
+sleeps
+locker
+laying
+fairly
+oldest
+deaths
+rode
+breeze
+korean
+seated
+click
+sylvia
+heels
+adults
+subway
+pigeon
+swore
+fee
+minus
+deed
+thumb
+dizzy
+blues
+grip
+dale
+prom
+pepper
+skill
+ballet
+beds
+candle
+slight
+vital
+plug
+elder
+flames
+types
+homer
+groom
+reply
+hon
+fur
+sixty
+melody
+pillow
+outer
+stream
+clubs
+bald
+loses
+beware
+diana
+quote
+hath
+spots
+sirens
+dreamt
+era
+copies
+fry
+tub
+ana
+sector
+chess
+nanny
+dummy
+belief
+added
+wizard
+lance
+rang
+tables
+aliens
+booked
+walt
+bench
+whores
+owes
+stab
+cathy
+dial
+mill
+edgar
+creepy
+irene
+doubts
+acid
+lungs
+vera
+chorus
+author
+visual
+adore
+arrow
+barks
+helmet
+random
+deer
+larger
+ling
+tribe
+deals
+blond
+parent
+tide
+gig
+bold
+sakes
+seed
+severe
+nap
+hatch
+twin
+fixing
+moans
+lip
+prices
+gossip
+cruise
+triple
+hawk
+hockey
+chains
+parked
+debts
+meets
+lame
+maniac
+brat
+wang
+greedy
+bacon
+bleed
+tarzan
+dug
+galaxy
+chased
+depth
+rumor
+mid
+ape
+pencil
+lions
+flood
+jolly
+cargo
+hats
+beam
+banana
+vault
+cheek
+jill
+phoned
+turtle
+ginger
+rely
+beers
+coma
+corps
+elvis
+formed
+cured
+bloom
+narrow
+serial
+heir
+cough
+aha
+greet
+gamble
+organ
+losers
+react
+judges
+sandra
+levels
+backs
+rolls
+naive
+starve
+income
+bean
+heroin
+custom
+wiped
+easter
+pirate
+bass
+strict
+slice
+ties
+harsh
+roast
+marc
+moses
+slim
+lobby
+liquid
+rivers
+solo
+bounce
+bonus
+regard
+tore
+madrid
+length
+bent
+tested
+chapel
+meals
+cloth
+dice
+yea
+cape
+lung
+ton
+tales
+breed
+tango
+herd
+parole
+web
+deeds
+tool
+coins
+nun
+kansas
+den
+ditch
+shares
+hector
+venice
+pub
+chew
+razor
+hawaii
+noah
+whisky
+comic
+dallas
+minded
+slams
+scares
+brass
+mines
+doomed
+sworn
+sharks
+autumn
+logan
+swords
+swine
+pace
+bloke
+wooden
+vessel
+harper
+bon
+drain
+pond
+debbie
+butler
+wade
+bin
+shrink
+fled
+dam
+carlo
+alibi
+cafe
+chow
+smoked
+models
+reads
+stores
+rum
+kent
+theirs
+glove
+violin
+behold
+mon
+roots
+hanged
+rebel
+rains
+rented
+diane
+hugo
+fatal
+gut
+bark
+menu
+orphan
+honks
+luxury
+ahem
+fare
+worms
+rascal
+formal
+mouths
+hah
+risky
+salute
+nicer
+peggy
+riot
+risks
+racket
+tasty
+theft
+swamp
+yankee
+voyage
+saddle
+serves
+lawn
+potter
+shoots
+ritual
+sucked
+offers
+brutal
+float
+rarely
+oven
+hay
+sidney
+petty
+waking
+morris
+modest
+grain
+injury
+lester
+swiss
+sells
+nazis
+votes
+debate
+milan
+draft
+mice
+savage
+arabic
+ransom
+knives
+repay
+sydney
+spill
+wit
+leaf
+arnold
+seas
+medium
+dock
+proves
+loudly
+crow
+choir
+belle
+tech
+thats
+tick
+ski
+ruins
+weigh
+rico
+prey
+venus
+online
+covers
+bubble
+echo
+dried
+wells
+bandit
+stove
+rex
+dot
+flee
+values
+wax
+mae
+ducks
+eager
+funds
+scent
+hangs
+scheme
+romans
+meter
+samuel
+growth
+tricky
+wrist
+helena
+franz
+reed
+amber
+marina
+asylum
+creek
+kneel
+ash
+mule
+fold
+kirk
+nurses
+noises
+locate
+shaw
+senate
+thrill
+chairs
+cycle
+poems
+jose
+volume
+spray
+smiles
+seeds
+cakes
+owners
+sole
+learnt
+bully
+gypsy
+rays
+mini
+pilots
+tips
+phrase
+rosie
+begged
+keen
+static
+spoon
+vienna
+disco
+cane
+virtue
+kicks
+loads
+via
+dated
+tan
+ming
+mature
+sofa
+stir
+joyce
+pine
+joker
+fries
+armor
+wander
+peanut
+scores
+picks
+mafia
+olive
+madman
+floors
+awhile
+logic
+manual
+rolled
+mates
+ink
+voted
+snack
+pledge
+crook
+laser
+bands
+hamlet
+races
+rebels
+aged
+endure
+mia
+shade
+stamp
+lasted
+buster
+beasts
+batman
+brake
+occur
+dolly
+delta
+orbit
+pulls
+fork
+campus
+rig
+dances
+dodge
+allies
+hooray
+hereby
+wig
+alas
+joel
+dealt
+misses
+maker
+hobby
+tails
+spies
+oui
+devils
+andre
+feared
+clues
+pals
+nickel
+auto
+towns
+notion
+flush
+straw
+sigh
+stunt
+veins
+buys
+maiden
+whack
+bonnie
+kidnap
+owl
+rally
+seize
+canyon
+lords
+barber
+abe
+weep
+spying
+esther
+shorts
+wakes
+phony
+cans
+jeep
+burger
+haul
+myth
+duel
+sour
+spike
+throws
+stall
+rug
+adams
+robe
+roars
+ninety
+gladly
+pierce
+asia
+ego
+tissue
+ranger
+sherry
+karate
+dudes
+choke
+sheila
+vow
+cue
+allows
+queens
+bees
+curly
+shiny
+camel
+prior
+melt
+jewel
+freaks
+bonds
+evans
+anchor
+zip
+morgue
+hotels
+ruled
+pose
+exams
+carbon
+shops
+pony
+assist
+altar
+graves
+jun
+untie
+thud
+ensure
+mainly
+ashore
+doris
+jar
+resign
+accuse
+prayed
+violet
+butch
+copper
+camps
+scan
+awaits
+freely
+offend
+dip
+owed
+ants
+hint
+wired
+pooh
+fade
+ankle
+knocks
+rides
+stew
+trips
+urge
+ping
+attic
+jaw
+sweets
+boil
+darwin
+dolls
+porch
+noisy
+cant
+eighty
+sync
+origin
+mutual
+visits
+pastor
+tease
+dagger
+caring
+stereo
+sniffs
+rack
+gown
+issued
+stark
+hairy
+proven
+grades
+leap
+posted
+items
+divide
+engage
+plead
+sting
+lifted
+kathy
+dared
+waits
+baba
+clumsy
+wages
+ropes
+denver
+apollo
+cord
+rifles
+fails
+diving
+habits
+lust
+troop
+puzzle
+lined
+ferry
+filed
+woody
+luther
+heel
+jeanne
+bolt
+bra
+label
+drake
+cups
+tomato
+shorty
+colony
+dove
+filth
+dings
+tuck
+ghetto
+submit
+torch
+val
+avenge
+bounty
+pipes
+virgil
+puppet
+locks
+mole
+wires
+lively
+resort
+waltz
+joshua
+dante
+pursue
+strain
+hudson
+sought
+gained
+recipe
+austin
+iris
+tucker
+glue
+annual
+jelly
+hunch
+mug
+amazed
+ridge
+sissy
+atomic
+hence
+invent
+cheque
+thread
+erase
+salmon
+boxer
+patty
+sofia
+buzzer
+saints
+hike
+scarf
+globe
+usa
+swan
+palmer
+tops
+ally
+comb
+pains
+eighth
+breach
+canal
+rogers
+runner
+messy
+subtle
+wheat
+adopt
+peach
+shrimp
+ribs
+fierce
+rises
+seoul
+popped
+fuse
+banker
+quest
+spark
+warmth
+expose
+shelf
+peak
+shaft
+rupees
+rash
+blamed
+jeans
+glow
+crab
+jammed
+sip
+roar
+beggar
+cope
+pinch
+organs
+hips
+shaped
+towers
+towels
+clip
+pad
+ninth
+hugh
+meg
+phoebe
+favors
+carson
+lodge
+delhi
+cheeks
+rue
+hack
+feds
+lasts
+yacht
+greed
+goals
+spends
+hen
+shook
+des
+masks
+yuan
+nailed
+rider
+bumped
+mamma
+paulo
+stern
+agenda
+resume
+armies
+tasted
+drift
+thirst
+treats
+vulgar
+waist
+creeps
+grasp
+monte
+heroic
+slam
+smiled
+onions
+sultan
+extend
+marcel
+baked
+tigers
+shack
+gaze
+eden
+chart
+tidy
+monks
+codes
+lyrics
+rushed
+hum
+est
+fax
+shoo
+vacuum
+notify
+bribe
+mock
+plea
+flint
+frost
+clap
+trauma
+stoned
+parrot
+dozens
+stray
+axe
+freed
+calvin
+refer
+herb
+shells
+pinky
+crop
+paths
+garlic
+kin
+veil
+robber
+whew
+cracks
+gasp
+setup
+kilos
+wont
+bosses
+sew
+fisher
+reform
+dearly
+junkie
+sock
+courts
+spared
+intact
+agony
+scored
+stack
+oak
+ohio
+stain
+fran
+pier
+liking
+loosen
+comet
+obtain
+piggy
+roller
+flown
+refuge
+traces
+rag
+vile
+scouts
+scram
+reduce
+eugene
+exotic
+dusty
+peel
+swept
+heap
+shines
+pasta
+versus
+surf
+grease
+backed
+clowns
+shovel
+cooler
+fades
+ambush
+superb
+hollow
+pawn
+jingle
+slit
+byron
+moose
+zeus
+knots
+buenos
+topic
+hustle
+mute
+metro
+dash
+peek
+waving
+etc
+burial
+cuban
+ammo
+quid
+gag
+smokes
+keeper
+factor
+equals
+spear
+await
+views
+shin
+sleeve
+bunk
+glen
+arabs
+naval
+losses
+boogie
+julius
+hose
+millie
+bushes
+vanish
+slower
+lire
+flour
+miriam
+bitten
+pearls
+scars
+womb
+treaty
+stamps
+finals
+weary
+stalin
+lent
+unite
+tong
+poles
+spine
+lucia
+sadly
+masses
+zombie
+wan
+bliss
+isaac
+saloon
+verse
+reign
+purely
+biting
+elaine
+cozy
+token
+archer
+elite
+tray
+steer
+craft
+flock
+ripe
+tubes
+horace
+coop
+ant
+trophy
+ruler
+brute
+hog
+lease
+squeal
+boiled
+potion
+grapes
+tuna
+dwarf
+lounge
+robots
+invest
+franco
+rogue
+taller
+ranks
+fluid
+asian
+mayday
+spice
+poster
+liable
+rail
+thai
+giants
+hart
+timer
+sunk
+shelly
+toxic
+lethal
+info
+alaska
+nuns
+adjust
+parlor
+sticky
+prague
+lang
+hides
+sane
+rainy
+dose
+kidney
+whoops
+gutter
+legacy
+bake
+bites
+absent
+lid
+bricks
+whites
+unlock
+poke
+panel
+gap
+condom
+traps
+visa
+tailor
+linked
+reject
+maps
+ark
+buzzes
+flute
+diner
+shaved
+acres
+rival
+agrees
+puff
+banner
+dixie
+fees
+jumps
+bats
+sights
+daring
+listed
+blink
+cigars
+sway
+vest
+healed
+sketch
+soaked
+claw
+pupil
+chico
+casual
+knot
+stud
+salon
+fabric
+skate
+sheer
+racist
+onion
+heave
+enters
+ale
+amelia
+finch
+exile
+spells
+extent
+fatty
+glance
+vet
+flows
+assets
+calf
+thugs
+whales
+realm
+kitten
+slick
+poppy
+edith
+elders
+filmed
+medals
+buyer
+strap
+bundle
+saves
+toad
+cement
+seduce
+stroll
+utter
+classy
+scrap
+apache
+hardy
+yield
+runway
+reich
+lump
+limo
+tow
+cognac
+warmer
+frogs
+unity
+lone
+whitey
+define
+dine
+amigo
+grudge
+cobb
+sewer
+pairs
+punks
+newton
+nicest
+vase
+hebrew
+parish
+vows
+marble
+savior
+raft
+monroe
+hogan
+angus
+holler
+preach
+tab
+depths
+framed
+arrows
+mint
+brace
+broom
+arena
+splash
+intent
+ernest
+scoop
+abused
+woe
+ivy
+noses
+fright
+repent
+nixon
+petrol
+velvet
+steals
+fuller
+gulf
+probe
+aisle
+select
+lens
+pupils
+legion
+plaza
+puerto
+coats
+grim
+vanity
+void
+idle
+fang
+nay
+psst
+colt
+pickup
+sol
+loop
+lizard
+skiing
+sorted
+layer
+poured
+fury
+shrine
+thorn
+costa
+trials
+cola
+naples
+medic
+motto
+mal
+nasa
+grid
+hairs
+belts
+shuts
+sewing
+stakes
+goats
+snaps
+swap
+blacks
+curve
+sinner
+retard
+videos
+tenth
+yummy
+elbow
+bombay
+charts
+golly
+doe
+pets
+newly
+scrub
+rhyme
+carrot
+gangs
+wagner
+beck
+audio
+flavor
+judith
+morons
+cradle
+ruling
+sniper
+judas
+milton
+gerald
+thrust
+bunker
+mode
+slack
+males
+novels
+coup
+funky
+cult
+aspect
+pesos
+burton
+wage
+bluff
+gifted
+karma
+boards
+hunted
+disk
+willis
+sloppy
+wrath
+rapid
+buses
+urban
+turk
+storms
+riddle
+mild
+beau
+moss
+jerks
+draws
+judged
+pause
+milady
+bathe
+bombed
+warsaw
+abort
+willow
+seized
+dexter
+merci
+tickle
+flags
+scam
+chili
+swat
+bomber
+flora
+thug
+tempo
+decade
+grove
+ideals
+crows
+sparks
+shalt
+yelled
+signor
+jonah
+blouse
+numb
+quack
+greeks
+arise
+havana
+richer
+piglet
+fruits
+dares
+thine
+bates
+ethics
+alter
+crowds
+lotus
+shakes
+wager
+bulls
+doggy
+fishy
+curfew
+geneva
+vague
+slug
+ironic
+lure
+summon
+hiring
+butts
+cosmic
+turf
+flirt
+semi
+hound
+sahib
+pint
+wretch
+seldom
+loot
+mozart
+partly
+claws
+wool
+woof
+clive
+thighs
+doth
+sites
+trout
+packs
+noted
+hotter
+caviar
+eldest
+banned
+jasper
+norma
+dim
+mills
+chilly
+fuzzy
+hurrah
+gibson
+beaver
+turks
+convoy
+birdie
+sniff
+cain
+dent
+merlin
+yale
+mutt
+med
+bleep
+resent
+lace
+calmly
+freaky
+memo
+lowest
+ribbon
+pod
+midst
+wagons
+dome
+tackle
+reef
+midget
+marker
+relate
+chalk
+hayes
+swift
+charms
+esteem
+pike
+weasel
+dental
+opium
+critic
+unload
+heidi
+ache
+polo
+tossed
+cutter
+chiefs
+insect
+regime
+flank
+barge
+oxford
+crank
+streak
+lex
+gong
+basil
+crabs
+samba
+aiming
+gale
+fiddle
+limp
+marian
+risked
+fiance
+holden
+shaggy
+hitch
+aires
+moi
+invade
+wits
+stitch
+cuckoo
+coco
+pow
+expand
+tipped
+osaka
+peas
+rags
+peep
+amazon
+shifts
+gospel
+voting
+mare
+finn
+parks
+bun
+infant
+curry
+crops
+buckle
+trim
+plum
+marsh
+mop
+scope
+idol
+cuffs
+paddy
+sailed
+cobra
+macho
+sinned
+forged
+sails
+toll
+chaps
+mining
+rehab
+merit
+flea
+dandy
+traded
+kyoto
+reader
+fists
+crooks
+rates
+bind
+reborn
+grind
+stinky
+thesis
+haste
+crib
+stocks
+blades
+damp
+hating
+munich
+amuse
+warp
+sly
+gloomy
+atom
+anders
+awards
+spa
+mixing
+undo
+thumbs
+rookie
+sweaty
+marge
+blunt
+grill
+verify
+haired
+chimes
+crews
+pasha
+canvas
+recite
+weighs
+geek
+mist
+wales
+padre
+stated
+brooks
+wesley
+purity
+reno
+weekly
+survey
+bender
+feeds
+polar
+athens
+allied
+valid
+ronald
+jock
+truce
+tribes
+alma
+tumor
+madly
+spooky
+riches
+liars
+erased
+para
+pas
+oceans
+nova
+bums
+hag
+sonar
+gandhi
+trusts
+funk
+lining
+motors
+fills
+tummy
+stash
+tel
+patron
+hump
+kite
+irony
+carved
+tutor
+tanner
+traced
+crowns
+detect
+pots
+locals
+perish
+cloak
+hast
+baths
+chops
+spicy
+caves
+wed
+plains
+cam
+pong
+crude
+consul
+exits
+bronx
+juicy
+morals
+valve
+melted
+bash
+zoom
+farms
+slips
+halls
+dicks
+gamma
+roles
+mccoy
+employ
+cooks
+arch
+ole
+bangs
+scrape
+solemn
+blend
+ounce
+typing
+toto
+mein
+raven
+update
+stool
+shades
+necks
+lime
+honors
+boar
+teller
+grams
+hare
+pact
+immune
+sawyer
+sponge
+genes
+heed
+yank
+bio
+truman
+yoga
+apes
+pins
+bryan
+caller
+saturn
+joints
+cuff
+adding
+beta
+trench
+bakery
+newest
+wink
+depart
+disc
+bah
+fuhrer
+panda
+kettle
+tsar
+leash
+gadget
+abbey
+dub
+hull
+verge
+haunt
+ban
+gravy
+buns
+openly
+prank
+asthma
+annoy
+martyr
+doggie
+sicily
+poets
+knox
+peg
+tart
+nod
+helm
+pest
+upbeat
+crate
+warner
+dwight
+pinned
+posts
+icy
+babes
+occupy
+styles
+rover
+squash
+seals
+morale
+beacon
+prep
+cairo
+czech
+rests
+alarms
+poorly
+mend
+essay
+pies
+paws
+hums
+ethel
+denial
+bikes
+rib
+mao
+snorts
+cereal
+rene
+tonic
+depot
+eater
+pip
+milky
+hasty
+rattle
+greta
+pea
+debris
+wedded
+coral
+snacks
+tens
+fewer
+ida
+berry
+eagles
+castro
+tavern
+stench
+syrup
+donor
+elf
+falcon
+firmly
+reel
+harlem
+erin
+yonder
+shaken
+summit
+guru
+suited
+whilst
+smelly
+remark
+loans
+stages
+maine
+boost
+spits
+remedy
+jockey
+errand
+kilo
+maids
+oppose
+slot
+bred
+nevada
+foods
+dwell
+stored
+roam
+laurel
+hunk
+hyde
+viva
+freud
+vent
+posse
+occurs
+squire
+serge
+tex
+chump
+hallo
+morton
+crappy
+tying
+chores
+rodeo
+pickle
+packet
+noel
+tenant
+echoes
+paddle
+stains
+hoover
+marx
+sod
+tapped
+wrists
+jap
+amos
+revs
+tee
+nearer
+limb
+laden
+eddy
+fading
+rental
+doodle
+trek
+seeks
+defy
+groove
+steed
+gents
+creaks
+jets
+demo
+piper
+bamboo
+dye
+tuned
+paw
+moons
+tyrant
+tunes
+guided
+adapt
+cork
+cosmos
+revoir
+sermon
+lisbon
+kat
+poetic
+patsy
+dread
+hooks
+menace
+fasten
+rosy
+bulb
+clutch
+reg
+raises
+raging
+ceo
+quiz
+miners
+links
+parted
+utmost
+utah
+squirt
+loaf
+lam
+lava
+outlaw
+donate
+voodoo
+lists
+learns
+reds
+argued
+beads
+foam
+goody
+decree
+hazard
+uptown
+batch
+bosom
+hicks
+bases
+dowry
+meteor
+flare
+strung
+adds
+weirdo
+saul
+raja
+corny
+anthem
+batter
+aimed
+groan
+squid
+moms
+cadet
+revolt
+chum
+greasy
+meow
+mat
+tar
+islam
+raving
+sow
+lays
+bugle
+maxim
+howl
+herbs
+cutie
+aces
+garcia
+cetera
+limbs
+adolf
+weaker
+lice
+boiler
+peters
+dorm
+mourn
+screws
+docks
+regain
+cocoa
+valet
+shapes
+goofy
+excess
+wimp
+felony
+siege
+comics
+cheeky
+soy
+iced
+chubby
+prop
+muddy
+reagan
+taped
+slate
+shogun
+impose
+sank
+rudder
+sized
+steep
+hash
+windy
+grabs
+froze
+slash
+arctic
+chic
+spades
+crunch
+stared
+canned
+hopper
+tad
+dames
+nero
+jinx
+domain
+noodle
+stuffy
+yin
+angles
+email
+frau
+skates
+rigged
+grunt
+epic
+choked
+veer
+hurray
+swings
+harmed
+caress
+convey
+nobel
+whoop
+fling
+abbot
+darker
+faking
+sneaky
+manor
+puss
+caps
+danish
+mace
+daft
+multi
+jumbo
+scales
+stance
+blush
+touchy
+apiece
+banged
+grub
+asset
+skirts
+mutiny
+enjoys
+casper
+radius
+nerd
+thigh
+masked
+copied
+nosy
+crust
+colder
+sleigh
+joanne
+skins
+wiser
+moor
+flop
+serum
+geisha
+bronze
+zen
+jude
+fiery
+undone
+norm
+snuck
+carve
+pluck
+jab
+par
+loco
+matrix
+facial
+legged
+bled
+seller
+tame
+pry
+inmate
+ridden
+snitch
+sands
+tung
+rouge
+alias
+abide
+skunk
+yahoo
+relay
+blaze
+rained
+ere
+foe
+coca
+mosque
+sap
+eerie
+chant
+cod
+bing
+salty
+dirk
+mart
+bowls
+tribal
+spade
+awaken
+flyer
+dane
+recess
+drip
+lira
+dew
+riots
+soak
+heated
+tempt
+stein
+marcia
+oregon
+spears
+brag
+poking
+teen
+orion
+fiend
+abyss
+pumps
+moan
+coyote
+jumper
+clam
+broker
+jaws
+tread
+rust
+subs
+unreal
+witty
+buffet
+loony
+lunar
+vacant
+riders
+shores
+litter
+ramp
+pup
+fin
+cinch
+rye
+lenin
+gunner
+mold
+gays
+risen
+lamps
+sierra
+humbly
+slob
+slay
+ripper
+whim
+dryer
+zipper
+vocal
+ankles
+lama
+rep
+elect
+spaces
+faded
+armour
+vicar
+grape
+sacks
+wrench
+squeak
+rhodes
+xavier
+elves
+jared
+dense
+sioux
+adored
+cub
+genie
+arc
+manly
+bikini
+veal
+cohen
+palms
+weaver
+mink
+nag
+turbo
+vermin
+diesel
+mutant
+meadow
+moody
+geese
+emerge
+ultra
+loft
+squat
+shoved
+harp
+okey
+bumper
+ahoy
+adios
+trunks
+dairy
+weave
+wilt
+awe
+cello
+jug
+pits
+amused
+inc
+flick
+swam
+rites
+wider
+flats
+pros
+titles
+builds
+moo
+flap
+tiring
+voters
+groovy
+yelps
+barb
+sphere
+ingrid
+lassie
+beams
+baking
+psych
+slain
+downs
+lakes
+hindi
+hassle
+hale
+omen
+hindu
+laptop
+runt
+shady
+grin
+uneasy
+forge
+linen
+plato
+hacked
+sinful
+howls
+bout
+braces
+uncles
+curb
+hub
+lawful
+dusk
+nile
+hippie
+wails
+viking
+slope
+cubs
+aurora
+bounds
+omega
+muffin
+mules
+cables
+stem
+diaper
+baton
+clocks
+licked
+camels
+plasma
+routes
+farce
+ludwig
+pools
+fags
+gauge
+sensed
+lever
+ferris
+layers
+brink
+canoe
+beetle
+filter
+canary
+senile
+mails
+antony
+tally
+radios
+snappy
+mann
+pluto
+germs
+thump
+blares
+rim
+rested
+steaks
+deuce
+snowy
+sully
+lacks
+maple
+cube
+pep
+outs
+unpack
+eel
+geezer
+crisp
+faults
+dost
+patent
+sect
+grande
+spank
+jumpy
+pumped
+tends
+dakota
+debut
+titus
+rows
+layout
+cling
+weeds
+trifle
+oblige
+wilder
+ration
+dots
+pens
+taps
+knob
+eta
+scold
+sizes
+wright
+nets
+jacks
+digs
+rake
+import
+brew
+manu
+sham
+laps
+woken
+goon
+posing
+hazel
+grumpy
+paints
+chunk
+atoms
+skulls
+minnie
+ese
+hoax
+gals
+tents
+ivory
+ogre
+quits
+shah
+fluffy
+duffy
+astray
+dos
+barren
+crates
+filing
+lark
+herald
+slimy
+ares
+horrid
+edited
+cramp
+sued
+brats
+govern
+detour
+shush
+logs
+vine
+paso
+carver
+tinker
+fowler
+zurich
+trans
+zebra
+mating
+aerial
+muck
+notre
+hawks
+attach
+sauna
+safest
+douche
+gem
+rubles
+viper
+stingy
+scalp
+devote
+rhymes
+fidel
+cloudy
+asap
+aging
+redeem
+chute
+mambo
+nato
+inhale
+pastry
+widows
+unions
+ezra
+fusion
+budge
+sitter
+gavel
+ave
+revive
+scroll
+bruise
+joys
+deacon
+bonnet
+cone
+fossil
+dublin
+tug
+wench
+shaky
+tram
+plunge
+wail
+tucked
+hacker
+retain
+homey
+deb
+fleas
+iraqi
+disney
+edges
+fret
+notch
+mayo
+wept
+bridal
+mentor
+parcel
+cages
+mush
+polls
+zodiac
+liras
+krauts
+prizes
+scarce
+gears
+beak
+lifts
+uphold
+errors
+folly
+export
+uno
+taco
+casket
+grieve
+lest
+titan
+bumps
+biggie
+bop
+kimono
+banjo
+bows
+toot
+griff
+clone
+merger
+broads
+lass
+napkin
+sen
+gao
+tibet
+noose
+blinds
+fender
+finale
+hymn
+flaw
+afar
+warmed
+mashed
+slides
+adieu
+pagan
+joyful
+heist
+bowie
+paste
+bummer
+sirs
+trance
+famine
+tablet
+clause
+pascal
+maze
+clarke
+stale
+cactus
+folded
+tours
+din
+domino
+troupe
+pronto
+amulet
+attila
+olives
+decay
+morse
+vouch
+tiles
+itchy
+pedal
+quarry
+thor
+grail
+puck
+goons
+mailed
+artery
+props
+needy
+haw
+rated
+calmed
+toots
+intern
+thuds
+pushes
+vivid
+fishes
+ounces
+eiffel
+ufo
+longed
+brow
+bach
+hooper
+moth
+tags
+dads
+labs
+magnet
+aching
+riggs
+insert
+tones
+buyers
+aft
+digest
+legit
+furry
+defect
+bumpy
+sprung
+leaks
+climax
+henrik
+slater
+tasks
+ledge
+cliffs
+fours
+dona
+acute
+crater
+ordeal
+giddy
+marvel
+wiping
+suing
+portal
+lesser
+vein
+athena
+heater
+feeble
+racial
+gunnar
+cawing
+joins
+rumble
+barker
+crummy
+fabian
+basin
+hiking
+finer
+corky
+rating
+edit
+aloud
+tao
+futile
+derby
+bearer
+lotion
+cove
+hilary
+lefty
+whines
+deploy
+rubbed
+cutest
+laddie
+venom
+picky
+una
+snot
+sneeze
+bailed
+crotch
+aunts
+arabia
+capone
+troll
+opener
+robes
+verbal
+overdo
+emery
+rubble
+blanks
+lenses
+module
+clash
+sash
+goof
+spur
+frames
+blokes
+rom
+hounds
+walkie
+dung
+mellow
+buff
+condo
+valium
+yogurt
+cuddle
+bypass
+evolve
+sanity
+isle
+labels
+unseen
+obeyed
+verses
+sage
+mast
+snob
+ref
+kosher
+kisser
+posh
+comply
+span
+nutty
+scoot
+muller
+mort
+snuff
+sled
+extras
+input
+teeny
+decoy
+irons
+timber
+devour
+dart
+growl
+aches
+thorns
+creed
+elbows
+vista
+chewed
+sums
+marrow
+voila
+rite
+frying
+taj
+jog
+wiggle
+tat
+mecca
+saucer
+booing
+apron
+index
+gabby
+reset
+spoilt
+salsa
+fudge
+fences
+kaiser
+trot
+salami
+meows
+pear
+drying
+spoils
+softer
+mar
+floats
+knack
+surge
+spoons
+ulcer
+trader
+vie
+jive
+tofu
+pecker
+buffy
+orchid
+piles
+differ
+craven
+pricks
+hutch
+staged
+lambs
+cons
+glued
+yanks
+tack
+hangar
+neighs
+mortar
+oyster
+shred
+gram
+saliva
+texts
+wand
+cot
+renew
+assign
+coo
+acre
+fay
+lair
+boxers
+lyon
+aloha
+aide
+onward
+fitted
+sliced
+slogan
+gallon
+paces
+decker
+user
+rugby
+plank
+gogh
+drunks
+sling
+margin
+raids
+chord
+knit
+munch
+myrtle
+mater
+salaam
+delete
+drone
+spinal
+matron
+hansen
+badger
+greens
+mantle
+viola
+hoist
+ore
+makers
+thirds
+kip
+chug
+admits
+deceit
+rinse
+ell
+stoop
+slowed
+comfy
+lei
+hisses
+gender
+gore
+johns
+adores
+dire
+aryan
+hens
+smug
+haines
+versa
+duct
+pillar
+buds
+swede
+ins
+tucson
+civic
+diver
+bowman
+oddly
+speedy
+iowa
+hark
+omaha
+manure
+ballad
+trend
+stump
+refund
+gorge
+guides
+axis
+mystic
+baboon
+hoot
+snail
+faked
+giggle
+vale
+ceased
+pug
+crumbs
+snails
+hoop
+strive
+unjust
+mugs
+offs
+bolts
+fiber
+leaked
+brawl
+brook
+excite
+saigon
+gator
+homage
+wholly
+stomp
+balboa
+truths
+sparky
+cosmo
+dumps
+cheats
+cupid
+brig
+dada
+emmy
+puddle
+cleans
+pulp
+hosts
+wisely
+sacked
+rivals
+nicked
+amends
+expel
+bribed
+hugged
+rave
+humane
+curls
+muse
+earns
+guild
+sinks
+bey
+taro
+clamp
+covert
+manson
+spat
+gall
+decks
+ponies
+wills
+crave
+helper
+stung
+sturdy
+tights
+crest
+cooing
+washes
+absorb
+tuning
+holder
+marsha
+lowly
+poe
+omelet
+ensign
+ether
+welsh
+melts
+polka
+buggy
+furs
+solely
+postal
+grazie
+curves
+digger
+flyers
+poodle
+phoney
+havoc
+inland
+tahiti
+smear
+wildly
+loathe
+lumber
+wiener
+albums
+spook
+messes
+valued
+crazed
+pads
+stride
+scot
+spouse
+louse
+macy
+gunman
+pepsi
+detain
+render
+shaman
+morbid
+raided
+whine
+demise
+darned
+barbed
+writ
+mugged
+morrow
+sewers
+reload
+neon
+garret
+elmer
+basics
+lain
+saxon
+dunk
+safari
+pager
+reflex
+sonic
+zones
+reaper
+unfit
+boils
+notary
+stormy
+stag
+wines
+darts
+kelvin
+bugged
+aviv
+curl
+accord
+fig
+shawl
+trump
+caste
+gravel
+popeye
+mil
+arson
+bodily
+miner
+talker
+peril
+regent
+meddle
+lagoon
+stings
+abc
+fink
+rails
+flaws
+midday
+leone
+widely
+dues
+sordid
+galley
+barley
+shalom
+moist
+champs
+feat
+reeves
+herbal
+denies
+intro
+joyous
+jest
+hun
+rabble
+hades
+tuxedo
+bribes
+hulk
+hostel
+borne
+alps
+snoop
+encore
+upsets
+hogs
+quota
+carton
+dell
+wiring
+lima
+burma
+vortex
+posed
+edison
+huddle
+flares
+mascot
+garde
+fluids
+sedan
+peer
+inject
+petals
+reap
+hugs
+plow
+newark
+hermit
+forum
+saga
+bulk
+lacked
+dodger
+cavity
+bogus
+apt
+rugged
+slag
+rhino
+cosy
+ducky
+exhale
+hind
+fumes
+payday
+snarls
+cecil
+smoker
+server
+vowed
+donuts
+mango
+floods
+boyle
+slum
+satin
+jackal
+clink
+grants
+icon
+dicky
+hedge
+hooves
+attain
+shabby
+cures
+spiral
+clover
+swears
+wacky
+misty
+lug
+tilt
+unkind
+rocker
+dreary
+gurney
+rigid
+exiled
+boon
+oasis
+jacked
+speeds
+castor
+loaned
+broth
+rah
+ozone
+rubin
+bathed
+frown
+curses
+snore
+blames
+trails
+jell
+peeled
+penal
+leech
+mow
+chimp
+saudi
+speck
+array
+foxy
+slices
+locket
+inter
+poorer
+cider
+smokey
+shiva
+triad
+floss
+vinci
+divert
+poll
+orient
+frenzy
+airs
+enlist
+wham
+waved
+rods
+holdup
+cranky
+hinges
+swung
+aids
+ratio
+gains
+nosed
+plots
+clips
+latter
+divers
+sweeps
+wad
+carts
+nobles
+unwell
+timid
+shun
+hybrid
+foxes
+fern
+gases
+tumble
+unfold
+bowen
+ante
+tanker
+tacky
+sesame
+peers
+bursts
+corral
+pouch
+serene
+untrue
+pushy
+linger
+sluts
+frail
+calves
+nectar
+boast
+franks
+cutler
+surfer
+ports
+roofs
+coy
+lilies
+hick
+clams
+malice
+yawns
+penn
+latch
+magnum
+jaguar
+chino
+vulcan
+stares
+scraps
+spun
+whiff
+spree
+basque
+amaze
+gaius
+rican
+levi
+rand
+parson
+rewind
+yogi
+afro
+yak
+delays
+fulfil
+goblin
+aura
+owning
+beirut
+bog
+watts
+spices
+tailed
+hearty
+rabies
+relic
+forks
+shocks
+slugs
+mingle
+gloom
+rung
+herds
+plugs
+tactic
+badges
+sassy
+chords
+levy
+bids
+cubes
+slows
+pizzas
+glide
+tic
+amigos
+combo
+oracle
+sided
+gen
+specs
+qui
+timed
+logo
+rowing
+mag
+pilar
+bogey
+tile
+healer
+fatter
+barred
+quaint
+haze
+vents
+pans
+costly
+mirage
+refill
+cheesy
+wedge
+rotate
+odor
+tarts
+curt
+rocked
+mound
+pious
+gala
+whoosh
+twig
+sahara
+utopia
+hornet
+ethnic
+zed
+patio
+jars
+manic
+fussy
+outrun
+wary
+detest
+naming
+stout
+friar
+quebec
+micro
+goner
+anew
+bowels
+czar
+tov
+cartel
+taxis
+imply
+zulu
+cluck
+wasp
+plano
+enable
+rook
+spins
+pinkie
+hubby
+dialed
+carnal
+judo
+clans
+alms
+dreamy
+barrow
+midway
+sox
+hoods
+crock
+soften
+drapes
+elk
+gobble
+dears
+hither
+snort
+micah
+plumb
+scorn
+moods
+gras
+blasts
+mammal
+temp
+entity
+flaps
+blur
+sentry
+dearie
+merge
+shreds
+zap
+gant
+stunts
+toil
+owls
+slab
+talkie
+twitch
+jester
+lineup
+bono
+edgy
+gigs
+wraps
+vendor
+tout
+plaque
+slums
+rural
+keg
+gaul
+cyborg
+flakes
+seaman
+fungus
+sheik
+sewed
+yap
+thaw
+pap
+lemons
+chills
+gaming
+potent
+oars
+bleak
+heath
+hearse
+spurs
+mayhem
+dazzle
+tabs
+piled
+strand
+weaken
+muzzle
+typed
+lorry
+thrive
+ballot
+rink
+nil
+lured
+rhode
+hoops
+sleazy
+faucet
+dharma
+stork
+irs
+starry
+picket
+possum
+gehrig
+reggae
+hula
+tombs
+doves
+begs
+petit
+riff
+mutton
+gags
+hive
+chunks
+wreath
+bazaar
+psyche
+dipped
+solace
+oxen
+vested
+malt
+disarm
+rhea
+meek
+mormon
+cedar
+gent
+unborn
+bestow
+plight
+cadets
+stat
+urn
+porky
+gemma
+donut
+retail
+peking
+brunch
+honky
+refers
+thrash
+banish
+sensor
+flair
+overly
+assess
+drool
+icebox
+holed
+jiffy
+output
+frosty
+carp
+kroner
+racism
+clang
+folder
+helium
+stalk
+lanes
+duo
+lash
+mooing
+ragged
+fangs
+sera
+wigs
+brood
+dukes
+pantry
+whirl
+purge
+bleach
+snores
+shiver
+alba
+winged
+brooch
+dyed
+bulbs
+quill
+racer
+dinars
+merits
+maggot
+washer
+plump
+reopen
+coded
+fluke
+molten
+peaks
+payoff
+doses
+strips
+minors
+pours
+teens
+pimps
+shone
+timers
+dory
+blazes
+comma
+slaps
+toledo
+nausea
+elijah
+venue
+rushes
+hauled
+rents
+wring
+louvre
+twain
+maddox
+rosary
+chime
+lumps
+infect
+tux
+mead
+hitter
+harass
+grad
+reins
+namely
+lodged
+slayer
+clerks
+crypt
+trough
+calms
+shrewd
+grains
+dover
+trades
+neatly
+vogue
+occult
+hombre
+berg
+bern
+bends
+volts
+vines
+spleen
+ashton
+sao
+soar
+unrest
+ravine
+skis
+gazing
+relish
+arcade
+stalls
+prune
+muster
+pence
+genre
+maitre
+amidst
+tidal
+pox
+fished
+cashed
+tvs
+gill
+heals
+canton
+prima
+cooled
+bonny
+wraith
+kappa
+oslo
+gauls
+gowns
+canon
+digits
+straws
+awoke
+prone
+urged
+lax
+lard
+halo
+lush
+usher
+tagged
+arouse
+skid
+pilate
+wick
+dud
+dimes
+lupin
+roper
+beeper
+saber
+quo
+hiss
+salem
+squads
+walnut
+fats
+keats
+idaho
+casing
+rooted
+cretin
+ample
+persia
+caliph
+envoy
+eskimo
+gage
+stokes
+biker
+tori
+bleeds
+cooped
+viable
+gulls
+oats
+tides
+pooch
+torah
+goalie
+coil
+gulps
+arose
+albany
+swells
+lyons
+loch
+yippee
+perch
+bookie
+avatar
+lazar
+urges
+felon
+awol
+eaters
+gigolo
+wrongs
+uproar
+ruckus
+ami
+fakes
+heirs
+nerds
+mazel
+taping
+owing
+paced
+strife
+aroma
+grocer
+relics
+ringer
+mccall
+bras
+cougar
+frigid
+towed
+spilt
+firms
+quail
+slopes
+majors
+gums
+opal
+newt
+sodium
+loner
+jailed
+thames
+jig
+myths
+rips
+chit
+sari
+suitor
+corpus
+rubies
+raf
+leper
+yearn
+seine
+boa
+bali
+mesa
+deli
+juno
+mets
+swarm
+poses
+edible
+tramps
+feud
+outing
+foggy
+mash
+gems
+colon
+nix
+salts
+elope
+hindus
+dwarfs
+seeker
+bene
+weir
+trojan
+rapper
+nab
+locke
+fringe
+aims
+manila
+casts
+bidder
+fins
+sewn
+conned
+kahn
+evils
+nappy
+tulip
+hyena
+buick
+hunts
+pups
+gulp
+flashy
+fuzz
+parry
+conn
+atone
+nigh
+mite
+abode
+hyper
+nugget
+leaned
+hors
+renoir
+paging
+bison
+jerky
+diva
+cicero
+oaks
+pollen
+lyra
+wipes
+hock
+suns
+fates
+glare
+ado
+lashes
+rulers
+cramps
+emir
+heresy
+brides
+quince
+envied
+outlet
+lumpy
+horde
+chimps
+tended
+flier
+mats
+rodent
+stow
+inning
+bolted
+bogart
+germ
+hap
+weber
+enigma
+rev
+tory
+honda
+hectic
+hopped
+swiped
+coarse
+swamps
+soothe
+fluff
+sprout
+disks
+neural
+swims
+puffs
+splits
+snows
+viewed
+pints
+flask
+hanky
+warms
+mane
+ruddy
+puffy
+mala
+dumber
+dunes
+straps
+wither
+crafty
+slop
+scab
+faulty
+queers
+atlas
+moors
+buoy
+pebble
+valves
+panels
+gaulle
+vacate
+pun
+chaser
+deluxe
+breeds
+puny
+gallop
+canopy
+vive
+orb
+pres
+baa
+geiger
+gusto
+rumba
+graces
+vector
+tolls
+ware
+weeps
+strut
+turnip
+siam
+swoop
+dept
+stacks
+perks
+rabid
+audit
+candid
+glum
+ussr
+dole
+burner
+sequel
+borg
+largo
+clank
+uterus
+users
+sulfur
+exceed
+walrus
+mixer
+simmer
+bongo
+dow
+gel
+dahlia
+spawn
+gentry
+pranks
+foil
+whips
+mangy
+piazza
+ticks
+ploy
+beagle
+welch
+scuba
+tenor
+chokes
+tingle
+turin
+bowed
+sac
+melons
+cuss
+grover
+liege
+fronts
+oaf
+spikes
+oft
+liner
+crumb
+rica
+upward
+cycles
+impure
+hurl
+craps
+shucks
+peachy
+berth
+drains
+birch
+kebab
+limbo
+skater
+odin
+jag
+danube
+rut
+quotes
+yarn
+ratted
+wharf
+cabs
+sumo
+pointy
+eels
+sever
+hex
+chaste
+dosage
+pubic
+vices
+soles
+hoof
+kinder
+twos
+quoted
+hairdo
+jihad
+kiev
+banzai
+duly
+snout
+yon
+prays
+lag
+abner
+hippo
+yorker
+fiesta
+rowdy
+burps
+pusher
+auld
+deemed
+swipe
+jigsaw
+chirps
+draper
+stun
+dei
+taffy
+flowed
+blower
+plough
+thrice
+avail
+isaiah
+afloat
+trait
+fated
+crick
+algae
+hijack
+indies
+bayou
+hippy
+fetish
+herpes
+chases
+afghan
+moths
+homing
+lapse
+clergy
+itches
+embark
+puking
+minion
+trusty
+daffy
+cabins
+staked
+fiasco
+gills
+amour
+fouled
+cords
+oval
+arises
+unsure
+funded
+soho
+soiled
+squawk
+fickle
+repaid
+iceman
+bony
+twit
+jams
+redo
+python
+hanks
+snag
+unholy
+cuter
+cubans
+mower
+crispy
+pang
+fester
+iodine
+hints
+hussy
+thong
+hem
+plait
+desks
+poked
+chrome
+leaps
+grange
+glitch
+mercer
+gunned
+duet
+bashed
+glands
+drags
+podium
+coney
+prying
+nos
+haunts
+elm
+catchy
+juices
+sci
+alamo
+fetus
+lucid
+fixes
+canine
+armada
+medina
+liters
+whims
+cram
+deduct
+mumble
+manned
+novice
+sewage
+tinkle
+torso
+bowler
+briar
+flake
+rejoin
+snug
+dual
+piling
+clot
+cocked
+moat
+yam
+hermes
+brit
+theta
+serbs
+murmur
+trolls
+huts
+mould
+scans
+rift
+reefs
+croak
+mont
+attire
+spout
+fuses
+mantis
+dries
+finder
+vary
+drills
+beards
+rummy
+waffle
+tangle
+prompt
+funnel
+tubby
+cantor
+threes
+aggie
+moped
+embryo
+bowel
+fend
+screwy
+traits
+invoke
+morn
+hooch
+boxed
+winch
+camper
+ankara
+baht
+fined
+hooky
+looker
+odessa
+misled
+shamed
+naught
+dune
+reside
+tau
+camped
+ceases
+flunk
+amid
+crept
+rhine
+sphinx
+fiver
+fives
+sulk
+staten
+snotty
+crutch
+gothic
+vans
+graze
+paved
+relive
+caper
+nests
+proofs
+madden
+chants
+keaton
+shroud
+raced
+bran
+rad
+bijou
+marek
+bores
+ascend
+shrunk
+unwise
+tulips
+chopin
+aspire
+actin
+bails
+mousse
+quint
+valor
+purest
+themes
+dopey
+hobson
+sixes
+topped
+smelt
+damsel
+metals
+gemini
+blitz
+tamed
+pears
+rustle
+snowed
+indoor
+pail
+typhus
+mit
+turban
+boldly
+nibble
+foes
+beige
+aiding
+reacts
+whence
+mime
+clangs
+grit
+abrupt
+sheikh
+sickly
+verb
+fibers
+barf
+purify
+willed
+blurry
+zeros
+stub
+cipher
+weakly
+youths
+genoa
+gaps
+quart
+umpire
+lingo
+adrift
+nitwit
+sadist
+flung
+chefs
+quake
+creak
+vita
+flawed
+mach
+equity
+wards
+oily
+fie
+raisin
+bough
+privy
+batty
+defied
+gaping
+coping
+roster
+mans
+devout
+viewer
+koran
+bagged
+corned
+braid
+sodas
+replay
+wart
+bossy
+visas
+paine
+medics
+sexist
+tuba
+logged
+chisel
+pigsty
+prix
+anglo
+smudge
+pseudo
+ferret
+nebula
+feisty
+riled
+jolt
+latino
+chests
+slang
+ion
+laces
+vial
+hasten
+sax
+alto
+mags
+aspen
+bagel
+lotto
+nabbed
+reeks
+spied
+hover
+alleys
+brands
+tycoon
+cad
+harlot
+mended
+baggy
+stripe
+tact
+siding
+stank
+toxins
+duce
+medusa
+tenure
+pegged
+uglier
+pliers
+smithy
+fathom
+evade
+dismal
+bubbly
+bowing
+hype
+chunky
+piracy
+swans
+celery
+cob
+tacos
+gland
+silky
+dashed
+mania
+fluent
+slew
+druid
+frisk
+bead
+lobe
+loins
+fryer
+beret
+unsafe
+annoys
+ponder
+shes
+armory
+caged
+pines
+barman
+ironed
+taft
+otter
+amuses
+crease
+sutra
+retro
+lasers
+bikers
+toke
+ufos
+faust
+folds
+untied
+climbs
+creams
+gunmen
+tripe
+hiccup
+induce
+barter
+bronco
+warped
+stems
+pods
+brazen
+cleric
+cubic
+cortex
+fleece
+reigns
+cavern
+bonded
+lout
+mushy
+soot
+binds
+raffle
+primal
+lawman
+nylon
+digit
+quench
+lofty
+sloth
+java
+tipsy
+halves
+ignite
+blanc
+flips
+aloft
+cones
+tulsa
+yeti
+groin
+ranked
+linden
+vaults
+dodo
+hymns
+bale
+cabot
+brando
+measly
+creamy
+ledger
+rugs
+cokes
+bummed
+selves
+tweet
+mocked
+ardent
+juror
+laced
+unpaid
+fad
+snip
+teased
+eject
+gutted
+fines
+burp
+aqua
+trendy
+prowl
+giver
+bitchy
+jails
+graft
+lyric
+wasps
+marino
+vegan
+synced
+zion
+vitals
+sender
+cuddly
+prof
+dined
+mildly
+plated
+kennel
+gopher
+nev
+uncool
+elixir
+pleas
+mimics
+juggle
+eros
+takers
+taipei
+phi
+mendel
+almond
+sweats
+tarot
+sparta
+zeta
+gull
+keel
+slid
+mumbo
+swedes
+solves
+pocus
+snare
+smacks
+loon
+rec
+socket
+unison
+calmer
+sonata
+teapot
+smarty
+witted
+figs
+vixen
+dorado
+seams
+claps
+condor
+vibes
+luau
+sneaks
+pining
+clair
+corset
+sable
+alpine
+plums
+repel
+obi
+cools
+grips
+rotted
+nursed
+suede
+famed
+gassed
+volga
+beryl
+pooped
+twists
+curled
+titans
+lament
+dieter
+sic
+filly
+toddy
+rots
+phobia
+varied
+klan
+intake
+uss
+viral
+fruity
+wrecks
+braver
+drones
+taker
+hires
+flex
+suburb
+moro
+wanton
+ditto
+shay
+sup
+hyenas
+woes
+hue
+joss
+maxi
+movers
+puked
+glee
+wisest
+strays
+miser
+axes
+corona
+combed
+hocus
+dill
+bilge
+coals
+fiscal
+moles
+aria
+ruff
+dinky
+bona
+zing
+jurors
+hanger
+insure
+fliers
+bugler
+rouse
+chore
+ruse
+dainty
+canaan
+urging
+ganges
+bwana
+wield
+dab
+lockup
+pacing
+pus
+flak
+putt
+robs
+blooms
+dubbed
+vat
+ripple
+drowns
+pant
+levine
+pelvis
+fresno
+mined
+lurch
+rein
+upload
+jokers
+spaced
+dangle
+rapids
+licks
+yawn
+yip
+fares
+warts
+bengal
+fiends
+bossa
+cynic
+sears
+taurus
+sunup
+ducats
+sodom
+paged
+avant
+stirs
+eights
+mumps
+brahms
+bower
+vipers
+hazy
+femme
+trois
+unwind
+twerp
+donors
+sui
+kicker
+essays
+teal
+grammy
+jagger
+agile
+cordon
+vigil
+sores
+amiss
+waxed
+rogues
+sawed
+moping
+eureka
+mishap
+sledge
+canals
+swab
+camden
+tanned
+tartar
+fir
+napalm
+blip
+fora
+diem
+nines
+icing
+doings
+uphill
+devoid
+tho
+girdle
+smoky
+lowers
+operas
+dingo
+sevens
+yeast
+kaput
+twigs
+fiat
+wag
+savor
+savvy
+navel
+exert
+biased
+hoe
+char
+mullet
+mayan
+tiara
+rubs
+yeller
+guise
+untold
+pari
+sheds
+teas
+ninny
+brewer
+exempt
+babble
+rusted
+prissy
+rapes
+oink
+swami
+duds
+lewd
+plural
+fray
+eloped
+oar
+trays
+quilt
+frock
+enmity
+newer
+nosey
+guppy
+prose
+nudge
+grubby
+polio
+topics
+salted
+begone
+nasal
+verde
+modify
+liter
+gout
+width
+remake
+imam
+idols
+gourd
+bland
+vocals
+virgo
+hydra
+bleeps
+minh
+beaut
+wastes
+musket
+astern
+boars
+bustle
+dramas
+cravat
+braids
+dozed
+grouch
+expire
+bosun
+armpit
+fawn
+albino
+stooge
+ulcers
+petal
+coupon
+def
+arty
+bouncy
+humid
+loops
+jus
+ails
+madmen
+orgies
+frisky
+mosaic
+hep
+hefty
+regal
+amazes
+sickle
+sri
+petite
+gleam
+emblem
+ablaze
+rube
+sown
+relied
+longs
+footed
+libel
+pounce
+jailer
+sugars
+crosby
+grovel
+aught
+perm
+huns
+clad
+uranus
+lager
+wallop
+quiver
+mince
+dilly
+blot
+packer
+robust
+racine
+palate
+alumni
+gook
+injure
+kale
+poised
+greets
+dawned
+censor
+revere
+mongol
+ducked
+bros
+kites
+lister
+durham
+clones
+priors
+buffer
+meaner
+puffed
+bard
+garter
+dally
+putty
+banal
+facade
+pimple
+fisk
+menus
+argo
+gizmo
+mays
+quark
+dazed
+knave
+evenly
+picker
+res
+frolic
+sexes
+dimwit
+ticker
+hickey
+reek
+bins
+dolce
+quad
+splat
+clout
+browns
+douse
+wallow
+shafts
+taint
+yoke
+hordes
+fodder
+stiffs
+benign
+hedges
+byes
+curved
+comets
+cloned
+dusted
+piety
+exodus
+presto
+ornery
+sadder
+scamp
+zeal
+hinder
+afoot
+scurvy
+fide
+toxin
+ghouls
+caw
+sachs
+cappy
+toying
+revise
+stats
+choosy
+swig
+typist
+drafts
+alibis
+boned
+tunic
+yanked
+shifty
+reels
+enzyme
+cocoon
+narc
+sexier
+vinyl
+acne
+vapor
+carte
+refuel
+niche
+stair
+pauper
+deem
+grits
+docked
+deport
+burrow
+cooker
+acorn
+confer
+marlin
+hushed
+sicker
+compel
+coated
+craze
+timely
+jerked
+malls
+hoses
+goethe
+stead
+faro
+lunacy
+unruly
+tier
+fowl
+heaps
+olden
+shaker
+henna
+scots
+orbits
+salads
+squish
+kafka
+expo
+shriek
+nieces
+briefs
+devise
+watery
+grate
+rigor
+duped
+chirp
+undies
+pitied
+rajah
+proxy
+voter
+gable
+dives
+dials
+kits
+hops
+brogan
+papaya
+lahore
+silo
+sundae
+cong
+faxed
+terra
+deux
+padded
+glows
+faker
+anvil
+gar
+curing
+waive
+sprint
+subdue
+jugs
+ailing
+priced
+goto
+curie
+soggy
+cranes
+runny
+hanoi
+cheery
+trashy
+balm
+deity
+celtic
+befall
+lepers
+unites
+wobbly
+arches
+flabby
+bibles
+binge
+grazed
+enema
+brutes
+mardi
+rages
+census
+relies
+vests
+gab
+bias
+drowsy
+crawls
+toads
+bois
+mounts
+dotty
+suntan
+roost
+starch
+spiked
+slots
+shimmy
+mew
+tampa
+kiosk
+flops
+finely
+odious
+col
+spills
+getup
+suites
+bulge
+triads
+deduce
+dipper
+capped
+lupus
+nobis
+prod
+fuels
+deejay
+iraqis
+amish
+arming
+shiv
+sunken
+radish
+shears
+fused
+strait
+abuses
+godson
+lastly
+manchu
+huff
+garner
+toupee
+lilac
+tokens
+lute
+gloss
+saucy
+aline
+hobbit
+perky
+mortem
+oils
+bunks
+unused
+axle
+muted
+roamed
+mixes
+llama
+talon
+kelp
+tees
+septic
+flicks
+exec
+hearth
+cur
+atop
+assert
+mora
+braves
+pew
+aloof
+pester
+doubly
+hater
+gander
+borneo
+tins
+coupe
+spruce
+gauze
+aztec
+studs
+booker
+hailed
+rarest
+galore
+filet
+soir
+manger
+perk
+pores
+andes
+isis
+sawing
+fumble
+whiter
+gully
+tiptoe
+dias
+purses
+klutz
+mowing
+jiggle
+lube
+antics
+pellet
+lavish
+forte
+asher
+caved
+optic
+concur
+heifer
+sprang
+woven
+jour
+twirl
+crusty
+mints
+wicket
+sorely
+kook
+loaves
+jocks
+hernia
+cliche
+mire
+floppy
+fib
+sandal
+daze
+harpo
+nifty
+probes
+fable
+tiff
+clanks
+bitsy
+obeys
+pelt
+blob
+lope
+magma
+mitt
+serb
+dunce
+pawned
+madder
+hives
+alight
+humbug
+crafts
+creole
+vis
+dinghy
+acorns
+ethic
+smog
+discs
+prude
+shank
+amp
+slump
+gnome
+suave
+kimchi
+latent
+gazed
+homely
+eulogy
+warmly
+moll
+somber
+grimm
+leaky
+meats
+rustic
+bile
+conga
+chatty
+idling
+glazed
+kraft
+slurps
+lends
+cosa
+leapt
+avert
+tract
+ode
+danes
+clasp
+scenic
+stony
+booths
+diddle
+pips
+bowers
+spud
+ebony
+bodied
+covet
+gung
+rector
+mono
+rims
+hilly
+baltic
+housed
+lackey
+seamen
+rani
+waxing
+garbo
+tripod
+liven
+brawn
+minx
+toffee
+infamy
+hemp
+jigger
+dotted
+enroll
+hatter
+carina
+tehran
+gaff
+doped
+spooks
+graph
+ranges
+gipsy
+moines
+widen
+cited
+hoots
+ouija
+decor
+furies
+cater
+combs
+balkan
+tweed
+murky
+uppity
+nighty
+crete
+tremor
+tarp
+grotto
+dwells
+gilded
+phooey
+pianos
+raps
+peddle
+gust
+haggle
+swish
+zephyr
+fixer
+brim
+weld
+flanks
+hilt
+cite
+vane
+loafer
+crass
+evens
+resin
+grouse
+queasy
+oxide
+yuppie
+downed
+titled
+stills
+prized
+trends
+pout
+defies
+roving
+leans
+pearly
+pane
+peaked
+fresco
+soaps
+breezy
+leach
+roe
+rigs
+moos
+annex
+evict
+fonda
+welded
+mod
+ani
+leased
+unplug
+motels
+uneven
+walled
+corp
+racers
+goings
+mitts
+pedals
+edged
+fife
+supple
+honeys
+stowed
+labors
+fueled
+masons
+kern
+ooze
+mimic
+guile
+clothe
+handel
+stoker
+drivel
+pippin
+colds
+copped
+plume
+mapped
+woolly
+towing
+spar
+innate
+busts
+clinks
+farrow
+golfer
+syrian
+sheen
+satire
+pisces
+drover
+because
+nothing
+thought
+believe
+another
+through
+someone
+morning
+talking
+looking
+getting
+without
+already
+brother
+tonight
+friends
+problem
+waiting
+minutes
+married
+against
+working
+exactly
+husband
+trouble
+captain
+anymore
+country
+between
+brought
+welcome
+started
+anybody
+outside
+perhaps
+playing
+telling
+leaving
+promise
+evening
+himself
+darling
+feeling
+serious
+running
+company
+special
+careful
+goodbye
+perfect
+million
+happens
+parents
+alright
+general
+control
+suppose
+strange
+picture
+forgive
+calling
+changed
+explain
+meeting
+finally
+clothes
+officer
+present
+kidding
+imagine
+forever
+charlie
+decided
+mistake
+station
+wedding
+worried
+message
+america
+instead
+certain
+hundred
+english
+history
+michael
+quickly
+sitting
+colonel
+missing
+service
+respect
+stopped
+besides
+forward
+killing
+neither
+whether
+teacher
+protect
+machine
+amazing
+totally
+private
+realize
+singing
+village
+wearing
+walking
+dollars
+seconds
+staying
+driving
+college
+arrived
+nervous
+contact
+writing
+majesty
+further
+flowers
+written
+partner
+address
+learned
+mission
+kitchen
+chicken
+holding
+allowed
+however
+putting
+usually
+destroy
+animals
+herself
+dancing
+reading
+soldier
+dressed
+support
+mention
+charles
+grandma
+screams
+freedom
+chinese
+breathe
+command
+patient
+figured
+weapons
+england
+stories
+sheriff
+keeping
+richard
+natural
+witness
+correct
+earlier
+weekend
+account
+hanging
+deserve
+ringing
+example
+grandpa
+several
+monster
+silence
+prepare
+justice
+letters
+knowing
+society
+russian
+willing
+student
+jealous
+weather
+opinion
+suicide
+biggest
+noticed
+helping
+nowhere
+meaning
+somehow
+towards
+streets
+manager
+suspect
+dropped
+excited
+whoever
+sweetie
+science
+discuss
+spanish
+medical
+pleased
+invited
+purpose
+survive
+ordered
+hearing
+pretend
+numbers
+suggest
+ashamed
+turning
+falling
+release
+fortune
+fingers
+matters
+subject
+selling
+stomach
+british
+opening
+reality
+funeral
+airport
+program
+defense
+project
+checked
+covered
+clearly
+created
+tickets
+burning
+glasses
+foreign
+courage
+yelling
+depends
+warning
+belongs
+success
+reasons
+italian
+journey
+century
+ancient
+factory
+sending
+records
+william
+changes
+heading
+letting
+bedroom
+finding
+revenge
+divorce
+someday
+useless
+awesome
+miracle
+smoking
+deliver
+victory
+planned
+showing
+growing
+reached
+doctors
+request
+passing
+younger
+becomes
+chicago
+members
+obvious
+workers
+touched
+regular
+details
+emperor
+destiny
+enemies
+sisters
+thunder
+watched
+central
+traffic
+process
+carried
+beating
+percent
+license
+offered
+holiday
+theater
+germans
+whistle
+answers
+receive
+uniform
+screwed
+escaped
+hunting
+disease
+mystery
+beloved
+comrade
+section
+fishing
+senator
+healthy
+curious
+staring
+kingdom
+quarter
+secrets
+barking
+concern
+beeping
+picking
+windows
+managed
+passion
+panting
+engaged
+wounded
+treated
+johnson
+surface
+remains
+closing
+moments
+popular
+reports
+chances
+dealing
+pulling
+unusual
+blowing
+charges
+advance
+cooking
+collect
+winning
+wasting
+capable
+confess
+settled
+results
+illegal
+leading
+appears
+knocked
+gasping
+victims
+garbage
+federal
+council
+jackson
+tuesday
+express
+blessed
+bullets
+billion
+wanting
+degrees
+parties
+trained
+robbery
+servant
+foolish
+unknown
+trapped
+unhappy
+crossed
+culture
+failure
+hitting
+library
+sobbing
+greater
+cutting
+quality
+alcohol
+rolling
+nuclear
+proceed
+vehicle
+fashion
+setting
+arrange
+gunshot
+violent
+quietly
+refused
+fifteen
+hurting
+capital
+channel
+parking
+chasing
+anytime
+angeles
+diamond
+torture
+actress
+comfort
+article
+attempt
+kissing
+despite
+scratch
+entered
+concert
+spirits
+surgery
+indians
+pushing
+whiskey
+selfish
+similar
+drawing
+provide
+barbara
+patrick
+affairs
+heavens
+current
+enjoyed
+traitor
+frankly
+stanley
+distant
+accused
+plastic
+landing
+dreamed
+marshal
+trusted
+western
+stephen
+begging
+vincent
+package
+balance
+citizen
+cameras
+routine
+perform
+eternal
+awfully
+figures
+liberty
+painful
+smiling
+admiral
+clients
+warrant
+ability
+thieves
+players
+cleaned
+thirsty
+bitches
+disturb
+trigger
+reminds
+stretch
+average
+worries
+charity
+francis
+species
+moaning
+lessons
+tragedy
+antonio
+martial
+contest
+highway
+vampire
+network
+chatter
+systems
+highest
+related
+classes
+studied
+bottles
+manners
+laundry
+beneath
+schools
+exhales
+fighter
+smaller
+swallow
+removed
+painted
+sounded
+madness
+raymond
+slipped
+effects
+prevent
+warrior
+injured
+shelter
+chamber
+produce
+costume
+succeed
+propose
+complex
+murders
+raining
+granted
+benefit
+replace
+therapy
+fantasy
+charged
+cleared
+custody
+mothers
+version
+counter
+jumping
+prayers
+florida
+rubbish
+romance
+anxious
+naughty
+product
+actions
+testing
+lawyers
+assault
+anthony
+kicking
+squeeze
+crystal
+hostage
+fellows
+extreme
+october
+luggage
+shaking
+cheated
+pockets
+abandon
+washing
+offense
+confirm
+digging
+typical
+shining
+returns
+african
+heavily
+praying
+academy
+matthew
+humming
+honking
+attacks
+capture
+battery
+buzzing
+messing
+classic
+musical
+matches
+lincoln
+dignity
+massive
+shocked
+respond
+comment
+studies
+laughed
+mexican
+leaders
+feeding
+shortly
+happily
+chapter
+mankind
+breasts
+serving
+grabbed
+measure
+illness
+arguing
+retired
+demands
+artists
+blanket
+loyalty
+fooling
+samurai
+perfume
+banging
+bargain
+payment
+dragged
+russell
+drowned
+bernard
+pattern
+pacific
+conduct
+goddess
+engines
+stepped
+visited
+lighter
+apology
+session
+supreme
+masters
+shadows
+fathers
+arrives
+arrival
+wailing
+butcher
+houston
+happier
+joining
+protest
+retreat
+operate
+various
+gravity
+leather
+nations
+embassy
+instant
+penalty
+packing
+giggles
+honored
+halfway
+closely
+testify
+amongst
+achieve
+buffalo
+approve
+islands
+deposit
+strikes
+visitor
+dessert
+awkward
+scandal
+grounds
+causing
+concept
+wrapped
+explode
+cookies
+killers
+surgeon
+raising
+privacy
+sharing
+economy
+existed
+largest
+dearest
+stabbed
+content
+gabriel
+require
+profile
+reverse
+declare
+defence
+planets
+connect
+options
+eastern
+embrace
+shirley
+passage
+buddies
+develop
+intense
+lecture
+counsel
+limited
+painter
+recover
+observe
+examine
+muffled
+persons
+candles
+reserve
+monkeys
+roaring
+episode
+leonard
+worship
+gunfire
+dresses
+choices
+massage
+twisted
+curtain
+helpful
+kennedy
+spotted
+damaged
+dorothy
+heather
+spoiled
+phoenix
+follows
+emotion
+exposed
+bearing
+dentist
+seventh
+january
+excuses
+ceiling
+improve
+magical
+monitor
+lunatic
+genuine
+desires
+jewelry
+crashed
+farmers
+skipper
+stuffed
+obliged
+collins
+include
+amusing
+sailing
+elected
+poverty
+muscles
+objects
+regards
+bicycle
+despair
+trailer
+impress
+strings
+priests
+crowded
+smarter
+britain
+methods
+shotgun
+display
+planted
+blocked
+douglas
+howling
+gallery
+endless
+smashed
+handled
+bandits
+crushed
+pervert
+cracked
+nearest
+orleans
+customs
+formula
+filming
+physics
+sweater
+sighing
+seeking
+circles
+adopted
+verdict
+efforts
+charley
+guessed
+tobacco
+elegant
+cabinet
+closest
+missile
+noodles
+located
+sucking
+maximum
+sausage
+furious
+minimum
+signing
+element
+buttons
+watches
+tension
+bothers
+compare
+welfare
+devoted
+assumed
+fiction
+carries
+harvest
+harmony
+speaker
+pirates
+arizona
+absence
+vicious
+decides
+cocaine
+gesture
+amateur
+whisper
+cousins
+claimed
+cleaner
+savings
+wealthy
+pointed
+sixteen
+bombing
+stadium
+marines
+counted
+divided
+footage
+seattle
+luckily
+lottery
+honesty
+roberts
+whining
+assured
+touches
+balcony
+lesbian
+filling
+primary
+samples
+mansion
+stewart
+precise
+harvard
+resting
+platoon
+pension
+triumph
+experts
+sadness
+conquer
+quicker
+relaxed
+snoring
+signals
+cavalry
+logical
+haunted
+possess
+psychic
+regrets
+presume
+knights
+focused
+wonders
+shooter
+fragile
+sources
+sincere
+legally
+compete
+prophet
+balloon
+receipt
+climate
+consent
+autopsy
+haircut
+reaches
+hunters
+peasant
+rainbow
+colored
+treason
+refuses
+hostile
+threats
+blaring
+pouring
+couples
+screech
+needing
+feather
+pursuit
+turkish
+scaring
+satisfy
+swedish
+detroit
+marilyn
+shuttle
+drivers
+cottage
+applied
+warming
+seventy
+holland
+heights
+corpses
+ruining
+ireland
+tourist
+drunken
+rabbits
+quarrel
+writers
+hardest
+opposed
+convict
+circuit
+chopper
+corrupt
+journal
+travels
+ticking
+bizarre
+anxiety
+gilbert
+storage
+fulfill
+farther
+expense
+jupiter
+genetic
+convent
+rescued
+battles
+contain
+trading
+springs
+profits
+railway
+hallway
+villain
+advised
+despise
+fainted
+delayed
+attract
+judging
+catches
+foreman
+betting
+bowling
+explore
+tricked
+trumpet
+skipped
+dracula
+cowards
+aspirin
+fascist
+rooster
+targets
+harriet
+baggage
+rushing
+pudding
+spencer
+melissa
+reduced
+wishing
+restore
+smelled
+echoing
+hissing
+tearing
+delight
+choking
+crooked
+freaked
+essence
+handful
+madison
+eleanor
+failing
+cheaper
+offices
+robbing
+empress
+phantom
+unlucky
+shorter
+hearted
+griffin
+execute
+pumpkin
+fastest
+duchess
+variety
+cricket
+dealers
+climbed
+feature
+robbers
+whipped
+sinking
+sunrise
+auction
+pressed
+abraham
+finance
+flowing
+blaming
+equally
+dancers
+bedtime
+utterly
+horizon
+crucial
+gregory
+behaved
+greatly
+corners
+clayton
+boiling
+gardens
+grenade
+ignored
+publish
+bathing
+involve
+initial
+brigade
+reunion
+forcing
+peanuts
+whereas
+beatles
+radical
+madonna
+chewing
+maestro
+listens
+antique
+visible
+edition
+healing
+terrace
+organic
+mirrors
+herbert
+torment
+creates
+fitting
+bananas
+tiffany
+cunning
+bubbles
+visions
+decades
+printed
+offence
+deepest
+resolve
+backing
+needles
+solomon
+blossom
+muslims
+jasmine
+lightly
+deceive
+camping
+caution
+mustard
+justify
+hideous
+volcano
+barrier
+marquis
+coroner
+martini
+popcorn
+relieve
+niggers
+casting
+lobster
+insects
+witches
+seasons
+atlanta
+venture
+yankees
+showers
+bathtub
+talents
+slapped
+teaches
+montana
+pitched
+jackass
+risking
+stripes
+crosses
+carrier
+nursing
+dislike
+popping
+longing
+richest
+exhibit
+evolved
+lookout
+bridges
+starved
+gorilla
+longest
+winners
+spilled
+sailors
+chuckle
+borders
+sundays
+chopped
+housing
+pigeons
+inquiry
+tougher
+olympic
+expects
+peoples
+forests
+punched
+shallow
+torpedo
+natives
+shields
+tapping
+blooded
+teasing
+chester
+grocery
+blasted
+rapidly
+rockets
+teenage
+nursery
+lambert
+confuse
+dismiss
+onboard
+gestapo
+bourbon
+shouted
+referee
+wrecked
+liberal
+trainer
+cabbage
+plumber
+slavery
+digital
+puppies
+beliefs
+weeping
+undress
+audible
+scholar
+admired
+tactics
+females
+compass
+tractor
+neutral
+orphans
+cruelty
+gambler
+busting
+suzanne
+pyramid
+blanche
+shaving
+janitor
+tequila
+reflect
+biology
+chimney
+skating
+flights
+pharaoh
+orderly
+consult
+loading
+savages
+devices
+sponsor
+pitiful
+decency
+kenneth
+tribute
+airline
+founded
+outcome
+sinners
+inherit
+pajamas
+predict
+dolores
+israeli
+suckers
+ecstasy
+messiah
+toilets
+disgust
+rocking
+freezer
+cherish
+krishna
+brothel
+shrieks
+dragons
+pleases
+glimpse
+tracked
+hottest
+annoyed
+lighten
+tunnels
+sparrow
+lifting
+tempted
+banquet
+flooded
+impulse
+mutters
+scooter
+vatican
+unarmed
+symbols
+cowboys
+editing
+bombers
+inmates
+insists
+portion
+inhales
+signora
+posters
+burglar
+secured
+coconut
+tremble
+fiancee
+recruit
+creator
+forgets
+militia
+affects
+simpler
+rotting
+provoke
+heating
+condoms
+surfing
+leopard
+elderly
+handing
+guarded
+ketchup
+spiders
+banking
+rebuild
+payroll
+avoided
+drummer
+flatter
+picasso
+peaches
+pumping
+princes
+fairies
+invaded
+studios
+tongues
+hamburg
+quantum
+promote
+scenery
+frances
+idiotic
+cracker
+mercury
+tighter
+bouquet
+oneself
+locking
+leaning
+bidding
+legends
+caravan
+bruises
+hostess
+decline
+courses
+singers
+praised
+advisor
+shutter
+athlete
+crashes
+ripping
+suffers
+octopus
+amounts
+rangers
+damages
+inspire
+beaches
+denying
+rejoice
+walling
+melting
+baghdad
+winters
+mineral
+condemn
+modesty
+daytime
+bandage
+remorse
+drawers
+webster
+unaware
+penguin
+cartoon
+cannons
+handles
+shampoo
+swollen
+monthly
+secrecy
+revving
+jackpot
+biscuit
+summers
+designs
+resumes
+licking
+forrest
+warfare
+easiest
+squeaks
+statues
+postman
+glowing
+diapers
+tripped
+dynasty
+bankers
+dolphin
+roughly
+throats
+vanilla
+critics
+snapped
+rubbing
+gypsies
+bravery
+immense
+proudly
+repairs
+bottoms
+runaway
+carrots
+bugging
+prefers
+squeals
+freeway
+proving
+dangers
+wheeler
+cashier
+cynical
+edwards
+babylon
+uptight
+motives
+prisons
+flipped
+shipped
+funding
+blinded
+steward
+pauline
+lawsuit
+columns
+faculty
+chooses
+convert
+mermaid
+dreamer
+immoral
+context
+memphis
+freight
+escapes
+pennies
+oysters
+sensors
+skilled
+readers
+barrels
+chained
+dungeon
+punches
+altered
+descent
+dumping
+suburbs
+console
+stained
+excused
+oranges
+aspects
+mixture
+viewers
+zombies
+lucifer
+fireman
+timothy
+partial
+insults
+alabama
+sundown
+virgins
+lucille
+kidneys
+amnesia
+forming
+gaining
+tighten
+protein
+scarlet
+upright
+exploit
+doorway
+veteran
+pickles
+extinct
+courier
+beggars
+gallant
+dispose
+tightly
+bangkok
+donovan
+ratings
+drugged
+farming
+titanic
+obscene
+heroine
+crawled
+briefly
+sorrows
+extract
+boredom
+earning
+plaster
+markets
+outrage
+tattoos
+panther
+marries
+breaker
+powered
+serpent
+redhead
+matched
+leaking
+stunned
+culprit
+mounted
+lasting
+desired
+seduced
+getaway
+coolest
+mocking
+chaplin
+schultz
+anguish
+lacking
+pistols
+siberia
+alarmed
+royalty
+lantern
+pitcher
+analyze
+chronic
+indiana
+combine
+inspect
+remarks
+wrestle
+snowing
+slowing
+rations
+sabbath
+gallons
+temples
+weighed
+heavier
+marbles
+notices
+doubled
+eclipse
+sighted
+caliber
+patriot
+whacked
+armored
+parting
+johnnie
+dresser
+overall
+insight
+hookers
+mailbox
+clearer
+dispute
+fanfare
+toronto
+educate
+vessels
+errands
+shelley
+refined
+charmed
+fearful
+payback
+cologne
+sleeves
+conceal
+acquire
+ashtray
+trivial
+refugee
+doorman
+thanked
+breaths
+islamic
+alleged
+arrests
+admirer
+darkest
+commune
+martian
+stables
+tolling
+tyranny
+chemist
+ominous
+leisure
+lettuce
+tuition
+valiant
+replied
+jackets
+gallows
+flaming
+diploma
+trooper
+canteen
+indoors
+skinner
+indulge
+sanders
+descend
+spreads
+belgian
+fleeing
+pianist
+periods
+anarchy
+awarded
+surname
+outfits
+chateau
+charter
+hideout
+appoint
+chiming
+qualify
+cabaret
+zealand
+patrols
+placing
+sustain
+sweeter
+burgers
+reveals
+standby
+peacock
+pending
+severed
+mumbles
+reactor
+ribbons
+obscure
+terrain
+tenants
+scrooge
+dialing
+dinners
+vintage
+furnace
+abigail
+prairie
+capsule
+rightly
+sublime
+captive
+rituals
+handbag
+prevail
+amnesty
+takeoff
+twinkle
+scatter
+emerged
+reviews
+neglect
+carmine
+harness
+soviets
+antenna
+sharply
+anatomy
+burying
+swimmer
+neptune
+seizure
+starter
+founder
+expired
+beseech
+vaccine
+liaison
+nagging
+radiant
+sliding
+ghastly
+accepts
+shuffle
+emerald
+solving
+freshen
+turtles
+doubted
+fertile
+pinched
+arsenal
+malaria
+clarify
+firstly
+hugging
+settles
+transit
+herring
+baptist
+bladder
+pioneer
+virtues
+endured
+scanner
+rumbles
+cruiser
+orlando
+marched
+berries
+hooting
+winding
+strokes
+credits
+intrude
+invites
+catcher
+buckets
+rewrite
+origins
+defined
+rattles
+cooling
+vinegar
+drought
+overdue
+pierced
+earthly
+skinned
+blazing
+seminar
+gagging
+prefect
+thinner
+manages
+install
+ranking
+lullaby
+insured
+vitamin
+dynamic
+jerking
+appeals
+salvage
+pageant
+hustler
+permits
+persian
+roasted
+mustang
+horrors
+garland
+soaking
+newborn
+drained
+firemen
+pilgrim
+vermont
+vulture
+willard
+analyst
+runners
+lowered
+hotshot
+seniors
+trinity
+uranium
+stalker
+stirred
+batting
+forgave
+mildred
+slammed
+consume
+merrily
+forfeit
+orchard
+careers
+pursued
+isolate
+bullied
+capitol
+dilemma
+crusade
+aroused
+disobey
+cursing
+faggots
+intends
+bruised
+grammar
+bailiff
+collier
+roaming
+fencing
+incense
+envious
+lazarus
+imitate
+hygiene
+dictate
+hopeful
+worldly
+squares
+regions
+evident
+drafted
+earring
+outlaws
+renting
+cockpit
+cheetah
+peeping
+penance
+bending
+flashes
+suspend
+bounced
+sandals
+fleming
+katrina
+clarity
+adapted
+premier
+sparkle
+puzzles
+fearing
+coaster
+dickens
+boarded
+rascals
+genesis
+wichita
+revenue
+fascism
+sleeper
+smuggle
+olympus
+centers
+cackles
+applies
+meadows
+doubles
+applaud
+mortals
+chariot
+squawks
+passive
+invalid
+shelves
+shoving
+virtual
+crowned
+glamour
+goliath
+spelled
+refrain
+wyoming
+rapping
+serbian
+peppers
+squared
+emptied
+sneezes
+tablets
+carolyn
+harding
+pencils
+whitney
+tribune
+guineas
+drastic
+ongoing
+dashing
+junkies
+bravely
+jogging
+coffins
+puppets
+morally
+grilled
+slander
+reacted
+negroes
+mammals
+dialect
+bravest
+pillows
+itching
+hatchet
+stashed
+manhood
+plugged
+summons
+removal
+schemes
+vaguely
+prosper
+quieter
+imposed
+wildest
+thermal
+inhuman
+helmets
+platter
+donated
+rewards
+goodman
+coffees
+doggone
+kittens
+tangled
+outline
+repeats
+atheist
+gibbons
+tickles
+cushion
+flushed
+streams
+giraffe
+refresh
+addicts
+bermuda
+posture
+confide
+cheater
+spinach
+implore
+wilhelm
+tornado
+cardiac
+raphael
+fatigue
+mitzvah
+lending
+minding
+violate
+slumber
+gateway
+murmurs
+pompous
+lurking
+insides
+faraway
+snipers
+voyager
+riviera
+puberty
+tossing
+bologna
+manning
+turmoil
+hopping
+costing
+stating
+weights
+suffice
+chaotic
+stylish
+sulking
+jukebox
+drifted
+brownie
+tracker
+pancake
+rounded
+inquire
+clatter
+guiding
+craving
+cheerio
+apaches
+sterile
+bygones
+factors
+alimony
+ethical
+legions
+snowman
+booking
+waiters
+trashed
+stumble
+seafood
+strauss
+printer
+hawking
+sensual
+uncover
+hitched
+largely
+lengths
+builder
+scripts
+lunches
+koreans
+raiders
+finnish
+charade
+singles
+toaster
+screens
+enhance
+lizards
+marital
+unclear
+enforce
+widower
+museums
+peeking
+trolley
+bottled
+drinker
+waffles
+tedious
+okinawa
+pasture
+masseur
+hobbies
+camelot
+valleys
+cyclops
+rooftop
+graphic
+closure
+workout
+hauling
+iceberg
+deprive
+freeman
+viewing
+carving
+jamming
+seaside
+bellies
+greeted
+frantic
+shifted
+tasting
+quoting
+emerson
+ulysses
+threads
+blacked
+crucify
+exhaust
+endings
+grizzly
+ditched
+stamped
+fanatic
+sinatra
+authors
+recital
+leagues
+presses
+steamed
+compose
+orchids
+hateful
+hippies
+cyanide
+fancies
+riddles
+nickels
+typhoon
+dioxide
+outdoor
+hacking
+redneck
+heathen
+sneaked
+mockery
+baloney
+crumble
+earnest
+catfish
+deserts
+colleen
+perjury
+pickled
+awaited
+praises
+pesetas
+notions
+bedside
+scalpel
+glasgow
+methane
+hooking
+scoring
+thicker
+cornell
+booster
+implant
+unicorn
+carpets
+harmful
+poisons
+devised
+refusal
+hillary
+brushed
+phrases
+patches
+fetched
+siamese
+carcass
+torches
+gourmet
+kiddies
+niagara
+florist
+mailman
+plainly
+gangway
+urgency
+bergman
+cleanse
+bumping
+shatter
+coaches
+behaves
+seaweed
+sticker
+phoning
+sellers
+coppers
+goodies
+novelty
+glacier
+swiftly
+odyssey
+rebuilt
+docking
+anomaly
+eyeball
+commend
+hamster
+wronged
+baptism
+dumbest
+brushes
+buggers
+pledged
+outpost
+compact
+unheard
+galahad
+eyelids
+pretext
+upgrade
+cuisine
+mammoth
+burrows
+cholera
+catalog
+ironing
+memoirs
+stealth
+staging
+slipper
+syringe
+touring
+marking
+bedford
+estates
+entrust
+renewed
+prudent
+hurried
+aladdin
+upwards
+varsity
+stacked
+fancied
+festive
+maggots
+bulldog
+napkins
+tailing
+seagull
+paradox
+berserk
+funnier
+binding
+castles
+boulder
+bonfire
+clemens
+elusive
+pegasus
+psyched
+flushes
+cupcake
+upstate
+smacked
+hiccups
+stamina
+guitars
+cluster
+copying
+blindly
+adviser
+crimson
+frontal
+granite
+striped
+butters
+premium
+cramped
+branded
+bonding
+lasagna
+crazier
+jacuzzi
+reclaim
+proverb
+mistook
+seasick
+martyrs
+machete
+habitat
+freshly
+voltage
+goggles
+maniacs
+rebound
+measles
+whatnot
+bristol
+pinball
+wizards
+thyself
+traders
+dodgers
+casinos
+editors
+signore
+hayward
+booming
+forgery
+eminent
+playful
+glitter
+minimal
+bullies
+motions
+matador
+unified
+relying
+sarcasm
+forbids
+baskets
+recipes
+deleted
+tainted
+patrons
+spooked
+lodging
+futures
+oatmeal
+nirvana
+quixote
+maidens
+brewing
+pandora
+belches
+saddest
+sprayed
+stutter
+heretic
+pillars
+everest
+trainee
+meowing
+density
+algebra
+blurred
+thereby
+pottery
+rebirth
+wartime
+leeches
+consort
+coupons
+texture
+surplus
+parsons
+knuckle
+bayonet
+slashed
+stature
+hoodlum
+cocking
+soprano
+robbins
+bearded
+coastal
+presley
+ghostly
+rejects
+garment
+nonstop
+galileo
+weakest
+implies
+wrinkle
+bloated
+arabian
+clipped
+dwarves
+unleash
+caramel
+hangman
+induced
+milking
+evasive
+bellows
+outlook
+morales
+puzzled
+summary
+barging
+nearing
+coyotes
+undergo
+sorting
+forsake
+hatched
+blondes
+vikings
+dreaded
+briefed
+jehovah
+wrongly
+hancock
+demonic
+cleaver
+marches
+avenged
+dubious
+milkman
+janeiro
+stifler
+mandate
+breakup
+gambled
+fossils
+plucked
+windsor
+kibbutz
+hosting
+meanest
+chipped
+sharpen
+fridays
+goblins
+thinker
+tsunami
+disrupt
+derrick
+plunder
+abdomen
+bashful
+lovable
+plunged
+abusive
+pleaded
+memento
+heroism
+devious
+turkeys
+persist
+evicted
+residue
+midwife
+tropics
+yawning
+overrun
+midwest
+startle
+strayed
+scolded
+seating
+monarch
+slender
+faraday
+cobbler
+whitman
+orgasms
+tissues
+buzzard
+candies
+nightly
+statute
+violets
+scraped
+sweetly
+peabody
+concede
+linking
+fullest
+tourism
+silicon
+uncanny
+outcast
+stormed
+cheques
+mongrel
+sensing
+sedated
+cossack
+hormone
+posting
+billing
+tracing
+rooting
+algiers
+setback
+volumes
+raccoon
+beastly
+ugliest
+keepers
+bribery
+inquest
+fallout
+fussing
+wallets
+reptile
+midgets
+galilee
+lowlife
+augusta
+caveman
+weaving
+tibetan
+borough
+mutants
+plateau
+custard
+looting
+crowbar
+archers
+insulin
+addison
+grazing
+updated
+tolstoy
+scourge
+smitten
+squeaky
+abiding
+swaying
+allergy
+heinous
+dummies
+dodging
+fitness
+piccolo
+scarred
+norfolk
+sweeney
+collide
+patched
+pendant
+banners
+nephews
+fiddler
+foresee
+latrine
+closets
+bangles
+pebbles
+blender
+neutron
+nucleus
+artwork
+flutter
+donkeys
+suction
+bullock
+chilled
+steamer
+genghis
+rosebud
+malachi
+abusing
+raisins
+arsenic
+dipping
+leaping
+symptom
+violins
+packets
+crybaby
+jingles
+spartan
+archive
+alerted
+utility
+outward
+floated
+daisies
+newport
+listing
+thrills
+recount
+commits
+freezes
+stalled
+mileage
+default
+planner
+babysit
+baptize
+brewery
+mouthed
+tidings
+warhead
+bunnies
+scrawny
+heiress
+calcium
+holster
+charger
+charmer
+shorten
+sprouts
+tiniest
+larceny
+bouncer
+swindle
+onwards
+quartet
+turnout
+railing
+plotted
+chilean
+ostrich
+scrolls
+smeared
+burgess
+markers
+trotter
+soaring
+sandman
+gadgets
+tickled
+palaces
+hammers
+pimples
+muffins
+striker
+swapped
+learner
+orpheus
+nemesis
+defiant
+sorcery
+yapping
+checkup
+regroup
+optical
+labeled
+roaches
+inflict
+enabled
+cleanup
+firearm
+folding
+endowed
+escorts
+suzette
+shaping
+sardine
+parched
+deficit
+resides
+revived
+bazooka
+preview
+dormant
+winslow
+sipping
+eyebrow
+dossier
+godless
+monsoon
+discard
+wildcat
+kindest
+greased
+mondays
+waldorf
+sectors
+rethink
+sermons
+rapture
+excites
+grinder
+clement
+curator
+appetit
+cuckold
+bailing
+decreed
+replies
+flunked
+plagued
+watered
+carnage
+rigging
+emerges
+daggers
+segment
+limping
+guesses
+shivers
+exclude
+collars
+ballast
+tending
+forbade
+trapeze
+defects
+confine
+revered
+swamped
+seville
+extends
+sharper
+twelfth
+cheered
+diverse
+caterer
+literal
+electra
+vibrant
+strains
+flashed
+tallest
+defying
+circled
+richter
+topside
+scented
+absolve
+cutters
+whoring
+secular
+betrays
+smashes
+wanders
+saffron
+elastic
+gunning
+bigfoot
+yelping
+faintly
+peeling
+zapping
+saddled
+apostle
+frisbee
+banning
+shrinks
+harshly
+erratic
+anything
+remember
+happened
+yourself
+together
+tomorrow
+business
+everyone
+somebody
+actually
+children
+thinking
+probably
+whatever
+daughter
+supposed
+question
+possible
+straight
+laughing
+hospital
+american
+speaking
+finished
+terrible
+birthday
+pleasure
+building
+watching
+chuckles
+anywhere
+surprise
+problems
+thousand
+accident
+security
+marriage
+fighting
+standing
+personal
+position
+promised
+continue
+sergeant
+sleeping
+bullshit
+evidence
+suddenly
+starting
+pictures
+brothers
+upstairs
+involved
+drinking
+shouting
+shooting
+decision
+princess
+language
+interest
+director
+innocent
+military
+computer
+soldiers
+complete
+feelings
+bathroom
+nonsense
+pregnant
+strength
+greatest
+consider
+favorite
+japanese
+mountain
+national
+practice
+pressure
+horrible
+minister
+although
+cheering
+monsieur
+possibly
+handsome
+prepared
+saturday
+arrested
+powerful
+medicine
+breaking
+murdered
+bringing
+believed
+laughter
+training
+whenever
+students
+criminal
+received
+distance
+followed
+returned
+universe
+contract
+expected
+thoughts
+murderer
+grunting
+property
+planning
+goodness
+audience
+research
+realized
+bastards
+painting
+football
+wherever
+officers
+vacation
+governor
+original
+carrying
+honestly
+exciting
+romantic
+midnight
+millions
+charming
+stranger
+recently
+familiar
+memories
+prisoner
+violence
+sometime
+precious
+changing
+confused
+grateful
+official
+applause
+families
+knocking
+ordinary
+creature
+treasure
+friendly
+highness
+positive
+darkness
+groaning
+terrific
+shopping
+attorney
+presence
+terribly
+bleeding
+attacked
+stronger
+becoming
+stealing
+hundreds
+magazine
+checking
+dreaming
+reporter
+physical
+politics
+throwing
+gorgeous
+elevator
+sentence
+attitude
+shoulder
+material
+cleaning
+exchange
+separate
+progress
+properly
+swimming
+champion
+directly
+movement
+thursday
+counting
+solution
+mistakes
+pleasant
+district
+patients
+schedule
+negative
+studying
+pathetic
+accepted
+entirely
+religion
+released
+location
+paradise
+learning
+stopping
+narrator
+convince
+approach
+spending
+teaching
+arranged
+farewell
+opposite
+disaster
+mistress
+struggle
+presents
+entrance
+customer
+campaign
+patience
+ceremony
+starving
+citizens
+baseball
+incident
+suffered
+services
+generous
+neighbor
+delivery
+lifetime
+transfer
+behavior
+instance
+passport
+producer
+exercise
+believes
+touching
+betrayed
+basement
+downtown
+partners
+diamonds
+growling
+division
+sandwich
+margaret
+reported
+coughing
+adjusted
+everyday
+marrying
+chairman
+messages
+normally
+mistaken
+reverend
+enjoying
+cheating
+machines
+identity
+describe
+worrying
+wondered
+complain
+answered
+invented
+visiting
+designed
+occasion
+survived
+electric
+comrades
+jonathan
+splendid
+standard
+valuable
+russians
+homework
+informed
+industry
+offering
+punished
+fabulous
+discover
+headache
+dramatic
+reaction
+suitcase
+stubborn
+divorced
+engineer
+whispers
+deserves
+doorbell
+appeared
+absolute
+captured
+peaceful
+listened
+superior
+contrary
+imagined
+argument
+operator
+williams
+identify
+quarters
+election
+specific
+catholic
+troubles
+dressing
+elephant
+freezing
+teachers
+gambling
+enormous
+humanity
+judgment
+potatoes
+directed
+southern
+response
+festival
+sunshine
+visitors
+promises
+nowadays
+corporal
+slightly
+occurred
+meantime
+faithful
+crossing
+dropping
+maintain
+stations
+cemetery
+servants
+supplies
+increase
+hopeless
+rumbling
+delicate
+almighty
+compared
+virginia
+activity
+entering
+bothered
+accounts
+talented
+gathered
+blessing
+giggling
+december
+catching
+kindness
+hercules
+gunshots
+monsters
+previous
+superman
+republic
+emotions
+homicide
+november
+survival
+boarding
+airplane
+necklace
+lawrence
+belonged
+countess
+european
+produced
+glorious
+searched
+required
+chanting
+borrowed
+weakness
+attached
+deserved
+daylight
+recorded
+sticking
+suspects
+meetings
+announce
+grandson
+register
+congress
+intended
+chickens
+anderson
+carriage
+arriving
+proposal
+dreadful
+railroad
+treating
+internal
+northern
+disgrace
+eternity
+critical
+telegram
+constant
+marshall
+chemical
+brooklyn
+holidays
+floating
+defeated
+provided
+obsessed
+poisoned
+whistles
+somewhat
+advanced
+tracking
+aircraft
+annoying
+creating
+scotland
+victoria
+executed
+freaking
+strategy
+creative
+audition
+offended
+relative
+strictly
+dialogue
+appetite
+ministry
+function
+ultimate
+nicholas
+concerns
+covering
+dynamite
+homeless
+creation
+graduate
+fighters
+insisted
+employee
+document
+whirring
+sequence
+helpless
+facility
+screwing
+envelope
+miracles
+elements
+warriors
+requires
+climbing
+napoleon
+conflict
+expenses
+clicking
+trousers
+entitled
+assigned
+hostages
+shepherd
+vanished
+clothing
+sympathy
+overcome
+guardian
+sensible
+tortured
+educated
+remained
+forgiven
+illusion
+benjamin
+portrait
+regiment
+infected
+husbands
+declared
+relieved
+respects
+civilian
+admitted
+invasion
+threaten
+sickness
+crashing
+platform
+economic
+ignorant
+veronica
+paranoid
+designer
+instinct
+analysis
+umbrella
+occupied
+vampires
+priority
+addition
+stinking
+disguise
+revealed
+inspired
+reaching
+harmless
+salesman
+pounding
+assassin
+withdraw
+frighten
+preacher
+mitchell
+replaced
+gangster
+collapse
+imperial
+spinning
+ambition
+controls
+franklin
+gracious
+alliance
+marching
+atlantic
+broadway
+february
+crawling
+concrete
+florence
+musician
+heavenly
+contacts
+thompson
+einstein
+waitress
+chirping
+subjects
+bachelor
+persuade
+roommate
+explains
+deserted
+jealousy
+uniforms
+adorable
+tolerate
+whimpers
+drowning
+arrogant
+secretly
+traveled
+cracking
+corridor
+reminded
+majority
+lipstick
+handling
+products
+rattling
+affected
+cocktail
+shanghai
+included
+troubled
+feathers
+forehead
+intimate
+lighting
+deceased
+magician
+opponent
+clearing
+tourists
+password
+rejected
+creaking
+hesitate
+curtains
+thrilled
+abortion
+immortal
+capacity
+multiple
+pointing
+approval
+courtesy
+peculiar
+robinson
+quitting
+restless
+fourteen
+reliable
+eighteen
+suitable
+clarence
+preserve
+thirteen
+homeland
+guessing
+campbell
+symptoms
+cheerful
+accurate
+barracks
+recovery
+fletcher
+childish
+assembly
+fountain
+youngest
+scissors
+wretched
+assuming
+measures
+missiles
+proposed
+approved
+landlord
+isolated
+cultural
+shocking
+domestic
+peasants
+switched
+happiest
+inviting
+benefits
+stepping
+strongly
+costumes
+achieved
+insulted
+squadron
+mortgage
+sniffing
+sneaking
+merciful
+egyptian
+theories
+projects
+disagree
+billions
+chambers
+nickname
+reserved
+wardrobe
+mechanic
+unlikely
+artistic
+vehicles
+expelled
+dragging
+dispatch
+evacuate
+retarded
+indicate
+bracelet
+sunlight
+examined
+carnival
+fortress
+sweating
+prettier
+canadian
+pentagon
+mentally
+relation
+interior
+weekends
+backyard
+disorder
+disposal
+merchant
+articles
+infinite
+definite
+distress
+promoted
+traitors
+allergic
+calendar
+cardinal
+guidance
+barbecue
+villages
+selected
+insanity
+memorial
+province
+injuries
+discount
+terminal
+purchase
+tomatoes
+hamilton
+overtime
+ordering
+deceived
+swinging
+souvenir
+starring
+branches
+behaving
+blocking
+shipment
+gasoline
+destined
+canceled
+drawings
+mourning
+generals
+patricia
+sexually
+opinions
+deadline
+notebook
+speeches
+requests
+escaping
+contempt
+pressing
+careless
+hometown
+software
+carolina
+mattress
+slipping
+improved
+gardener
+magnetic
+charging
+italians
+lonesome
+pancakes
+thankful
+nuisance
+whooping
+accusing
+altitude
+purposes
+ruthless
+striking
+exploded
+features
+choosing
+diseases
+reckless
+earrings
+distract
+discreet
+squirrel
+slippers
+scenario
+profound
+solitary
+colorado
+protocol
+screamed
+programs
+rehearse
+equipped
+chatting
+allowing
+channels
+avoiding
+strangle
+blackout
+trusting
+lemonade
+betrayal
+mustache
+overseas
+teenager
+postcard
+workshop
+shipping
+stressed
+compound
+restored
+comments
+cupboard
+precinct
+sounding
+sweetest
+breeding
+patterns
+infantry
+recorder
+combined
+survivor
+leonardo
+coverage
+includes
+launched
+flirting
+devotion
+summoned
+peterson
+hardware
+incoming
+stunning
+muhammad
+mumbling
+phillips
+grounded
+semester
+blankets
+premises
+bankrupt
+treasury
+skeleton
+unstable
+acquired
+prophecy
+currency
+overhead
+reindeer
+morphine
+sniffles
+invested
+garrison
+rational
+petition
+commands
+crippled
+attended
+suggests
+mcdonald
+morrison
+sabotage
+employed
+speakers
+morality
+attempts
+cheyenne
+cadillac
+fugitive
+payments
+refugees
+michigan
+resolved
+exposure
+feminine
+repeated
+cautious
+addicted
+smelling
+fearless
+breathes
+relevant
+slippery
+parallel
+weddings
+rosemary
+churches
+smashing
+awaiting
+smallest
+observed
+spitting
+practise
+historic
+nineteen
+frontier
+commence
+activate
+biscuits
+juvenile
+confront
+triangle
+eminence
+syndrome
+tropical
+baroness
+columbus
+figuring
+solitude
+numerous
+greeting
+thorough
+evenings
+drifting
+estimate
+holiness
+dripping
+bluffing
+snarling
+clapping
+resigned
+apparent
+sidewalk
+resident
+likewise
+godzilla
+chandler
+oklahoma
+mainland
+panicked
+columbia
+monument
+guarding
+hydrogen
+detailed
+wandered
+protects
+mosquito
+claiming
+refusing
+freshman
+surround
+shortcut
+managing
+olympics
+referred
+shameful
+employer
+whinnies
+dolphins
+comedian
+stitches
+dinosaur
+stripper
+rewarded
+extended
+antidote
+cowardly
+operated
+disabled
+intercom
+academic
+demanded
+missions
+violated
+abducted
+epidemic
+stripped
+ignition
+mornings
+istanbul
+steering
+backpack
+crackers
+briefing
+repaired
+balloons
+specimen
+speeding
+gigantic
+kentucky
+printing
+imbecile
+assemble
+involves
+measured
+rustling
+shutting
+secondly
+retrieve
+sinister
+smartest
+anyplace
+provides
+ignoring
+contents
+postpone
+pussycat
+punching
+werewolf
+moreover
+stalking
+grabbing
+sherlock
+settling
+receiver
+benedict
+pharmacy
+dedicate
+sausages
+richards
+sessions
+twilight
+grenades
+defended
+babbling
+brighter
+publicly
+cannabis
+revolver
+severely
+bulletin
+literary
+backward
+reminder
+montreal
+galaxies
+adoption
+vitamins
+confined
+richmond
+gertrude
+stanford
+bouncing
+airborne
+toughest
+composed
+category
+obtained
+eyebrows
+prospect
+cultures
+sweeping
+airlines
+departed
+hangover
+initials
+traveler
+composer
+relaxing
+theodore
+missouri
+consumed
+salvador
+commerce
+plumbing
+clanging
+stirring
+scottish
+policies
+donation
+finishes
+disciple
+mischief
+brussels
+feedback
+intruder
+scorpion
+amateurs
+cleaners
+doorstep
+urgently
+garfield
+athletic
+detected
+sardines
+tactical
+metaphor
+premiere
+inclined
+spelling
+obstacle
+equation
+heritage
+packages
+snatched
+medieval
+brunette
+unlocked
+informer
+mattered
+regional
+bacteria
+conclude
+matching
+bursting
+smoothly
+bandages
+database
+noticing
+sneakers
+banished
+hormones
+agencies
+portland
+existing
+sporting
+cromwell
+destroys
+deciding
+stumbled
+humility
+stalling
+tempting
+notified
+goldfish
+inferior
+swearing
+landlady
+symphony
+watchman
+villains
+comeback
+starters
+stranded
+something
+beautiful
+everybody
+important
+different
+sometimes
+gentlemen
+wonderful
+somewhere
+president
+yesterday
+certainly
+christmas
+questions
+continues
+attention
+happening
+afternoon
+dangerous
+situation
+professor
+difficult
+screaming
+beginning
+apartment
+seriously
+listening
+boyfriend
+ourselves
+otherwise
+breakfast
+necessary
+detective
+excellent
+surprised
+following
+obviously
+forgotten
+gentleman
+wondering
+commander
+according
+emergency
+inspector
+happiness
+apologize
+operation
+recognize
+perfectly
+character
+telephone
+introduce
+fantastic
+destroyed
+condition
+expecting
+thousands
+concerned
+brilliant
+cigarette
+secretary
+expensive
+breathing
+political
+mountains
+carefully
+americans
+therefore
+christian
+champagne
+suffering
+knowledge
+insurance
+assistant
+interview
+celebrate
+chocolate
+community
+mentioned
+committed
+direction
+statement
+naturally
+disappear
+including
+chuckling
+newspaper
+delicious
+prisoners
+sacrifice
+subtitles
+advantage
+extremely
+available
+hollywood
+basically
+desperate
+education
+challenge
+equipment
+authority
+committee
+ambulance
+convinced
+elizabeth
+miserable
+customers
+treatment
+guarantee
+nightmare
+surrender
diff --git a/OpenKeychain/src/main/java/android/support/v4/widget/FlingNestedScrollView.java b/OpenKeychain/src/main/java/android/support/v4/widget/FlingNestedScrollView.java
new file mode 100644
index 000000000..2bfda5fa8
--- /dev/null
+++ b/OpenKeychain/src/main/java/android/support/v4/widget/FlingNestedScrollView.java
@@ -0,0 +1,1355 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.widget;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.v4.view.AccessibilityDelegateCompat;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.NestedScrollingChild;
+import android.support.v4.view.NestedScrollingChildHelper;
+import android.support.v4.view.NestedScrollingParent;
+import android.support.v4.view.NestedScrollingParentHelper;
+import android.support.v4.view.VelocityTrackerCompat;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.accessibility.AccessibilityEventCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v4.view.accessibility.AccessibilityRecordCompat;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.FocusFinder;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.FrameLayout;
+import android.widget.ScrollView;
+
+import java.util.ArrayList;
+
+/**
+ * Workaround for bug in support lib. From https://code.google.com/p/android/issues/detail?id=177729
+ *
+ * Also see workaround with padding in view_key_fragment.xml
+ */
+public class FlingNestedScrollView extends FrameLayout implements NestedScrollingParent, NestedScrollingChild {
+ static final int ANIMATED_SCROLL_GAP = 250;
+ static final float MAX_SCROLL_FACTOR = 0.5F;
+ private static final String TAG = "FlingNestedScrollView";
+ private long mLastScroll;
+ private final Rect mTempRect;
+ private ScrollerCompat mScroller;
+ private EdgeEffectCompat mEdgeGlowTop;
+ private EdgeEffectCompat mEdgeGlowBottom;
+ private int mLastMotionY;
+ private boolean mIsLayoutDirty;
+ private boolean mIsLaidOut;
+ private View mChildToScrollTo;
+ private boolean mIsBeingDragged;
+ private VelocityTracker mVelocityTracker;
+ private boolean mFillViewport;
+ private boolean mSmoothScrollingEnabled;
+ private int mTouchSlop;
+ private int mMinimumVelocity;
+ private int mMaximumVelocity;
+ private int mActivePointerId;
+ private final int[] mScrollOffset;
+ private final int[] mScrollConsumed;
+ private int mNestedYOffset;
+ private static final int INVALID_POINTER = -1;
+ private FlingNestedScrollView.SavedState mSavedState;
+ private static final FlingNestedScrollView.AccessibilityDelegate ACCESSIBILITY_DELEGATE = new FlingNestedScrollView.AccessibilityDelegate();
+ private static final int[] SCROLLVIEW_STYLEABLE = new int[]{16843130};
+ private final NestedScrollingParentHelper mParentHelper;
+ private final NestedScrollingChildHelper mChildHelper;
+ private float mVerticalScrollFactor;
+
+ public FlingNestedScrollView(Context context) {
+ this(context, (AttributeSet)null);
+ }
+
+ public FlingNestedScrollView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public FlingNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ this.mTempRect = new Rect();
+ this.mIsLayoutDirty = true;
+ this.mIsLaidOut = false;
+ this.mChildToScrollTo = null;
+ this.mIsBeingDragged = false;
+ this.mSmoothScrollingEnabled = true;
+ this.mActivePointerId = -1;
+ this.mScrollOffset = new int[2];
+ this.mScrollConsumed = new int[2];
+ this.initScrollView();
+ TypedArray a = context.obtainStyledAttributes(attrs, SCROLLVIEW_STYLEABLE, defStyleAttr, 0);
+ this.setFillViewport(a.getBoolean(0, false));
+ a.recycle();
+ this.mParentHelper = new NestedScrollingParentHelper(this);
+ this.mChildHelper = new NestedScrollingChildHelper(this);
+ this.setNestedScrollingEnabled(true);
+ ViewCompat.setAccessibilityDelegate(this, ACCESSIBILITY_DELEGATE);
+ }
+
+ public void setNestedScrollingEnabled(boolean enabled) {
+ this.mChildHelper.setNestedScrollingEnabled(enabled);
+ }
+
+ public boolean isNestedScrollingEnabled() {
+ return this.mChildHelper.isNestedScrollingEnabled();
+ }
+
+ public boolean startNestedScroll(int axes) {
+ return this.mChildHelper.startNestedScroll(axes);
+ }
+
+ public void stopNestedScroll() {
+ this.mChildHelper.stopNestedScroll();
+ }
+
+ public boolean hasNestedScrollingParent() {
+ return this.mChildHelper.hasNestedScrollingParent();
+ }
+
+ public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
+ return this.mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
+ }
+
+ public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
+ return this.mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
+ }
+
+ public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
+ return this.mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
+ }
+
+ public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
+ return this.mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
+ }
+
+ public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
+ return (nestedScrollAxes & 2) != 0;
+ }
+
+ public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
+ this.mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
+ this.startNestedScroll(2);
+ }
+
+ public void onStopNestedScroll(View target) {
+ this.stopNestedScroll();
+ }
+
+ public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
+ int oldScrollY = this.getScrollY();
+ this.scrollBy(0, dyUnconsumed);
+ int myConsumed = this.getScrollY() - oldScrollY;
+ int myUnconsumed = dyUnconsumed - myConsumed;
+ this.dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, (int[])null);
+ }
+
+ public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
+ }
+
+ public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
+ if(!consumed) {
+ this.flingWithNestedDispatch((int)velocityY);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+ return false;
+ }
+
+ public int getNestedScrollAxes() {
+ return this.mParentHelper.getNestedScrollAxes();
+ }
+
+ public boolean shouldDelayChildPressedState() {
+ return true;
+ }
+
+ protected float getTopFadingEdgeStrength() {
+ if(this.getChildCount() == 0) {
+ return 0.0F;
+ } else {
+ int length = this.getVerticalFadingEdgeLength();
+ int scrollY = this.getScrollY();
+ return scrollY < length?(float)scrollY / (float)length:1.0F;
+ }
+ }
+
+ protected float getBottomFadingEdgeStrength() {
+ if(this.getChildCount() == 0) {
+ return 0.0F;
+ } else {
+ int length = this.getVerticalFadingEdgeLength();
+ int bottomEdge = this.getHeight() - this.getPaddingBottom();
+ int span = this.getChildAt(0).getBottom() - this.getScrollY() - bottomEdge;
+ return span < length?(float)span / (float)length:1.0F;
+ }
+ }
+
+ public int getMaxScrollAmount() {
+ return (int)(0.5F * (float)this.getHeight());
+ }
+
+ private void initScrollView() {
+ this.mScroller = new ScrollerCompat(this.getContext(), (Interpolator)null);
+ this.setFocusable(true);
+ //noinspection ResourceType
+ this.setDescendantFocusability(262144);
+ this.setWillNotDraw(false);
+ ViewConfiguration configuration = ViewConfiguration.get(this.getContext());
+ this.mTouchSlop = configuration.getScaledTouchSlop();
+ this.mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
+ this.mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ }
+
+ public void addView(View child) {
+ if(this.getChildCount() > 0) {
+ throw new IllegalStateException("ScrollView can host only one direct child");
+ } else {
+ super.addView(child);
+ }
+ }
+
+ public void addView(View child, int index) {
+ if(this.getChildCount() > 0) {
+ throw new IllegalStateException("ScrollView can host only one direct child");
+ } else {
+ super.addView(child, index);
+ }
+ }
+
+ public void addView(View child, LayoutParams params) {
+ if(this.getChildCount() > 0) {
+ throw new IllegalStateException("ScrollView can host only one direct child");
+ } else {
+ super.addView(child, params);
+ }
+ }
+
+ public void addView(View child, int index, LayoutParams params) {
+ if(this.getChildCount() > 0) {
+ throw new IllegalStateException("ScrollView can host only one direct child");
+ } else {
+ super.addView(child, index, params);
+ }
+ }
+
+ private boolean canScroll() {
+ View child = this.getChildAt(0);
+ if(child != null) {
+ int childHeight = child.getHeight();
+ return this.getHeight() < childHeight + this.getPaddingTop() + this.getPaddingBottom();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isFillViewport() {
+ return this.mFillViewport;
+ }
+
+ public void setFillViewport(boolean fillViewport) {
+ if(fillViewport != this.mFillViewport) {
+ this.mFillViewport = fillViewport;
+ this.requestLayout();
+ }
+
+ }
+
+ public boolean isSmoothScrollingEnabled() {
+ return this.mSmoothScrollingEnabled;
+ }
+
+ public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled) {
+ this.mSmoothScrollingEnabled = smoothScrollingEnabled;
+ }
+
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if(this.mFillViewport) {
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ if(heightMode != 0) {
+ if(this.getChildCount() > 0) {
+ View child = this.getChildAt(0);
+ int height = this.getMeasuredHeight();
+ if(child.getMeasuredHeight() < height) {
+ android.widget.FrameLayout.LayoutParams lp = (android.widget.FrameLayout.LayoutParams)child.getLayoutParams();
+ int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, this.getPaddingLeft() + this.getPaddingRight(), lp.width);
+ height -= this.getPaddingTop();
+ height -= this.getPaddingBottom();
+ //noinspection ResourceType
+ int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, 1073741824);
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ }
+ }
+
+ }
+ }
+ }
+
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ return super.dispatchKeyEvent(event) || this.executeKeyEvent(event);
+ }
+
+ public boolean executeKeyEvent(KeyEvent event) {
+ this.mTempRect.setEmpty();
+ if(this.canScroll()) {
+ boolean handled1 = false;
+ if(event.getAction() == 0) {
+ switch(event.getKeyCode()) {
+ case 19:
+ if(!event.isAltPressed()) {
+ handled1 = this.arrowScroll(33);
+ } else {
+ handled1 = this.fullScroll(33);
+ }
+ break;
+ case 20:
+ if(!event.isAltPressed()) {
+ handled1 = this.arrowScroll(130);
+ } else {
+ handled1 = this.fullScroll(130);
+ }
+ break;
+ case 62:
+ this.pageScroll(event.isShiftPressed()?33:130);
+ }
+ }
+
+ return handled1;
+ } else if(this.isFocused() && event.getKeyCode() != 4) {
+ View handled = this.findFocus();
+ if(handled == this) {
+ handled = null;
+ }
+
+ View nextFocused = FocusFinder.getInstance().findNextFocus(this, handled, 130);
+ return nextFocused != null && nextFocused != this && nextFocused.requestFocus(130);
+ } else {
+ return false;
+ }
+ }
+
+ private boolean inChild(int x, int y) {
+ if(this.getChildCount() <= 0) {
+ return false;
+ } else {
+ int scrollY = this.getScrollY();
+ View child = this.getChildAt(0);
+ return y >= child.getTop() - scrollY && y < child.getBottom() - scrollY && x >= child.getLeft() && x < child.getRight();
+ }
+ }
+
+ private void initOrResetVelocityTracker() {
+ if(this.mVelocityTracker == null) {
+ this.mVelocityTracker = VelocityTracker.obtain();
+ } else {
+ this.mVelocityTracker.clear();
+ }
+
+ }
+
+ private void initVelocityTrackerIfNotExists() {
+ if(this.mVelocityTracker == null) {
+ this.mVelocityTracker = VelocityTracker.obtain();
+ }
+
+ }
+
+ private void recycleVelocityTracker() {
+ if(this.mVelocityTracker != null) {
+ this.mVelocityTracker.recycle();
+ this.mVelocityTracker = null;
+ }
+
+ }
+
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ if(disallowIntercept) {
+ this.recycleVelocityTracker();
+ }
+
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ }
+
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ int action = ev.getAction();
+ if(action == 2 && this.mIsBeingDragged) {
+ return true;
+ } else if(this.getScrollY() == 0 && !ViewCompat.canScrollVertically(this, 1)) {
+ return false;
+ } else {
+ int y;
+ switch(action & 255) {
+ case 0:
+ y = (int)ev.getY();
+ if(!this.inChild((int)ev.getX(), y)) {
+ this.mIsBeingDragged = false;
+ this.recycleVelocityTracker();
+ } else {
+ this.mLastMotionY = y;
+ this.mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
+ this.initOrResetVelocityTracker();
+ this.mVelocityTracker.addMovement(ev);
+ this.mIsBeingDragged = !this.mScroller.isFinished();
+ this.startNestedScroll(2);
+ }
+ break;
+ case 1:
+ case 3:
+ this.mIsBeingDragged = false;
+ this.mActivePointerId = -1;
+ this.recycleVelocityTracker();
+ this.stopNestedScroll();
+ break;
+ case 2:
+ y = this.mActivePointerId;
+ if(y != -1) {
+ int pointerIndex = MotionEventCompat.findPointerIndex(ev, y);
+ if(pointerIndex == -1) {
+ Log.e("FlingNestedScrollView", "Invalid pointerId=" + y + " in onInterceptTouchEvent");
+ } else {
+ int y1 = (int)MotionEventCompat.getY(ev, pointerIndex);
+ int yDiff = Math.abs(y1 - this.mLastMotionY);
+ if(yDiff > this.mTouchSlop && (this.getNestedScrollAxes() & 2) == 0) {
+ this.mIsBeingDragged = true;
+ this.mLastMotionY = y1;
+ this.initVelocityTrackerIfNotExists();
+ this.mVelocityTracker.addMovement(ev);
+ this.mNestedYOffset = 0;
+ ViewParent parent = this.getParent();
+ if(parent != null) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
+ }
+ }
+ }
+ case 4:
+ case 5:
+ default:
+ break;
+ case 6:
+ this.onSecondaryPointerUp(ev);
+ }
+
+ return this.mIsBeingDragged;
+ }
+ }
+
+ public boolean onTouchEvent(MotionEvent ev) {
+ this.initVelocityTrackerIfNotExists();
+ MotionEvent vtev = MotionEvent.obtain(ev);
+ int actionMasked = MotionEventCompat.getActionMasked(ev);
+ if(actionMasked == 0) {
+ this.mNestedYOffset = 0;
+ }
+
+ vtev.offsetLocation(0.0F, (float)this.mNestedYOffset);
+ int index;
+ int initialVelocity;
+ switch(actionMasked) {
+ case 0:
+ if(this.getChildCount() == 0) {
+ return false;
+ }
+
+ if(this.mIsBeingDragged = !this.mScroller.isFinished()) {
+ ViewParent activePointerIndex1 = this.getParent();
+ if(activePointerIndex1 != null) {
+ activePointerIndex1.requestDisallowInterceptTouchEvent(true);
+ }
+ }
+
+ if(!this.mScroller.isFinished()) {
+ this.mScroller.abortAnimation();
+ }
+
+ this.mLastMotionY = (int)ev.getY();
+ this.mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
+ this.startNestedScroll(2);
+ break;
+ case 1:
+ if(this.mIsBeingDragged) {
+ VelocityTracker index2 = this.mVelocityTracker;
+ index2.computeCurrentVelocity(1000, (float)this.mMaximumVelocity);
+ initialVelocity = (int) VelocityTrackerCompat.getYVelocity(index2, this.mActivePointerId);
+ if(Math.abs(initialVelocity) > this.mMinimumVelocity) {
+ this.flingWithNestedDispatch(-initialVelocity);
+ }
+
+ this.mActivePointerId = -1;
+ this.endDrag();
+ }
+ break;
+ case 2:
+ int activePointerIndex = MotionEventCompat.findPointerIndex(ev, this.mActivePointerId);
+ if(activePointerIndex == -1) {
+ Log.e("FlingNestedScrollView", "Invalid pointerId=" + this.mActivePointerId + " in onTouchEvent");
+ } else {
+ int y = (int)MotionEventCompat.getY(ev, activePointerIndex);
+ int deltaY = this.mLastMotionY - y;
+ if(this.dispatchNestedPreScroll(0, deltaY, this.mScrollConsumed, this.mScrollOffset)) {
+ deltaY -= this.mScrollConsumed[1];
+ vtev.offsetLocation(0.0F, (float)this.mScrollOffset[1]);
+ this.mNestedYOffset += this.mScrollOffset[1];
+ }
+
+ if(!this.mIsBeingDragged && Math.abs(deltaY) > this.mTouchSlop) {
+ ViewParent index1 = this.getParent();
+ if(index1 != null) {
+ index1.requestDisallowInterceptTouchEvent(true);
+ }
+
+ this.mIsBeingDragged = true;
+ if(deltaY > 0) {
+ deltaY -= this.mTouchSlop;
+ } else {
+ deltaY += this.mTouchSlop;
+ }
+ }
+
+ if(this.mIsBeingDragged) {
+ this.mLastMotionY = y - this.mScrollOffset[1];
+ index = this.getScrollY();
+ initialVelocity = this.getScrollRange();
+ int overscrollMode = ViewCompat.getOverScrollMode(this);
+ boolean canOverscroll = overscrollMode == 0 || overscrollMode == 1 && initialVelocity > 0;
+ if(this.overScrollByCompat(0, deltaY, 0, this.getScrollY(), 0, initialVelocity, 0, 0, true) && !this.hasNestedScrollingParent()) {
+ this.mVelocityTracker.clear();
+ }
+
+ int scrolledDeltaY = this.getScrollY() - index;
+ int unconsumedY = deltaY - scrolledDeltaY;
+ if(this.dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, this.mScrollOffset)) {
+ this.mLastMotionY -= this.mScrollOffset[1];
+ vtev.offsetLocation(0.0F, (float)this.mScrollOffset[1]);
+ this.mNestedYOffset += this.mScrollOffset[1];
+ } else if(canOverscroll) {
+ this.ensureGlows();
+ int pulledToY = index + deltaY;
+ if(pulledToY < 0) {
+ this.mEdgeGlowTop.onPull((float)deltaY / (float)this.getHeight(), MotionEventCompat.getX(ev, activePointerIndex) / (float)this.getWidth());
+ if(!this.mEdgeGlowBottom.isFinished()) {
+ this.mEdgeGlowBottom.onRelease();
+ }
+ } else if(pulledToY > initialVelocity) {
+ this.mEdgeGlowBottom.onPull((float)deltaY / (float)this.getHeight(), 1.0F - MotionEventCompat.getX(ev, activePointerIndex) / (float)this.getWidth());
+ if(!this.mEdgeGlowTop.isFinished()) {
+ this.mEdgeGlowTop.onRelease();
+ }
+ }
+
+ if(this.mEdgeGlowTop != null && (!this.mEdgeGlowTop.isFinished() || !this.mEdgeGlowBottom.isFinished())) {
+ ViewCompat.postInvalidateOnAnimation(this);
+ }
+ }
+ }
+ }
+ break;
+ case 3:
+ if(this.mIsBeingDragged && this.getChildCount() > 0) {
+ this.mActivePointerId = -1;
+ this.endDrag();
+ }
+ case 4:
+ default:
+ break;
+ case 5:
+ index = MotionEventCompat.getActionIndex(ev);
+ this.mLastMotionY = (int)MotionEventCompat.getY(ev, index);
+ this.mActivePointerId = MotionEventCompat.getPointerId(ev, index);
+ break;
+ case 6:
+ this.onSecondaryPointerUp(ev);
+ this.mLastMotionY = (int)MotionEventCompat.getY(ev, MotionEventCompat.findPointerIndex(ev, this.mActivePointerId));
+ }
+
+ if(this.mVelocityTracker != null) {
+ this.mVelocityTracker.addMovement(vtev);
+ }
+
+ vtev.recycle();
+ return true;
+ }
+
+ private void onSecondaryPointerUp(MotionEvent ev) {
+ int pointerIndex = (ev.getAction() & '\uff00') >> 8;
+ int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
+ if(pointerId == this.mActivePointerId) {
+ int newPointerIndex = pointerIndex == 0?1:0;
+ this.mLastMotionY = (int)MotionEventCompat.getY(ev, newPointerIndex);
+ this.mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
+ if(this.mVelocityTracker != null) {
+ this.mVelocityTracker.clear();
+ }
+ }
+
+ }
+
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ if((MotionEventCompat.getSource(event) & 2) != 0) {
+ switch(event.getAction()) {
+ case 8:
+ if(!this.mIsBeingDragged) {
+ float vscroll = MotionEventCompat.getAxisValue(event, 9);
+ if(vscroll != 0.0F) {
+ int delta = (int)(vscroll * this.getVerticalScrollFactorCompat());
+ int range = this.getScrollRange();
+ int oldScrollY = this.getScrollY();
+ int newScrollY = oldScrollY - delta;
+ if(newScrollY < 0) {
+ newScrollY = 0;
+ } else if(newScrollY > range) {
+ newScrollY = range;
+ }
+
+ if(newScrollY != oldScrollY) {
+ super.scrollTo(this.getScrollX(), newScrollY);
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private float getVerticalScrollFactorCompat() {
+ if(this.mVerticalScrollFactor == 0.0F) {
+ TypedValue outValue = new TypedValue();
+ Context context = this.getContext();
+ if(!context.getTheme().resolveAttribute(16842829, outValue, true)) {
+ throw new IllegalStateException("Expected theme to define listPreferredItemHeight.");
+ }
+
+ this.mVerticalScrollFactor = outValue.getDimension(context.getResources().getDisplayMetrics());
+ }
+
+ return this.mVerticalScrollFactor;
+ }
+
+ protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
+ super.scrollTo(scrollX, scrollY);
+ }
+
+ boolean overScrollByCompat(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
+ int overScrollMode = ViewCompat.getOverScrollMode(this);
+ boolean canScrollHorizontal = this.computeHorizontalScrollRange() > this.computeHorizontalScrollExtent();
+ boolean canScrollVertical = this.computeVerticalScrollRange() > this.computeVerticalScrollExtent();
+ boolean overScrollHorizontal = overScrollMode == 0 || overScrollMode == 1 && canScrollHorizontal;
+ boolean overScrollVertical = overScrollMode == 0 || overScrollMode == 1 && canScrollVertical;
+ int newScrollX = scrollX + deltaX;
+ if(!overScrollHorizontal) {
+ maxOverScrollX = 0;
+ }
+
+ int newScrollY = scrollY + deltaY;
+ if(!overScrollVertical) {
+ maxOverScrollY = 0;
+ }
+
+ int left = -maxOverScrollX;
+ int right = maxOverScrollX + scrollRangeX;
+ int top = -maxOverScrollY;
+ int bottom = maxOverScrollY + scrollRangeY;
+ boolean clampedX = false;
+ if(newScrollX > right) {
+ newScrollX = right;
+ clampedX = true;
+ } else if(newScrollX < left) {
+ newScrollX = left;
+ clampedX = true;
+ }
+
+ boolean clampedY = false;
+ if(newScrollY > bottom) {
+ newScrollY = bottom;
+ clampedY = true;
+ } else if(newScrollY < top) {
+ newScrollY = top;
+ clampedY = true;
+ }
+
+ this.onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
+ return clampedX || clampedY;
+ }
+
+ private int getScrollRange() {
+ int scrollRange = 0;
+ if(this.getChildCount() > 0) {
+ View child = this.getChildAt(0);
+ scrollRange = Math.max(0, child.getHeight() - (this.getHeight() - this.getPaddingBottom() - this.getPaddingTop()));
+ }
+
+ return scrollRange;
+ }
+
+ private View findFocusableViewInBounds(boolean topFocus, int top, int bottom) {
+ //noinspection ResourceType
+ ArrayList focusables = this.getFocusables(2);
+ View focusCandidate = null;
+ boolean foundFullyContainedFocusable = false;
+ int count = focusables.size();
+
+ for(int i = 0; i < count; ++i) {
+ View view = (View)focusables.get(i);
+ int viewTop = view.getTop();
+ int viewBottom = view.getBottom();
+ if(top < viewBottom && viewTop < bottom) {
+ boolean viewIsFullyContained = top < viewTop && viewBottom < bottom;
+ if(focusCandidate == null) {
+ focusCandidate = view;
+ foundFullyContainedFocusable = viewIsFullyContained;
+ } else {
+ boolean viewIsCloserToBoundary = topFocus && viewTop < focusCandidate.getTop() || !topFocus && viewBottom > focusCandidate.getBottom();
+ if(foundFullyContainedFocusable) {
+ if(viewIsFullyContained && viewIsCloserToBoundary) {
+ focusCandidate = view;
+ }
+ } else if(viewIsFullyContained) {
+ focusCandidate = view;
+ foundFullyContainedFocusable = true;
+ } else if(viewIsCloserToBoundary) {
+ focusCandidate = view;
+ }
+ }
+ }
+ }
+
+ return focusCandidate;
+ }
+
+ public boolean pageScroll(int direction) {
+ boolean down = direction == 130;
+ int height = this.getHeight();
+ if(down) {
+ this.mTempRect.top = this.getScrollY() + height;
+ int count = this.getChildCount();
+ if(count > 0) {
+ View view = this.getChildAt(count - 1);
+ if(this.mTempRect.top + height > view.getBottom()) {
+ this.mTempRect.top = view.getBottom() - height;
+ }
+ }
+ } else {
+ this.mTempRect.top = this.getScrollY() - height;
+ if(this.mTempRect.top < 0) {
+ this.mTempRect.top = 0;
+ }
+ }
+
+ this.mTempRect.bottom = this.mTempRect.top + height;
+ return this.scrollAndFocus(direction, this.mTempRect.top, this.mTempRect.bottom);
+ }
+
+ public boolean fullScroll(int direction) {
+ boolean down = direction == 130;
+ int height = this.getHeight();
+ this.mTempRect.top = 0;
+ this.mTempRect.bottom = height;
+ if(down) {
+ int count = this.getChildCount();
+ if(count > 0) {
+ View view = this.getChildAt(count - 1);
+ this.mTempRect.bottom = view.getBottom() + this.getPaddingBottom();
+ this.mTempRect.top = this.mTempRect.bottom - height;
+ }
+ }
+
+ return this.scrollAndFocus(direction, this.mTempRect.top, this.mTempRect.bottom);
+ }
+
+ private boolean scrollAndFocus(int direction, int top, int bottom) {
+ boolean handled = true;
+ int height = this.getHeight();
+ int containerTop = this.getScrollY();
+ int containerBottom = containerTop + height;
+ boolean up = direction == 33;
+ Object newFocused = this.findFocusableViewInBounds(up, top, bottom);
+ if(newFocused == null) {
+ newFocused = this;
+ }
+
+ if(top >= containerTop && bottom <= containerBottom) {
+ handled = false;
+ } else {
+ int delta = up?top - containerTop:bottom - containerBottom;
+ this.doScrollY(delta);
+ }
+
+ if(newFocused != this.findFocus()) {
+ ((View)newFocused).requestFocus(direction);
+ }
+
+ return handled;
+ }
+
+ public boolean arrowScroll(int direction) {
+ View currentFocused = this.findFocus();
+ if(currentFocused == this) {
+ currentFocused = null;
+ }
+
+ View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
+ int maxJump = this.getMaxScrollAmount();
+ int descendantFocusability;
+ if(nextFocused != null && this.isWithinDeltaOfScreen(nextFocused, maxJump, this.getHeight())) {
+ nextFocused.getDrawingRect(this.mTempRect);
+ this.offsetDescendantRectToMyCoords(nextFocused, this.mTempRect);
+ descendantFocusability = this.computeScrollDeltaToGetChildRectOnScreen(this.mTempRect);
+ this.doScrollY(descendantFocusability);
+ nextFocused.requestFocus(direction);
+ } else {
+ descendantFocusability = maxJump;
+ if(direction == 33 && this.getScrollY() < maxJump) {
+ descendantFocusability = this.getScrollY();
+ } else if(direction == 130 && this.getChildCount() > 0) {
+ int daBottom = this.getChildAt(0).getBottom();
+ int screenBottom = this.getScrollY() + this.getHeight() - this.getPaddingBottom();
+ if(daBottom - screenBottom < maxJump) {
+ descendantFocusability = daBottom - screenBottom;
+ }
+ }
+
+ if(descendantFocusability == 0) {
+ return false;
+ }
+
+ this.doScrollY(direction == 130?descendantFocusability:-descendantFocusability);
+ }
+
+ if(currentFocused != null && currentFocused.isFocused() && this.isOffScreen(currentFocused)) {
+ descendantFocusability = this.getDescendantFocusability();
+ //noinspection ResourceType
+ this.setDescendantFocusability(131072);
+ this.requestFocus();
+ this.setDescendantFocusability(descendantFocusability);
+ }
+
+ return true;
+ }
+
+ private boolean isOffScreen(View descendant) {
+ return !this.isWithinDeltaOfScreen(descendant, 0, this.getHeight());
+ }
+
+ private boolean isWithinDeltaOfScreen(View descendant, int delta, int height) {
+ descendant.getDrawingRect(this.mTempRect);
+ this.offsetDescendantRectToMyCoords(descendant, this.mTempRect);
+ return this.mTempRect.bottom + delta >= this.getScrollY() && this.mTempRect.top - delta <= this.getScrollY() + height;
+ }
+
+ private void doScrollY(int delta) {
+ if(delta != 0) {
+ if(this.mSmoothScrollingEnabled) {
+ this.smoothScrollBy(0, delta);
+ } else {
+ this.scrollBy(0, delta);
+ }
+ }
+
+ }
+
+ public final void smoothScrollBy(int dx, int dy) {
+ if(this.getChildCount() != 0) {
+ long duration = AnimationUtils.currentAnimationTimeMillis() - this.mLastScroll;
+ if(duration > 250L) {
+ int height = this.getHeight() - this.getPaddingBottom() - this.getPaddingTop();
+ int bottom = this.getChildAt(0).getHeight();
+ int maxY = Math.max(0, bottom - height);
+ int scrollY = this.getScrollY();
+ dy = Math.max(0, Math.min(scrollY + dy, maxY)) - scrollY;
+ this.mScroller.startScroll(this.getScrollX(), scrollY, 0, dy);
+ ViewCompat.postInvalidateOnAnimation(this);
+ } else {
+ if(!this.mScroller.isFinished()) {
+ this.mScroller.abortAnimation();
+ }
+
+ this.scrollBy(dx, dy);
+ }
+
+ this.mLastScroll = AnimationUtils.currentAnimationTimeMillis();
+ }
+ }
+
+ public final void smoothScrollTo(int x, int y) {
+ this.smoothScrollBy(x - this.getScrollX(), y - this.getScrollY());
+ }
+
+ protected int computeVerticalScrollRange() {
+ int count = this.getChildCount();
+ int contentHeight = this.getHeight() - this.getPaddingBottom() - this.getPaddingTop();
+ if(count == 0) {
+ return contentHeight;
+ } else {
+ int scrollRange = this.getChildAt(0).getBottom();
+ int scrollY = this.getScrollY();
+ int overscrollBottom = Math.max(0, scrollRange - contentHeight);
+ if(scrollY < 0) {
+ scrollRange -= scrollY;
+ } else if(scrollY > overscrollBottom) {
+ scrollRange += scrollY - overscrollBottom;
+ }
+
+ return scrollRange;
+ }
+ }
+
+ protected int computeVerticalScrollOffset() {
+ return Math.max(0, super.computeVerticalScrollOffset());
+ }
+
+ protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, this.getPaddingLeft() + this.getPaddingRight(), lp.width);
+ //noinspection ResourceType
+ int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, 0);
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ }
+
+ protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
+ MarginLayoutParams lp = (MarginLayoutParams)child.getLayoutParams();
+ int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, this.getPaddingLeft() + this.getPaddingRight() + lp.leftMargin + lp.rightMargin + widthUsed, lp.width);
+ //noinspection ResourceType
+ int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.topMargin + lp.bottomMargin, 0);
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ }
+
+ public void computeScroll() {
+ if(this.mScroller.computeScrollOffset()) {
+ int oldX = this.getScrollX();
+ int oldY = this.getScrollY();
+ int x = this.mScroller.getCurrX();
+ int y = this.mScroller.getCurrY();
+ if(oldX != x || oldY != y) {
+ int range = this.getScrollRange();
+ int overscrollMode = ViewCompat.getOverScrollMode(this);
+ boolean canOverscroll = overscrollMode == 0 || overscrollMode == 1 && range > 0;
+ this.overScrollByCompat(x - oldX, y - oldY, oldX, oldY, 0, range, 0, 0, false);
+ if(canOverscroll) {
+ this.ensureGlows();
+ if(y <= 0 && oldY > 0) {
+ this.mEdgeGlowTop.onAbsorb((int)this.mScroller.getCurrVelocity());
+ } else if(y >= range && oldY < range) {
+ this.mEdgeGlowBottom.onAbsorb((int)this.mScroller.getCurrVelocity());
+ }
+ }
+ }
+ }
+
+ }
+
+ private void scrollToChild(View child) {
+ child.getDrawingRect(this.mTempRect);
+ this.offsetDescendantRectToMyCoords(child, this.mTempRect);
+ int scrollDelta = this.computeScrollDeltaToGetChildRectOnScreen(this.mTempRect);
+ if(scrollDelta != 0) {
+ this.scrollBy(0, scrollDelta);
+ }
+
+ }
+
+ private boolean scrollToChildRect(Rect rect, boolean immediate) {
+ int delta = this.computeScrollDeltaToGetChildRectOnScreen(rect);
+ boolean scroll = delta != 0;
+ if(scroll) {
+ if(immediate) {
+ this.scrollBy(0, delta);
+ } else {
+ this.smoothScrollBy(0, delta);
+ }
+ }
+
+ return scroll;
+ }
+
+ protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
+ if(this.getChildCount() == 0) {
+ return 0;
+ } else {
+ int height = this.getHeight();
+ int screenTop = this.getScrollY();
+ int screenBottom = screenTop + height;
+ int fadingEdge = this.getVerticalFadingEdgeLength();
+ if(rect.top > 0) {
+ screenTop += fadingEdge;
+ }
+
+ if(rect.bottom < this.getChildAt(0).getHeight()) {
+ screenBottom -= fadingEdge;
+ }
+
+ int scrollYDelta = 0;
+ if(rect.bottom > screenBottom && rect.top > screenTop) {
+ if(rect.height() > height) {
+ scrollYDelta += rect.top - screenTop;
+ } else {
+ scrollYDelta += rect.bottom - screenBottom;
+ }
+
+ int bottom = this.getChildAt(0).getBottom();
+ int distanceToBottom = bottom - screenBottom;
+ scrollYDelta = Math.min(scrollYDelta, distanceToBottom);
+ } else if(rect.top < screenTop && rect.bottom < screenBottom) {
+ if(rect.height() > height) {
+ scrollYDelta -= screenBottom - rect.bottom;
+ } else {
+ scrollYDelta -= screenTop - rect.top;
+ }
+
+ scrollYDelta = Math.max(scrollYDelta, -this.getScrollY());
+ }
+
+ return scrollYDelta;
+ }
+ }
+
+ public void requestChildFocus(View child, View focused) {
+ if(!this.mIsLayoutDirty) {
+ this.scrollToChild(focused);
+ } else {
+ this.mChildToScrollTo = focused;
+ }
+
+ super.requestChildFocus(child, focused);
+ }
+
+ protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+ if(direction == 2) {
+ direction = 130;
+ } else if(direction == 1) {
+ direction = 33;
+ }
+
+ View nextFocus = previouslyFocusedRect == null?FocusFinder.getInstance().findNextFocus(this, (View)null, direction):FocusFinder.getInstance().findNextFocusFromRect(this, previouslyFocusedRect, direction);
+ return nextFocus == null?false:(this.isOffScreen(nextFocus)?false:nextFocus.requestFocus(direction, previouslyFocusedRect));
+ }
+
+ public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
+ rectangle.offset(child.getLeft() - child.getScrollX(), child.getTop() - child.getScrollY());
+ return this.scrollToChildRect(rectangle, immediate);
+ }
+
+ public void requestLayout() {
+ this.mIsLayoutDirty = true;
+ super.requestLayout();
+ }
+
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ this.mIsLayoutDirty = false;
+ if(this.mChildToScrollTo != null && isViewDescendantOf(this.mChildToScrollTo, this)) {
+ this.scrollToChild(this.mChildToScrollTo);
+ }
+
+ this.mChildToScrollTo = null;
+ if(!this.mIsLaidOut) {
+ if(this.mSavedState != null) {
+ this.scrollTo(this.getScrollX(), this.mSavedState.scrollPosition);
+ this.mSavedState = null;
+ }
+
+ int childHeight = this.getChildCount() > 0?this.getChildAt(0).getMeasuredHeight():0;
+ int scrollRange = Math.max(0, childHeight - (b - t - this.getPaddingBottom() - this.getPaddingTop()));
+ if(this.getScrollY() > scrollRange) {
+ this.scrollTo(this.getScrollX(), scrollRange);
+ } else if(this.getScrollY() < 0) {
+ this.scrollTo(this.getScrollX(), 0);
+ }
+ }
+
+ this.scrollTo(this.getScrollX(), this.getScrollY());
+ this.mIsLaidOut = true;
+ }
+
+ @SuppressLint("MissingSuperCall")
+ public void onAttachedToWindow() {
+ this.mIsLaidOut = false;
+ }
+
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ View currentFocused = this.findFocus();
+ if(null != currentFocused && this != currentFocused) {
+ if(this.isWithinDeltaOfScreen(currentFocused, 0, oldh)) {
+ currentFocused.getDrawingRect(this.mTempRect);
+ this.offsetDescendantRectToMyCoords(currentFocused, this.mTempRect);
+ int scrollDelta = this.computeScrollDeltaToGetChildRectOnScreen(this.mTempRect);
+ this.doScrollY(scrollDelta);
+ }
+
+ }
+ }
+
+ private static boolean isViewDescendantOf(View child, View parent) {
+ if(child == parent) {
+ return true;
+ } else {
+ ViewParent theParent = child.getParent();
+ return theParent instanceof ViewGroup && isViewDescendantOf((View)theParent, parent);
+ }
+ }
+
+ /**
+ * Adjusted from AppCompat v23.0.0 so that the function returns true if the nested fling will
+ * end at the top of the scroll view. Which means that it should be dispatched to the
+ * CoordinatorLayout/AppBarLayout
+ * @param velocityY
+ * @return
+ */
+ public boolean fling(int velocityY) {
+ if(this.getChildCount() > 0) {
+ int height = this.getHeight() - this.getPaddingBottom() - this.getPaddingTop();
+ int bottom = this.getChildAt(0).getHeight();
+ this.mScroller.fling(this.getScrollX(), this.getScrollY(), 0, velocityY, 0, 0, 0, Math.max(0, bottom - height), 0, height / 2);
+ ViewCompat.postInvalidateOnAnimation(this);
+ return mScroller.getFinalY() == 0;
+ }
+ return false;
+ }
+
+ private void flingWithNestedDispatch(int velocityY) {
+ int scrollY = this.getScrollY();
+ boolean canFling = (scrollY > 0 || velocityY > 0) && (scrollY < this.getScrollRange() || velocityY < 0);
+ if(!this.dispatchNestedPreFling(0.0F, (float)velocityY)) {
+ boolean dispatchFling = true;
+ if (canFling)
+ dispatchFling = fling(velocityY);
+ this.dispatchNestedFling(0.0F, (float)velocityY, !dispatchFling);
+ }
+
+ }
+
+ private void endDrag() {
+ this.mIsBeingDragged = false;
+ this.recycleVelocityTracker();
+ this.stopNestedScroll();
+ if(this.mEdgeGlowTop != null) {
+ this.mEdgeGlowTop.onRelease();
+ this.mEdgeGlowBottom.onRelease();
+ }
+
+ }
+
+ public void scrollTo(int x, int y) {
+ if(this.getChildCount() > 0) {
+ View child = this.getChildAt(0);
+ x = clamp(x, this.getWidth() - this.getPaddingRight() - this.getPaddingLeft(), child.getWidth());
+ y = clamp(y, this.getHeight() - this.getPaddingBottom() - this.getPaddingTop(), child.getHeight());
+ if(x != this.getScrollX() || y != this.getScrollY()) {
+ super.scrollTo(x, y);
+ }
+ }
+
+ }
+
+ private void ensureGlows() {
+ if(ViewCompat.getOverScrollMode(this) != 2) {
+ if(this.mEdgeGlowTop == null) {
+ Context context = this.getContext();
+ this.mEdgeGlowTop = new EdgeEffectCompat(context);
+ this.mEdgeGlowBottom = new EdgeEffectCompat(context);
+ }
+ } else {
+ this.mEdgeGlowTop = null;
+ this.mEdgeGlowBottom = null;
+ }
+
+ }
+
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+ if(this.mEdgeGlowTop != null) {
+ int scrollY = this.getScrollY();
+ int restoreCount;
+ int width;
+ if(!this.mEdgeGlowTop.isFinished()) {
+ restoreCount = canvas.save();
+ width = this.getWidth() - this.getPaddingLeft() - this.getPaddingRight();
+ canvas.translate((float)this.getPaddingLeft(), (float)Math.min(0, scrollY));
+ this.mEdgeGlowTop.setSize(width, this.getHeight());
+ if(this.mEdgeGlowTop.draw(canvas)) {
+ ViewCompat.postInvalidateOnAnimation(this);
+ }
+
+ canvas.restoreToCount(restoreCount);
+ }
+
+ if(!this.mEdgeGlowBottom.isFinished()) {
+ restoreCount = canvas.save();
+ width = this.getWidth() - this.getPaddingLeft() - this.getPaddingRight();
+ int height = this.getHeight();
+ canvas.translate((float)(-width + this.getPaddingLeft()), (float)(Math.max(this.getScrollRange(), scrollY) + height));
+ canvas.rotate(180.0F, (float)width, 0.0F);
+ this.mEdgeGlowBottom.setSize(width, height);
+ if(this.mEdgeGlowBottom.draw(canvas)) {
+ ViewCompat.postInvalidateOnAnimation(this);
+ }
+
+ canvas.restoreToCount(restoreCount);
+ }
+ }
+
+ }
+
+ private static int clamp(int n, int my, int child) {
+ return my < child && n >= 0?(my + n > child?child - my:n):0;
+ }
+
+ protected void onRestoreInstanceState(Parcelable state) {
+ FlingNestedScrollView.SavedState ss = (FlingNestedScrollView.SavedState)state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ this.mSavedState = ss;
+ this.requestLayout();
+ }
+
+ protected Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ FlingNestedScrollView.SavedState ss = new FlingNestedScrollView.SavedState(superState);
+ ss.scrollPosition = this.getScrollY();
+ return ss;
+ }
+
+ static class AccessibilityDelegate extends AccessibilityDelegateCompat {
+ AccessibilityDelegate() {
+ }
+
+ public boolean performAccessibilityAction(View host, int action, Bundle arguments) {
+ if(super.performAccessibilityAction(host, action, arguments)) {
+ return true;
+ } else {
+ FlingNestedScrollView nsvHost = (FlingNestedScrollView)host;
+ if(!nsvHost.isEnabled()) {
+ return false;
+ } else {
+ int viewportHeight;
+ int targetScrollY;
+ switch(action) {
+ case 4096:
+ viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom() - nsvHost.getPaddingTop();
+ targetScrollY = Math.min(nsvHost.getScrollY() + viewportHeight, nsvHost.getScrollRange());
+ if(targetScrollY != nsvHost.getScrollY()) {
+ nsvHost.smoothScrollTo(0, targetScrollY);
+ return true;
+ }
+
+ return false;
+ case 8192:
+ viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom() - nsvHost.getPaddingTop();
+ targetScrollY = Math.max(nsvHost.getScrollY() - viewportHeight, 0);
+ if(targetScrollY != nsvHost.getScrollY()) {
+ nsvHost.smoothScrollTo(0, targetScrollY);
+ return true;
+ }
+
+ return false;
+ default:
+ return false;
+ }
+ }
+ }
+ }
+
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ FlingNestedScrollView nsvHost = (FlingNestedScrollView)host;
+ info.setClassName(ScrollView.class.getName());
+ if(nsvHost.isEnabled()) {
+ int scrollRange = nsvHost.getScrollRange();
+ if(scrollRange > 0) {
+ info.setScrollable(true);
+ if(nsvHost.getScrollY() > 0) {
+ info.addAction(8192);
+ }
+
+ if(nsvHost.getScrollY() < scrollRange) {
+ info.addAction(4096);
+ }
+ }
+ }
+
+ }
+
+ public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(host, event);
+ FlingNestedScrollView nsvHost = (FlingNestedScrollView)host;
+ event.setClassName(ScrollView.class.getName());
+ AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event);
+ boolean scrollable = nsvHost.getScrollRange() > 0;
+ record.setScrollable(scrollable);
+ record.setScrollX(nsvHost.getScrollX());
+ record.setScrollY(nsvHost.getScrollY());
+ record.setMaxScrollX(nsvHost.getScrollX());
+ record.setMaxScrollY(nsvHost.getScrollRange());
+ }
+ }
+
+ static class SavedState extends BaseSavedState {
+ public int scrollPosition;
+ public static final Creator<FlingNestedScrollView.SavedState> CREATOR = new Creator() {
+ public FlingNestedScrollView.SavedState createFromParcel(Parcel in) {
+ return new FlingNestedScrollView.SavedState(in);
+ }
+
+ public FlingNestedScrollView.SavedState[] newArray(int size) {
+ return new FlingNestedScrollView.SavedState[size];
+ }
+ };
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ public SavedState(Parcel source) {
+ super(source);
+ this.scrollPosition = source.readInt();
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(this.scrollPosition);
+ }
+
+ public String toString() {
+ return "HorizontalScrollView.SavedState{" + Integer.toHexString(System.identityHashCode(this)) + " scrollPosition=" + this.scrollPosition + "}";
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java b/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java
new file mode 100644
index 000000000..d35f1d751
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/CachingDataDecryptorFactory.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2013-2014 Philipp Jakubeit, Signe Rüsch, Dominik Schürmann
+ *
+ * Licensed under the Bouncy Castle License (MIT license). See LICENSE file for details.
+ */
+
+package org.spongycastle.openpgp.operator.jcajce;
+
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
+import org.spongycastle.openpgp.operator.PGPDataDecryptor;
+import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+public class CachingDataDecryptorFactory implements PublicKeyDataDecryptorFactory
+{
+ private final PublicKeyDataDecryptorFactory mWrappedDecryptor;
+ private final Map<ByteBuffer, byte[]> mSessionKeyCache;
+
+ private OperatorHelper mOperatorHelper;
+
+ public CachingDataDecryptorFactory(String providerName,
+ final Map<ByteBuffer,byte[]> sessionKeyCache)
+ {
+ mWrappedDecryptor = null;
+ mSessionKeyCache = sessionKeyCache;
+
+ mOperatorHelper = new OperatorHelper(new NamedJcaJceHelper(providerName));
+ }
+
+ public CachingDataDecryptorFactory(PublicKeyDataDecryptorFactory wrapped,
+ final Map<ByteBuffer,byte[]> sessionKeyCache)
+ {
+ mWrappedDecryptor = wrapped;
+ mSessionKeyCache = sessionKeyCache;
+
+ }
+
+ public boolean hasCachedSessionData(PGPPublicKeyEncryptedData encData) throws PGPException {
+ ByteBuffer bi = ByteBuffer.wrap(encData.getSessionKey()[0]);
+ return mSessionKeyCache.containsKey(bi);
+ }
+
+ public Map<ByteBuffer, byte[]> getCachedSessionKeys() {
+ return mSessionKeyCache;
+ }
+
+ public boolean canDecrypt() {
+ return mWrappedDecryptor != null;
+ }
+
+ @Override
+ public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData) throws PGPException {
+ ByteBuffer bi = ByteBuffer.wrap(secKeyData[0]); // encoded MPI
+ if (mSessionKeyCache.containsKey(bi)) {
+ return mSessionKeyCache.get(bi);
+ }
+
+ byte[] sessionData = mWrappedDecryptor.recoverSessionData(keyAlgorithm, secKeyData);
+ mSessionKeyCache.put(bi, sessionData);
+ return sessionData;
+ }
+
+ @Override
+ public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key)
+ throws PGPException {
+ if (mWrappedDecryptor != null) {
+ return mWrappedDecryptor.createDataDecryptor(withIntegrityPacket, encAlgorithm, key);
+ }
+ return mOperatorHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPublicKeyDataDecryptorFactoryBuilder.java b/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPublicKeyDataDecryptorFactoryBuilder.java
deleted file mode 100644
index 067bb3e19..000000000
--- a/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPublicKeyDataDecryptorFactoryBuilder.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/**
- * Copyright (c) 2013-2014 Philipp Jakubeit, Signe Rüsch, Dominik Schürmann
- *
- * Licensed under the Bouncy Castle License (MIT license). See LICENSE file for details.
- */
-
-package org.spongycastle.openpgp.operator.jcajce;
-
-import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
-import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
-import org.spongycastle.jcajce.util.NamedJcaJceHelper;
-import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.operator.PGPDataDecryptor;
-import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
-
-import java.nio.ByteBuffer;
-import java.security.Provider;
-import java.util.Map;
-
-
-/**
- * This class is based on JcePublicKeyDataDecryptorFactoryBuilder
- *
- */
-public class NfcSyncPublicKeyDataDecryptorFactoryBuilder
-{
- private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
- private OperatorHelper contentHelper = new OperatorHelper(new DefaultJcaJceHelper());
- private JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter();
-// private JcaPGPDigestCalculatorProviderBuilder digestCalculatorProviderBuilder = new JcaPGPDigestCalculatorProviderBuilder();
-// private JcaKeyFingerprintCalculator fingerprintCalculator = new JcaKeyFingerprintCalculator();
-
- public static class NfcInteractionNeeded extends RuntimeException
- {
- public byte[] encryptedSessionKey;
-
- public NfcInteractionNeeded(byte[] encryptedSessionKey)
- {
- super("NFC interaction required!");
- this.encryptedSessionKey = encryptedSessionKey;
- }
- }
-
- public NfcSyncPublicKeyDataDecryptorFactoryBuilder()
- {
- }
-
- /**
- * Set the provider object to use for creating cryptographic primitives in the resulting factory the builder produces.
- *
- * @param provider provider object for cryptographic primitives.
- * @return the current builder.
- */
- public NfcSyncPublicKeyDataDecryptorFactoryBuilder setProvider(Provider provider)
- {
- this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
- keyConverter.setProvider(provider);
- this.contentHelper = helper;
-
- return this;
- }
-
- /**
- * Set the provider name to use for creating cryptographic primitives in the resulting factory the builder produces.
- *
- * @param providerName the name of the provider to reference for cryptographic primitives.
- * @return the current builder.
- */
- public NfcSyncPublicKeyDataDecryptorFactoryBuilder setProvider(String providerName)
- {
- this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName));
- keyConverter.setProvider(providerName);
- this.contentHelper = helper;
-
- return this;
- }
-
- public NfcSyncPublicKeyDataDecryptorFactoryBuilder setContentProvider(Provider provider)
- {
- this.contentHelper = new OperatorHelper(new ProviderJcaJceHelper(provider));
-
- return this;
- }
-
- public NfcSyncPublicKeyDataDecryptorFactoryBuilder setContentProvider(String providerName)
- {
- this.contentHelper = new OperatorHelper(new NamedJcaJceHelper(providerName));
-
- return this;
- }
-
- public PublicKeyDataDecryptorFactory build(final Map<ByteBuffer,byte[]> nfcDecryptedMap) {
- return new PublicKeyDataDecryptorFactory()
- {
- public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
- throws PGPException
- {
- if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH)
- {
- throw new PGPException("ECDH not supported!");
- }
-
- return decryptSessionData(keyAlgorithm, secKeyData, nfcDecryptedMap);
- }
-
- public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key)
- throws PGPException
- {
- return contentHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key);
- }
- };
- }
-
-// public PublicKeyDataDecryptorFactory build(final PrivateKey privKey)
-// {
-// return new PublicKeyDataDecryptorFactory()
-// {
-// public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
-// throws PGPException
-// {
-// if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH)
-// {
-// throw new PGPException("ECDH requires use of PGPPrivateKey for decryption");
-// }
-// return decryptSessionData(keyAlgorithm, privKey, secKeyData);
-// }
-//
-// public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key)
-// throws PGPException
-// {
-// return contentHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key);
-// }
-// };
-// }
-
-// public PublicKeyDataDecryptorFactory build(final PGPPrivateKey privKey, final byte[] nfcDecrypted)
-// {
-// return new PublicKeyDataDecryptorFactory()
-// {
-// public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
-// throws PGPException
-// {
-// if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH)
-// {
-// return decryptSessionData(privKey.getPrivateKeyDataPacket(), privKey.getPublicKeyPacket(), secKeyData);
-// }
-//
-// return decryptSessionData(keyAlgorithm, keyConverter.getPrivateKey(privKey), secKeyData, nfcDecrypted);
-// }
-//
-// public PGPDataDecryptor createDataDecryptor(boolean withIntegrityPacket, int encAlgorithm, byte[] key)
-// throws PGPException
-// {
-// return contentHelper.createDataDecryptor(withIntegrityPacket, encAlgorithm, key);
-// }
-// };
-// }
-
-// private byte[] decryptSessionData(BCPGKey privateKeyPacket, PublicKeyPacket pubKeyData, byte[][] secKeyData)
-// throws PGPException
-// {
-// ECDHPublicBCPGKey ecKey = (ECDHPublicBCPGKey)pubKeyData.getKey();
-// X9ECParameters x9Params = NISTNamedCurves.getByOID(ecKey.getCurveOID());
-//
-// byte[] enc = secKeyData[0];
-//
-// int pLen = ((((enc[0] & 0xff) << 8) + (enc[1] & 0xff)) + 7) / 8;
-// byte[] pEnc = new byte[pLen];
-//
-// System.arraycopy(enc, 2, pEnc, 0, pLen);
-//
-// byte[] keyEnc = new byte[enc[pLen + 2]];
-//
-// System.arraycopy(enc, 2 + pLen + 1, keyEnc, 0, keyEnc.length);
-//
-// Cipher c = helper.createKeyWrapper(ecKey.getSymmetricKeyAlgorithm());
-//
-// ECPoint S = x9Params.getCurve().decodePoint(pEnc).multiply(((ECSecretBCPGKey)privateKeyPacket).getX()).normalize();
-//
-// RFC6637KDFCalculator rfc6637KDFCalculator = new RFC6637KDFCalculator(digestCalculatorProviderBuilder.build().get(ecKey.getHashAlgorithm()), ecKey.getSymmetricKeyAlgorithm());
-// Key key = new SecretKeySpec(rfc6637KDFCalculator.createKey(ecKey.getCurveOID(), S, fingerprintCalculator.calculateFingerprint(pubKeyData)), "AESWrap");
-//
-// try
-// {
-// c.init(Cipher.UNWRAP_MODE, key);
-//
-// Key paddedSessionKey = c.unwrap(keyEnc, "Session", Cipher.SECRET_KEY);
-//
-// return PGPPad.unpadSessionData(paddedSessionKey.getEncoded());
-// }
-// catch (InvalidKeyException e)
-// {
-// throw new PGPException("error setting asymmetric cipher", e);
-// }
-// catch (NoSuchAlgorithmException e)
-// {
-// throw new PGPException("error setting asymmetric cipher", e);
-// }
-// }
-
- private byte[] decryptSessionData(int keyAlgorithm, byte[][] secKeyData,
- Map<ByteBuffer,byte[]> nfcDecryptedMap)
- throws PGPException
- {
-// Cipher c1 = helper.createPublicKeyCipher(keyAlgorithm);
-//
-// try
-// {
-// c1.init(Cipher.DECRYPT_MODE, privKey);
-// }
-// catch (InvalidKeyException e)
-// {
-// throw new PGPException("error setting asymmetric cipher", e);
-// }
-
- if (keyAlgorithm == PGPPublicKey.RSA_ENCRYPT
- || keyAlgorithm == PGPPublicKey.RSA_GENERAL)
- {
- ByteBuffer bi = ByteBuffer.wrap(secKeyData[0]); // encoded MPI
-
- if (nfcDecryptedMap.containsKey(bi)) {
- return nfcDecryptedMap.get(bi);
- } else {
- // catch this when decryptSessionData() is executed and divert digest to card,
- // when doing the operation again reuse nfcDecrypted
- throw new NfcInteractionNeeded(bi.array());
- }
-
-// c1.update(bi, 2, bi.length - 2);
- }
- else
- {
- throw new PGPException("ElGamal not supported!");
-
-// ElGamalKey k = (ElGamalKey)privKey;
-// int size = (k.getParameters().getP().bitLength() + 7) / 8;
-// byte[] tmp = new byte[size];
-//
-// byte[] bi = secKeyData[0]; // encoded MPI
-// if (bi.length - 2 > size) // leading Zero? Shouldn't happen but...
-// {
-// c1.update(bi, 3, bi.length - 3);
-// }
-// else
-// {
-// System.arraycopy(bi, 2, tmp, tmp.length - (bi.length - 2), bi.length - 2);
-// c1.update(tmp);
-// }
-//
-// bi = secKeyData[1]; // encoded MPI
-// for (int i = 0; i != tmp.length; i++)
-// {
-// tmp[i] = 0;
-// }
-//
-// if (bi.length - 2 > size) // leading Zero? Shouldn't happen but...
-// {
-// c1.update(bi, 3, bi.length - 3);
-// }
-// else
-// {
-// System.arraycopy(bi, 2, tmp, tmp.length - (bi.length - 2), bi.length - 2);
-// c1.update(tmp);
-// }
- }
-
-// try
-// {
-// return c1.doFinal();
-// }
-// catch (Exception e)
-// {
-// throw new PGPException("exception decrypting session data", e);
-// }
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
index fc1cb8acc..6a9656b28 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
@@ -19,26 +19,31 @@ package org.sufficientlysecure.keychain;
import android.os.Environment;
-import org.spongycastle.bcpg.HashAlgorithmTags;
-import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import java.io.File;
+import java.net.Proxy;
public final class Constants {
public static final boolean DEBUG = BuildConfig.DEBUG;
public static final boolean DEBUG_LOG_DB_QUERIES = false;
public static final boolean DEBUG_SYNC_REMOVE_CONTACTS = false;
+ public static final boolean DEBUG_KEYSERVER_SYNC = false;
- public static final String TAG = "Keychain";
+ public static final String TAG = DEBUG ? "Keychain D" : "Keychain";
public static final String PACKAGE_NAME = "org.sufficientlysecure.keychain";
- public static final String ACCOUNT_NAME = "OpenKeychain";
- public static final String ACCOUNT_TYPE = PACKAGE_NAME + ".account";
+ public static final String ACCOUNT_NAME = DEBUG ? "OpenKeychain D" : "OpenKeychain";
+ public static final String ACCOUNT_TYPE = BuildConfig.ACCOUNT_TYPE;
public static final String CUSTOM_CONTACT_DATA_MIME_TYPE = "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.key";
+ public static final String PROVIDER_AUTHORITY = BuildConfig.PROVIDER_CONTENT_AUTHORITY;
+ public static final String TEMPSTORAGE_AUTHORITY = BuildConfig.APPLICATION_ID + ".tempstorage";
+
+ public static final String CLIPBOARD_LABEL = "Keychain";
+
// as defined in http://tools.ietf.org/html/rfc3156, section 7
public static final String NFC_MIME = "application/pgp-keys";
@@ -72,6 +77,11 @@ public final class Constants {
public static final File APP_DIR_FILE = new File(APP_DIR, "export.asc");
}
+ public static final class Notification {
+ public static final int PASSPHRASE_CACHE = 1;
+ public static final int KEYSERVER_SYNC_FAIL_ORBOT = 2;
+ }
+
public static final class Pref {
public static final String PASSPHRASE_CACHE_TTL = "passphraseCacheTtl";
public static final String PASSPHRASE_CACHE_SUBS = "passphraseCacheSubs";
@@ -84,11 +94,49 @@ public final class Constants {
public static final String SEARCH_KEYBASE = "search_keybase_pref";
public static final String USE_DEFAULT_YUBIKEY_PIN = "useDefaultYubikeyPin";
public static final String USE_NUMKEYPAD_FOR_YUBIKEY_PIN = "useNumKeypadForYubikeyPin";
+ public static final String ENCRYPT_FILENAMES = "encryptFilenames";
+ public static final String FILE_USE_COMPRESSION = "useFileCompression";
+ public static final String TEXT_USE_COMPRESSION = "useTextCompression";
+ public static final String USE_ARMOR = "useArmor";
+ // proxy settings
+ public static final String USE_NORMAL_PROXY = "useNormalProxy";
+ public static final String USE_TOR_PROXY = "useTorProxy";
+ public static final String PROXY_HOST = "proxyHost";
+ public static final String PROXY_PORT = "proxyPort";
+ public static final String PROXY_TYPE = "proxyType";
+ public static final String THEME = "theme";
+ // keyserver sync settings
+ public static final String SYNC_CONTACTS = "syncContacts";
+ public static final String SYNC_KEYSERVER = "syncKeyserver";
+ // other settings
+ public static final String EXPERIMENTAL_ENABLE_WORD_CONFIRM = "experimentalEnableWordConfirm";
+ public static final String EXPERIMENTAL_ENABLE_LINKED_IDENTITIES = "experimentalEnableLinkedIdentities";
+ public static final String EXPERIMENTAL_ENABLE_KEYBASE = "experimentalEnableKeybase";
+
+ public static final class Theme {
+ public static final String LIGHT = "light";
+ public static final String DARK = "dark";
+ public static final String DEFAULT = Constants.Pref.Theme.LIGHT;
+ }
+
+ public static final class ProxyType {
+ public static final String TYPE_HTTP = "proxyHttp";
+ public static final String TYPE_SOCKS = "proxySocks";
+ }
+ }
+
+ /**
+ * information to connect to Orbot's localhost HTTP proxy
+ */
+ public static final class Orbot {
+ public static final String PROXY_HOST = "127.0.0.1";
+ public static final int PROXY_PORT = 8118;
+ public static final Proxy.Type PROXY_TYPE = Proxy.Type.HTTP;
}
public static final class Defaults {
public static final String KEY_SERVERS = "hkps://hkps.pool.sks-keyservers.net, hkps://pgp.mit.edu";
- public static final int PREF_VERSION = 4;
+ public static final int PREF_VERSION = 6;
}
public static final class key {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
index 710dbf8aa..311ef2d3b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
@@ -23,7 +23,6 @@ import android.app.Application;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
@@ -33,8 +32,11 @@ import android.provider.ContactsContract;
import android.widget.Toast;
import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
+import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService;
import org.sufficientlysecure.keychain.ui.ConsolidateDialogActivity;
+import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PRNGFixes;
import org.sufficientlysecure.keychain.util.Preferences;
@@ -89,18 +91,21 @@ public class KeychainApplication extends Application {
}
brandGlowEffect(getApplicationContext(),
- getApplicationContext().getResources().getColor(R.color.primary));
+ FormattingUtils.getColorFromAttr(getApplicationContext(), R.attr.colorPrimary));
setupAccountAsNeeded(this);
// Update keyserver list as needed
- Preferences.getPreferences(this).updatePreferences();
+ Preferences.getPreferences(this).upgradePreferences(this);
TlsHelper.addStaticCA("pool.sks-keyservers.net", getAssets(), "sks-keyservers.netCA.cer");
TemporaryStorageProvider.cleanUp(this);
- checkConsolidateRecovery();
+ if (!checkConsolidateRecovery()) {
+ // force DB upgrade, https://github.com/open-keychain/open-keychain/issues/1334
+ new KeychainDatabase(this).getReadableDatabase().close();
+ }
}
public static HashMap<String,Bitmap> qrCodeCache = new HashMap<>();
@@ -117,29 +122,33 @@ public class KeychainApplication extends Application {
/**
* Restart consolidate process if it has been interruped before
*/
- public void checkConsolidateRecovery() {
+ public boolean checkConsolidateRecovery() {
if (Preferences.getPreferences(this).getCachedConsolidate()) {
Intent consolidateIntent = new Intent(this, ConsolidateDialogActivity.class);
consolidateIntent.putExtra(ConsolidateDialogActivity.EXTRA_CONSOLIDATE_RECOVERY, true);
consolidateIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(consolidateIntent);
+ return true;
+ } else {
+ return false;
}
}
/**
- * Add OpenKeychain account to Android to link contacts with keys
- *
- * @param context
+ * Add OpenKeychain account to Android to link contacts with keys and keyserver sync
*/
public static void setupAccountAsNeeded(Context context) {
try {
AccountManager manager = AccountManager.get(context);
Account[] accounts = manager.getAccountsByType(Constants.ACCOUNT_TYPE);
- if (accounts == null || accounts.length == 0) {
+
+ if (accounts.length == 0) {
Account account = new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE);
if (manager.addAccountExplicitly(account, null, null)) {
+ // for contact sync
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
+ KeyserverSyncAdapterService.enableKeyserverSync(context);
} else {
Log.e(Constants.TAG, "Adding account failed!");
}
@@ -165,7 +174,7 @@ public class KeychainApplication extends Application {
int edgeDrawableId = context.getResources().getIdentifier("overscroll_edge", "drawable", "android");
Drawable androidEdge = context.getResources().getDrawable(edgeDrawableId);
androidEdge.setColorFilter(brandColor, PorterDuff.Mode.SRC_IN);
- } catch (Resources.NotFoundException e) {
+ } catch (Exception ignored) {
}
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/AppCompatPreferenceActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/AppCompatPreferenceActivity.java
new file mode 100644
index 000000000..5200b8ced
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/AppCompatPreferenceActivity.java
@@ -0,0 +1,127 @@
+
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sufficientlysecure.keychain.compatibility;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatDelegate;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
+ * to be used with AppCompat.
+ * <p/>
+ * This technique can be used with an {@link android.app.Activity} class, not just
+ * {@link android.preference.PreferenceActivity}.
+ */
+public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
+ private AppCompatDelegate mDelegate;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ getDelegate().installViewFactory();
+ getDelegate().onCreate(savedInstanceState);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ getDelegate().onPostCreate(savedInstanceState);
+ }
+
+ public ActionBar getSupportActionBar() {
+ return getDelegate().getSupportActionBar();
+ }
+
+ public void setSupportActionBar(@Nullable Toolbar toolbar) {
+ getDelegate().setSupportActionBar(toolbar);
+ }
+
+ @Override
+ public MenuInflater getMenuInflater() {
+ return getDelegate().getMenuInflater();
+ }
+
+ @Override
+ public void setContentView(@LayoutRes int layoutResID) {
+ getDelegate().setContentView(layoutResID);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ getDelegate().setContentView(view);
+ }
+
+ @Override
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().setContentView(view, params);
+ }
+
+ @Override
+ public void addContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().addContentView(view, params);
+ }
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ getDelegate().onPostResume();
+ }
+
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ super.onTitleChanged(title, color);
+ getDelegate().setTitle(title);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ getDelegate().onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ getDelegate().onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ getDelegate().onDestroy();
+ }
+
+ public void invalidateOptionsMenu() {
+ getDelegate().invalidateOptionsMenu();
+ }
+
+ private AppCompatDelegate getDelegate() {
+ if (mDelegate == null) {
+ mDelegate = AppCompatDelegate.create(this, null);
+ }
+ return mDelegate;
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java
index fa3600ffb..abf16851d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java
@@ -17,79 +17,35 @@
package org.sufficientlysecure.keychain.compatibility;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
import android.content.Context;
+import android.support.annotation.Nullable;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
-import java.lang.reflect.Method;
-
public class ClipboardReflection {
- private static final String clipboardLabel = "Keychain";
-
- /**
- * Wrapper around ClipboardManager based on Android version using Reflection API
- *
- * @param context
- * @param text
- */
- public static void copyToClipboard(Context context, String text) {
- Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE);
- try {
- if ("android.text.ClipboardManager".equals(clipboard.getClass().getName())) {
- Method methodSetText = clipboard.getClass()
- .getMethod("setText", CharSequence.class);
- methodSetText.invoke(clipboard, text);
- } else if ("android.content.ClipboardManager".equals(clipboard.getClass().getName())) {
- Class<?> classClipData = Class.forName("android.content.ClipData");
- Method methodNewPlainText = classClipData.getMethod("newPlainText",
- CharSequence.class, CharSequence.class);
- Object clip = methodNewPlainText.invoke(null, clipboardLabel, text);
- methodNewPlainText = clipboard.getClass()
- .getMethod("setPrimaryClip", classClipData);
- methodNewPlainText.invoke(clipboard, clip);
- }
- } catch (Exception e) {
- Log.e(Constants.TAG, "There was an error copying the text to the clipboard", e);
+ @Nullable
+ public static String getClipboardText(@Nullable Context context) {
+ if (context == null) {
+ return null;
}
- }
-
- /**
- * Wrapper around ClipboardManager based on Android version using Reflection API
- *
- * @param context
- */
- public static CharSequence getClipboardText(Context context) {
- Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE);
- try {
- if ("android.text.ClipboardManager".equals(clipboard.getClass().getName())) {
- // CharSequence text = clipboard.getText();
- Method methodGetText = clipboard.getClass().getMethod("getText");
- Object text = methodGetText.invoke(clipboard);
+ ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
- return (CharSequence) text;
- } else if ("android.content.ClipboardManager".equals(clipboard.getClass().getName())) {
- // ClipData clipData = clipboard.getPrimaryClip();
- Method methodGetPrimaryClip = clipboard.getClass().getMethod("getPrimaryClip");
- Object clipData = methodGetPrimaryClip.invoke(clipboard);
-
- // ClipData.Item clipDataItem = clipData.getItemAt(0);
- Method methodGetItemAt = clipData.getClass().getMethod("getItemAt", int.class);
- Object clipDataItem = methodGetItemAt.invoke(clipData, 0);
-
- // CharSequence text = clipDataItem.coerceToText(context);
- Method methodGetString = clipDataItem.getClass().getMethod("coerceToText",
- Context.class);
- Object text = methodGetString.invoke(clipDataItem, context);
-
- return (CharSequence) text;
- } else {
- return null;
- }
- } catch (Exception e) {
- Log.e(Constants.TAG, "There was an error getting the text from the clipboard", e);
+ ClipData clip = clipboard.getPrimaryClip();
+ if (clip == null || clip.getItemCount() == 0) {
+ Log.e(Constants.TAG, "No clipboard data!");
return null;
}
+
+ ClipData.Item item = clip.getItemAt(0);
+ CharSequence seq = item.coerceToText(context);
+ if (seq != null) {
+ return seq.toString();
+ }
+ return null;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/DialogFragmentWorkaround.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/DialogFragmentWorkaround.java
index 07799f466..e1d8c0da7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/DialogFragmentWorkaround.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/DialogFragmentWorkaround.java
@@ -21,7 +21,7 @@ import android.os.Build;
import android.os.Handler;
/**
- * Bug on Android >= 4.2
+ * Bug on Android >= 4.2. Fixed in 4.2.2 ?
*
* http://code.google.com/p/android/issues/detail?id=41901
*
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java
index c0221fad3..d91dd28bc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java
@@ -20,6 +20,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
+import java.net.Proxy;
import java.util.ArrayList;
import java.util.Vector;
@@ -30,7 +31,8 @@ public class CloudSearch {
private final static long SECONDS = 1000;
- public static ArrayList<ImportKeysListEntry> search(final String query, Preferences.CloudSearchPrefs cloudPrefs)
+ public static ArrayList<ImportKeysListEntry> search(final String query, Preferences.CloudSearchPrefs cloudPrefs,
+ final Proxy proxy)
throws Keyserver.CloudSearchFailureException {
final ArrayList<Keyserver> servers = new ArrayList<>();
@@ -45,31 +47,42 @@ public class CloudSearch {
}
final ImportKeysList results = new ImportKeysList(servers.size());
+ ArrayList<Thread> searchThreads = new ArrayList<>();
for (final Keyserver keyserver : servers) {
Runnable r = new Runnable() {
@Override
public void run() {
try {
- results.addAll(keyserver.search(query));
+ results.addAll(keyserver.search(query, proxy));
} catch (Keyserver.CloudSearchFailureException e) {
problems.add(e);
}
results.finishedAdding(); // notifies if all searchers done
}
};
- new Thread(r).start();
+ Thread searchThread = new Thread(r);
+ searchThreads.add(searchThread);
+ searchThread.start();
}
- // wait for either all the searches to come back, or 10 seconds
- synchronized(results) {
+ // wait for either all the searches to come back, or 10 seconds. If using proxy, wait 30 seconds.
+ synchronized (results) {
try {
- results.wait(10 * SECONDS);
+ if (proxy != null) {
+ results.wait(30 * SECONDS);
+ } else {
+ results.wait(10 * SECONDS);
+ }
+ for (Thread thread : searchThreads) {
+ // kill threads that haven't returned yet
+ thread.interrupt();
+ }
} catch (InterruptedException e) {
}
}
if (results.outstandingSuppliers() > 0) {
- String message = "Launched " + servers.size() + " cloud searchers, but" +
+ String message = "Launched " + servers.size() + " cloud searchers, but " +
results.outstandingSuppliers() + "failed to complete.";
problems.add(new Keyserver.QueryFailedException(message));
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
index cb8a53e25..558b8ce7d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
@@ -18,18 +18,20 @@
package org.sufficientlysecure.keychain.keyimport;
+import com.squareup.okhttp.MediaType;
+import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.Request;
+import com.squareup.okhttp.RequestBody;
+import com.squareup.okhttp.Response;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.TlsHelper;
-import java.io.BufferedWriter;
import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
-import java.net.HttpURLConnection;
+import java.net.Proxy;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
@@ -39,6 +41,7 @@ import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -190,36 +193,52 @@ public class HkpKeyserver extends Keyserver {
return mSecure ? "https://" : "http://";
}
- private HttpURLConnection openConnection(URL url) throws IOException {
- HttpURLConnection conn = null;
+ /**
+ * returns a client with pinned certificate if necessary
+ *
+ * @param url url to be queried by client
+ * @param proxy proxy to be used by client
+ * @return client with a pinned certificate if necesary
+ */
+ public static OkHttpClient getClient(URL url, Proxy proxy) throws IOException {
+ OkHttpClient client = new OkHttpClient();
+
try {
- conn = (HttpURLConnection) TlsHelper.openConnection(url);
+ TlsHelper.pinCertificateIfNecessary(client, url);
} catch (TlsHelper.TlsHelperException e) {
Log.w(Constants.TAG, e);
}
- if (conn == null) {
- conn = (HttpURLConnection) url.openConnection();
+
+ if (proxy != null) {
+ client.setProxy(proxy);
+ client.setConnectTimeout(30000, TimeUnit.MILLISECONDS);
+ } else {
+ client.setProxy(Proxy.NO_PROXY);
+ client.setConnectTimeout(5000, TimeUnit.MILLISECONDS);
}
- conn.setConnectTimeout(5000);
- conn.setReadTimeout(25000);
- return conn;
+ client.setReadTimeout(45000, TimeUnit.MILLISECONDS);
+
+ return client;
}
- private String query(String request) throws QueryFailedException, HttpError {
+ private String query(String request, Proxy proxy) throws QueryFailedException, HttpError {
try {
URL url = new URL(getUrlPrefix() + mHost + ":" + mPort + request);
- Log.d(Constants.TAG, "hkp keyserver query: " + url);
- HttpURLConnection conn = openConnection(url);
- conn.connect();
- int response = conn.getResponseCode();
- if (response >= 200 && response < 300) {
- return readAll(conn.getInputStream(), conn.getContentEncoding());
+ Log.d(Constants.TAG, "hkp keyserver query: " + url + " Proxy: " + proxy);
+ OkHttpClient client = getClient(url, proxy);
+ Response response = client.newCall(new Request.Builder().url(url).build()).execute();
+
+ String responseBody = response.body().string();// contains body both in case of success or failure
+
+ if (response.isSuccessful()) {
+ return responseBody;
} else {
- String data = readAll(conn.getErrorStream(), conn.getContentEncoding());
- throw new HttpError(response, data);
+ throw new HttpError(response.code(), responseBody);
}
} catch (IOException e) {
- throw new QueryFailedException("Keyserver '" + mHost + "' is unavailable. Check your Internet connection!");
+ Log.e(Constants.TAG, "IOException at HkpKeyserver", e);
+ throw new QueryFailedException("Keyserver '" + mHost + "' is unavailable. Check your Internet connection!" +
+ proxy == null?"":" Using proxy " + proxy);
}
}
@@ -232,7 +251,7 @@ public class HkpKeyserver extends Keyserver {
* @throws QueryNeedsRepairException
*/
@Override
- public ArrayList<ImportKeysListEntry> search(String query) throws QueryFailedException,
+ public ArrayList<ImportKeysListEntry> search(String query, Proxy proxy) throws QueryFailedException,
QueryNeedsRepairException {
ArrayList<ImportKeysListEntry> results = new ArrayList<>();
@@ -250,7 +269,7 @@ public class HkpKeyserver extends Keyserver {
String data;
try {
- data = query(request);
+ data = query(request, proxy);
} catch (HttpError e) {
if (e.getData() != null) {
Log.d(Constants.TAG, "returned error data: " + e.getData().toLowerCase(Locale.ENGLISH));
@@ -334,13 +353,14 @@ public class HkpKeyserver extends Keyserver {
}
@Override
- public String get(String keyIdHex) throws QueryFailedException {
+ public String get(String keyIdHex, Proxy proxy) throws QueryFailedException {
String request = "/pks/lookup?op=get&options=mr&search=" + keyIdHex;
- Log.d(Constants.TAG, "hkp keyserver get: " + request);
+ Log.d(Constants.TAG, "hkp keyserver get: " + request + " using Proxy: " + proxy);
String data;
try {
- data = query(request);
+ data = query(request, proxy);
} catch (HttpError httpError) {
+ Log.d(Constants.TAG, "Failed to get key at HkpKeyserver", httpError);
throw new QueryFailedException("not found");
}
Matcher matcher = PgpHelper.PGP_PUBLIC_KEY.matcher(data);
@@ -351,38 +371,38 @@ public class HkpKeyserver extends Keyserver {
}
@Override
- public void add(String armoredKey) throws AddKeyException {
+ public void add(String armoredKey, Proxy proxy) throws AddKeyException {
try {
- String request = "/pks/add";
+ String path = "/pks/add";
String params;
try {
params = "keytext=" + URLEncoder.encode(armoredKey, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new AddKeyException();
}
- URL url = new URL(getUrlPrefix() + mHost + ":" + mPort + request);
+ URL url = new URL(getUrlPrefix() + mHost + ":" + mPort + path);
Log.d(Constants.TAG, "hkp keyserver add: " + url.toString());
Log.d(Constants.TAG, "params: " + params);
- HttpURLConnection conn = openConnection(url);
- conn.setRequestMethod("POST");
- conn.addRequestProperty("Content-Type", "application/x-www-form-urlencoded");
- conn.setRequestProperty("Content-Length", Integer.toString(params.getBytes().length));
- conn.setDoInput(true);
- conn.setDoOutput(true);
+ RequestBody body = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"), params);
+
+ Request request = new Request.Builder()
+ .url(url)
+ .addHeader("Content-Type", "application/x-www-form-urlencoded")
+ .addHeader("Content-Length", Integer.toString(params.getBytes().length))
+ .post(body)
+ .build();
- OutputStream os = conn.getOutputStream();
- BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
- writer.write(params);
- writer.flush();
- writer.close();
- os.close();
+ Response response = getClient(url, proxy).newCall(request).execute();
- conn.connect();
+ Log.d(Constants.TAG, "response code: " + response.code());
+ Log.d(Constants.TAG, "answer: " + response.body().string());
+
+ if (response.code() != 200) {
+ throw new AddKeyException();
+ }
- Log.d(Constants.TAG, "response code: " + conn.getResponseCode());
- Log.d(Constants.TAG, "answer: " + readAll(conn.getInputStream(), conn.getContentEncoding()));
} catch (IOException e) {
Log.e(Constants.TAG, "IOException", e);
throw new AddKeyException();
@@ -398,6 +418,7 @@ public class HkpKeyserver extends Keyserver {
* Tries to find a server responsible for a given domain
*
* @return A responsible Keyserver or null if not found.
+ * TODO: PHILIP Add proxy functionality
*/
public static HkpKeyserver resolve(String domain) {
try {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java
index e310e9a3f..c2865410e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java
@@ -26,6 +26,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
+import java.net.Proxy;
import java.util.ArrayList;
import java.util.List;
@@ -34,7 +35,7 @@ public class KeybaseKeyserver extends Keyserver {
private String mQuery;
@Override
- public ArrayList<ImportKeysListEntry> search(String query) throws QueryFailedException,
+ public ArrayList<ImportKeysListEntry> search(String query, Proxy proxy) throws QueryFailedException,
QueryNeedsRepairException {
ArrayList<ImportKeysListEntry> results = new ArrayList<>();
@@ -48,7 +49,7 @@ public class KeybaseKeyserver extends Keyserver {
mQuery = query;
try {
- Iterable<Match> matches = Search.search(query);
+ Iterable<Match> matches = Search.search(query, proxy);
for (Match match : matches) {
results.add(makeEntry(match));
}
@@ -98,16 +99,16 @@ public class KeybaseKeyserver extends Keyserver {
}
@Override
- public String get(String id) throws QueryFailedException {
+ public String get(String id, Proxy proxy) throws QueryFailedException {
try {
- return User.keyForUsername(id);
+ return User.keyForUsername(id, proxy);
} catch (KeybaseException e) {
throw new QueryFailedException(e.getMessage());
}
}
@Override
- public void add(String armoredKey) throws AddKeyException {
+ public void add(String armoredKey, Proxy proxy) throws AddKeyException {
throw new AddKeyException();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java
index 5e4bd0b70..640b39f44 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java
@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.keyimport;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.Proxy;
import java.util.List;
public abstract class Keyserver {
@@ -31,6 +32,7 @@ public abstract class Keyserver {
public CloudSearchFailureException(String message) {
super(message);
}
+
public CloudSearchFailureException() {
super();
}
@@ -67,12 +69,12 @@ public abstract class Keyserver {
private static final long serialVersionUID = -507574859137295530L;
}
- public abstract List<ImportKeysListEntry> search(String query) throws QueryFailedException,
+ public abstract List<ImportKeysListEntry> search(String query, Proxy proxy) throws QueryFailedException,
QueryNeedsRepairException;
- public abstract String get(String keyIdHex) throws QueryFailedException;
+ public abstract String get(String keyIdHex, Proxy proxy) throws QueryFailedException;
- public abstract void add(String armoredKey) throws AddKeyException;
+ public abstract void add(String armoredKey, Proxy proxy) throws AddKeyException;
public static String readAll(InputStream in, String encoding) throws IOException {
ByteArrayOutputStream raw = new ByteArrayOutputStream();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java
index a824e73d7..e4026eaaf 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java
@@ -18,17 +18,22 @@
package org.sufficientlysecure.keychain.operations;
import android.content.Context;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
+import org.sufficientlysecure.keychain.Constants.key;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.concurrent.atomic.AtomicBoolean;
-public abstract class BaseOperation implements PassphraseCacheInterface {
+public abstract class BaseOperation <T extends Parcelable> implements PassphraseCacheInterface {
final public Context mContext;
final public Progressable mProgressable;
@@ -40,7 +45,7 @@ public abstract class BaseOperation implements PassphraseCacheInterface {
* of common methods for progress, cancellation and passphrase cache handling.
*
* An "operation" in this sense is a high level operation which is called
- * by the KeychainIntentService or OpenPgpService services. Concrete
+ * by the KeychainService or OpenPgpService services. Concrete
* subclasses of this class should implement either a single or a group of
* related operations. An operation must rely solely on its input
* parameters for operation specifics. It should also write a log of its
@@ -49,7 +54,7 @@ public abstract class BaseOperation implements PassphraseCacheInterface {
*
* An operation must *not* throw exceptions of any kind, errors should be
* handled as part of the OperationResult! Consequently, all handling of
- * errors in KeychainIntentService and OpenPgpService should consist of
+ * errors in KeychainService and OpenPgpService should consist of
* informational rather than operational means.
*
* Note that subclasses of this class should be either Android- or
@@ -73,6 +78,9 @@ public abstract class BaseOperation implements PassphraseCacheInterface {
mCancelled = cancelled;
}
+ @NonNull
+ public abstract OperationResult execute(T input, CryptoInputParcel cryptoInput);
+
public void updateProgress(int message, int current, int total) {
if (mProgressable != null) {
mProgressable.setProgress(message, current, total);
@@ -104,8 +112,11 @@ public abstract class BaseOperation implements PassphraseCacheInterface {
@Override
public Passphrase getCachedPassphrase(long subKeyId) throws NoSecretKeyException {
try {
- long masterKeyId = mProviderHelper.getMasterKeyId(subKeyId);
- return getCachedPassphrase(masterKeyId, subKeyId);
+ if (subKeyId != key.symmetric) {
+ long masterKeyId = mProviderHelper.getMasterKeyId(subKeyId);
+ return getCachedPassphrase(masterKeyId, subKeyId);
+ }
+ return getCachedPassphrase(key.symmetric, key.symmetric);
} catch (NotFoundException e) {
throw new PassphraseCacheInterface.NoSecretKeyException();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
index 051517abd..eeed24db0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
@@ -18,17 +18,18 @@
package org.sufficientlysecure.keychain.operations;
import android.content.Context;
+import android.support.annotation.NonNull;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
-import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
import org.sufficientlysecure.keychain.operations.results.CertifyResult;
+import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface;
import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation;
import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation.PgpCertifyResult;
import org.sufficientlysecure.keychain.pgp.Progressable;
@@ -43,28 +44,33 @@ import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
+import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
+import java.net.Proxy;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
-/** An operation which implements a high level user id certification operation.
- *
+/**
+ * An operation which implements a high level user id certification operation.
+ * <p/>
* This operation takes a specific CertifyActionsParcel as its input. These
* contain a masterKeyId to be used for certification, and a list of
* masterKeyIds and related user ids to certify.
*
* @see CertifyActionsParcel
- *
*/
-public class CertifyOperation extends BaseOperation {
+public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
- public CertifyOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) {
+ public CertifyOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean
+ cancelled) {
super(context, providerHelper, progressable, cancelled);
}
- public CertifyResult certify(CertifyActionsParcel parcel, CryptoInputParcel cryptoInput, String keyServerUri) {
+ @NonNull
+ @Override
+ public CertifyResult execute(CertifyActionsParcel parcel, CryptoInputParcel cryptoInput) {
OperationLog log = new OperationLog();
log.add(LogType.MSG_CRT, 0);
@@ -79,14 +85,46 @@ public class CertifyOperation extends BaseOperation {
log.add(LogType.MSG_CRT_UNLOCK, 1);
certificationKey = secretKeyRing.getSecretKey();
- if (!cryptoInput.hasPassphrase()) {
- return new CertifyResult(log, RequiredInputParcel.createRequiredSignPassphrase(
- certificationKey.getKeyId(), certificationKey.getKeyId(), null));
+ Passphrase passphrase;
+
+ switch (certificationKey.getSecretKeyType()) {
+ case PIN:
+ case PATTERN:
+ case PASSPHRASE:
+ passphrase = cryptoInput.getPassphrase();
+ if (passphrase == null) {
+ try {
+ passphrase = getCachedPassphrase(certificationKey.getKeyId(), certificationKey.getKeyId());
+ } catch (PassphraseCacheInterface.NoSecretKeyException ignored) {
+ // treat as a cache miss for error handling purposes
+ }
+ }
+
+ if (passphrase == null) {
+ return new CertifyResult(log,
+ RequiredInputParcel.createRequiredSignPassphrase(
+ certificationKey.getKeyId(),
+ certificationKey.getKeyId(),
+ null),
+ cryptoInput
+ );
+ }
+ break;
+
+ case PASSPHRASE_EMPTY:
+ passphrase = new Passphrase("");
+ break;
+
+ case DIVERT_TO_CARD:
+ // the unlock operation will succeed for passphrase == null in a divertToCard key
+ passphrase = null;
+ break;
+
+ default:
+ log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2);
+ return new CertifyResult(CertifyResult.RESULT_ERROR, log);
}
- // certification is always with the master key id, so use that one
- Passphrase passphrase = cryptoInput.getPassphrase();
-
if (!certificationKey.unlock(passphrase)) {
log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2);
return new CertifyResult(CertifyResult.RESULT_ERROR, log);
@@ -152,9 +190,9 @@ public class CertifyOperation extends BaseOperation {
}
- if ( ! allRequiredInput.isEmpty()) {
+ if (!allRequiredInput.isEmpty()) {
log.add(LogType.MSG_CRT_NFC_RETURN, 1);
- return new CertifyResult(log, allRequiredInput.build());
+ return new CertifyResult(log, allRequiredInput.build(), cryptoInput);
}
log.add(LogType.MSG_CRT_SAVING, 1);
@@ -165,11 +203,24 @@ public class CertifyOperation extends BaseOperation {
return new CertifyResult(CertifyResult.RESULT_CANCELLED, log);
}
+ // these variables are used inside the following loop, but they need to be created only once
HkpKeyserver keyServer = null;
- ImportExportOperation importExportOperation = null;
- if (keyServerUri != null) {
- keyServer = new HkpKeyserver(keyServerUri);
- importExportOperation = new ImportExportOperation(mContext, mProviderHelper, mProgressable);
+ ExportOperation exportOperation = null;
+ Proxy proxy = null;
+ if (parcel.keyServerUri != null) {
+ keyServer = new HkpKeyserver(parcel.keyServerUri);
+ exportOperation = new ExportOperation(mContext, mProviderHelper, mProgressable);
+ if (cryptoInput.getParcelableProxy() == null) {
+ // explicit proxy not set
+ if (!OrbotHelper.isOrbotInRequiredState(mContext)) {
+ return new CertifyResult(null,
+ RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput);
+ }
+ proxy = Preferences.getPreferences(mContext).getProxyPrefs()
+ .parcelableProxy.getProxy();
+ } else {
+ proxy = cryptoInput.getParcelableProxy().getProxy();
+ }
}
// Write all certified keys into the database
@@ -178,7 +229,8 @@ public class CertifyOperation extends BaseOperation {
// Check if we were cancelled
if (checkCancelled()) {
log.add(LogType.MSG_OPERATION_CANCELLED, 0);
- return new CertifyResult(CertifyResult.RESULT_CANCELLED, log, certifyOk, certifyError, uploadOk, uploadError);
+ return new CertifyResult(CertifyResult.RESULT_CANCELLED, log, certifyOk, certifyError, uploadOk,
+ uploadError);
}
log.add(LogType.MSG_CRT_SAVE, 2,
@@ -187,13 +239,16 @@ public class CertifyOperation extends BaseOperation {
mProviderHelper.clearLog();
SaveKeyringResult result = mProviderHelper.savePublicKeyRing(certifiedKey);
- if (importExportOperation != null) {
- // TODO use subresult, get rid of try/catch!
- try {
- importExportOperation.uploadKeyRingToServer(keyServer, certifiedKey);
+ if (exportOperation != null) {
+ ExportResult uploadResult = exportOperation.uploadKeyRingToServer(
+ keyServer,
+ certifiedKey,
+ proxy);
+ log.add(uploadResult, 2);
+
+ if (uploadResult.success()) {
uploadOk += 1;
- } catch (AddKeyException e) {
- Log.e(Constants.TAG, "error uploading key", e);
+ } else {
uploadError += 1;
}
}
@@ -205,19 +260,24 @@ public class CertifyOperation extends BaseOperation {
}
log.add(result, 2);
-
}
if (certifyOk == 0) {
log.add(LogType.MSG_CRT_ERROR_NOTHING, 0);
- return new CertifyResult(CertifyResult.RESULT_ERROR, log, certifyOk, certifyError, uploadOk, uploadError);
+ return new CertifyResult(CertifyResult.RESULT_ERROR, log, certifyOk, certifyError,
+ uploadOk, uploadError);
}
- log.add(LogType.MSG_CRT_SUCCESS, 0);
- //since only verified keys are synced to contacts, we need to initiate a sync now
+ // since only verified keys are synced to contacts, we need to initiate a sync now
ContactSyncAdapterService.requestSync();
-
- return new CertifyResult(CertifyResult.RESULT_OK, log, certifyOk, certifyError, uploadOk, uploadError);
+
+ log.add(LogType.MSG_CRT_SUCCESS, 0);
+ if (uploadError != 0) {
+ return new CertifyResult(CertifyResult.RESULT_WARNINGS, log, certifyOk, certifyError, uploadOk,
+ uploadError);
+ } else {
+ return new CertifyResult(CertifyResult.RESULT_OK, log, certifyOk, certifyError, uploadOk, uploadError);
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ConsolidateOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ConsolidateOperation.java
new file mode 100644
index 000000000..782cd6800
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ConsolidateOperation.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.operations;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+
+import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
+import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.ConsolidateInputParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+
+public class ConsolidateOperation extends BaseOperation<ConsolidateInputParcel> {
+
+ public ConsolidateOperation(Context context, ProviderHelper providerHelper, Progressable
+ progressable) {
+ super(context, providerHelper, progressable);
+ }
+
+ @NonNull
+ @Override
+ public ConsolidateResult execute(ConsolidateInputParcel consolidateInputParcel,
+ CryptoInputParcel cryptoInputParcel) {
+ if (consolidateInputParcel.mConsolidateRecovery) {
+ return mProviderHelper.consolidateDatabaseStep2(mProgressable);
+ } else {
+ return mProviderHelper.consolidateDatabaseStep1(mProgressable);
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java
index 5ef04ab05..56bd3b786 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java
@@ -18,15 +18,19 @@
package org.sufficientlysecure.keychain.operations;
import android.content.Context;
+import android.support.annotation.NonNull;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.operations.results.DeleteResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
+import org.sufficientlysecure.keychain.service.DeleteKeyringParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
/** An operation which implements a high level keyring delete operation.
@@ -37,13 +41,24 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
* a list.
*
*/
-public class DeleteOperation extends BaseOperation {
+public class DeleteOperation extends BaseOperation<DeleteKeyringParcel> {
public DeleteOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
super(context, providerHelper, progressable);
}
- public DeleteResult execute(long[] masterKeyIds, boolean isSecret) {
+ @NonNull
+ @Override
+ public OperationResult execute(DeleteKeyringParcel deleteKeyringParcel,
+ CryptoInputParcel cryptoInputParcel) {
+
+ long[] masterKeyIds = deleteKeyringParcel.mMasterKeyIds;
+ boolean isSecret = deleteKeyringParcel.mIsSecret;
+
+ return onlyDeleteKey(masterKeyIds, isSecret);
+ }
+
+ private DeleteResult onlyDeleteKey(long[] masterKeyIds, boolean isSecret) {
OperationLog log = new OperationLog();
@@ -104,7 +119,6 @@ public class DeleteOperation extends BaseOperation {
}
return new DeleteResult(result, log, success, fail);
-
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java
index 4072d91c5..f5ba88502 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java
@@ -18,10 +18,12 @@
package org.sufficientlysecure.keychain.operations;
import android.content.Context;
+import android.support.annotation.NonNull;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.operations.results.ExportResult;
+import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
@@ -33,17 +35,20 @@ import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
+import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler;
+import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
-/** An operation which implements a high level key edit operation.
- *
+/**
+ * An operation which implements a high level key edit operation.
+ * <p/>
* This operation provides a higher level interface to the edit and
* create key operations in PgpKeyOperation. It takes care of fetching
* and saving the key before and after the operation.
@@ -51,14 +56,23 @@ import java.util.concurrent.atomic.AtomicBoolean;
* @see SaveKeyringParcel
*
*/
-public class EditKeyOperation extends BaseOperation {
+public class EditKeyOperation extends BaseOperation<SaveKeyringParcel> {
public EditKeyOperation(Context context, ProviderHelper providerHelper,
Progressable progressable, AtomicBoolean cancelled) {
super(context, providerHelper, progressable, cancelled);
}
- public OperationResult execute(SaveKeyringParcel saveParcel, CryptoInputParcel cryptoInput) {
+ /**
+ * Saves an edited key, and uploads it to a server atomically or otherwise as
+ * specified in saveParcel
+ *
+ * @param saveParcel primary input to the operation
+ * @param cryptoInput input that changes if user interaction is required
+ * @return the result of the operation
+ */
+ @NonNull
+ public InputPendingResult execute(SaveKeyringParcel saveParcel, CryptoInputParcel cryptoInput) {
OperationLog log = new OperationLog();
log.add(LogType.MSG_ED, 0);
@@ -119,6 +133,36 @@ public class EditKeyOperation extends BaseOperation {
// It's a success, so this must be non-null now
UncachedKeyRing ring = modifyResult.getRing();
+ if (saveParcel.isUpload()) {
+ UncachedKeyRing publicKeyRing;
+ try {
+ publicKeyRing = ring.extractPublicKeyRing();
+ } catch (IOException e) {
+ log.add(LogType.MSG_ED_ERROR_EXTRACTING_PUBLIC_UPLOAD, 1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ ExportKeyringParcel exportKeyringParcel =
+ new ExportKeyringParcel(saveParcel.getUploadKeyserver(),
+ publicKeyRing);
+
+ ExportResult uploadResult =
+ new ExportOperation(mContext, mProviderHelper, mProgressable)
+ .execute(exportKeyringParcel, cryptoInput);
+
+ if (uploadResult.isPending()) {
+ return uploadResult;
+ } else if (!uploadResult.success() && saveParcel.isUploadAtomic()) {
+ // if atomic, update fail implies edit operation should also fail and not save
+ log.add(uploadResult, 2);
+ return new EditKeyResult(log, RequiredInputParcel.createRetryUploadOperation(),
+ cryptoInput);
+ } else {
+ // upload succeeded or not atomic so we continue
+ log.add(uploadResult, 2);
+ }
+ }
+
// Save the new keyring.
SaveKeyringResult saveResult = mProviderHelper
.saveSecretKeyRing(ring, new ProgressScaler(mProgressable, 60, 95, 100));
@@ -130,15 +174,24 @@ public class EditKeyOperation extends BaseOperation {
}
// There is a new passphrase - cache it
- if (saveParcel.mNewUnlock != null) {
+ if (saveParcel.mNewUnlock != null && cryptoInput.mCachePassphrase) {
log.add(LogType.MSG_ED_CACHING_NEW, 1);
- PassphraseCacheService.addCachedPassphrase(mContext,
- ring.getMasterKeyId(),
- ring.getMasterKeyId(),
- saveParcel.mNewUnlock.mNewPassphrase != null
- ? saveParcel.mNewUnlock.mNewPassphrase
- : saveParcel.mNewUnlock.mNewPin,
- ring.getPublicKey().getPrimaryUserIdWithFallback());
+
+ // NOTE: Don't cache empty passphrases! Important for MOVE_KEY_TO_CARD
+ if (saveParcel.mNewUnlock.mNewPassphrase != null
+ && ( ! saveParcel.mNewUnlock.mNewPassphrase.isEmpty())) {
+ PassphraseCacheService.addCachedPassphrase(mContext,
+ ring.getMasterKeyId(),
+ ring.getMasterKeyId(),
+ saveParcel.mNewUnlock.mNewPassphrase,
+ ring.getPublicKey().getPrimaryUserIdWithFallback());
+ } else if (saveParcel.mNewUnlock.mNewPin != null) {
+ PassphraseCacheService.addCachedPassphrase(mContext,
+ ring.getMasterKeyId(),
+ ring.getMasterKeyId(),
+ saveParcel.mNewUnlock.mNewPin,
+ ring.getPublicKey().getPrimaryUserIdWithFallback());
+ }
}
updateProgress(R.string.progress_done, 100, 100);
@@ -150,5 +203,4 @@ public class EditKeyOperation extends BaseOperation {
return new EditKeyResult(EditKeyResult.RESULT_OK, log, ring.getMasterKeyId());
}
-
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java
new file mode 100644
index 000000000..531ac01f2
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.operations;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Proxy;
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+
+import org.spongycastle.bcpg.ArmoredOutputStream;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
+import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
+import org.sufficientlysecure.keychain.operations.results.ExportResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.FileHelper;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
+
+/**
+ * An operation class which implements high level export
+ * operations.
+ * This class receives a source and/or destination of keys as input and performs
+ * all steps for this export.
+ *
+ * @see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries()
+ * For the export operation, the input consists of a set of key ids and
+ * either the name of a file or an output uri to write to.
+ */
+public class ExportOperation extends BaseOperation<ExportKeyringParcel> {
+
+ public ExportOperation(Context context, ProviderHelper providerHelper, Progressable
+ progressable) {
+ super(context, providerHelper, progressable);
+ }
+
+ public ExportOperation(Context context, ProviderHelper providerHelper,
+ Progressable progressable, AtomicBoolean cancelled) {
+ super(context, providerHelper, progressable, cancelled);
+ }
+
+ public ExportResult uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring,
+ Proxy proxy) {
+ return uploadKeyRingToServer(server, keyring.getUncachedKeyRing(), proxy);
+ }
+
+ public ExportResult uploadKeyRingToServer(HkpKeyserver server, UncachedKeyRing keyring, Proxy proxy) {
+ mProgressable.setProgress(R.string.progress_uploading, 0, 1);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ArmoredOutputStream aos = null;
+ OperationLog log = new OperationLog();
+ log.add(LogType.MSG_EXPORT_UPLOAD_PUBLIC, 0, KeyFormattingUtils.convertKeyIdToHex(
+ keyring.getPublicKey().getKeyId()
+ ));
+
+ try {
+ aos = new ArmoredOutputStream(bos);
+ keyring.encode(aos);
+ aos.close();
+
+ String armoredKey = bos.toString("UTF-8");
+ server.add(armoredKey, proxy);
+
+ log.add(LogType.MSG_EXPORT_UPLOAD_SUCCESS, 1);
+ return new ExportResult(ExportResult.RESULT_OK, log);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException", e);
+
+ log.add(LogType.MSG_EXPORT_ERROR_KEY, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log);
+ } catch (AddKeyException e) {
+ Log.e(Constants.TAG, "AddKeyException", e);
+
+ log.add(LogType.MSG_EXPORT_ERROR_UPLOAD, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log);
+ } finally {
+ mProgressable.setProgress(R.string.progress_uploading, 1, 1);
+ try {
+ if (aos != null) {
+ aos.close();
+ }
+ bos.close();
+ } catch (IOException e) {
+ // this is just a finally thing, no matter if it doesn't work out.
+ }
+ }
+ }
+
+ public ExportResult exportToFile(long[] masterKeyIds, boolean exportSecret, String outputFile) {
+
+ OperationLog log = new OperationLog();
+ if (masterKeyIds != null) {
+ log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length);
+ } else {
+ log.add(LogType.MSG_EXPORT_ALL, 0);
+ }
+
+ // do we have a file name?
+ if (outputFile == null) {
+ log.add(LogType.MSG_EXPORT_ERROR_NO_FILE, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log);
+ }
+
+ log.add(LogType.MSG_EXPORT_FILE_NAME, 1, outputFile);
+
+ // check if storage is ready
+ if (!FileHelper.isStorageMounted(outputFile)) {
+ log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log);
+ }
+
+ try {
+ OutputStream outStream = new FileOutputStream(outputFile);
+ try {
+ ExportResult result = exportKeyRings(log, masterKeyIds, exportSecret, outStream);
+ if (result.cancelled()) {
+ //noinspection ResultOfMethodCallIgnored
+ new File(outputFile).delete();
+ }
+ return result;
+ } finally {
+ outStream.close();
+ }
+ } catch (IOException e) {
+ log.add(LogType.MSG_EXPORT_ERROR_FOPEN, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log);
+ }
+
+ }
+
+ public ExportResult exportToUri(long[] masterKeyIds, boolean exportSecret, Uri outputUri) {
+
+ OperationLog log = new OperationLog();
+ if (masterKeyIds != null) {
+ log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length);
+ } else {
+ log.add(LogType.MSG_EXPORT_ALL, 0);
+ }
+
+ // do we have a file name?
+ if (outputUri == null) {
+ log.add(LogType.MSG_EXPORT_ERROR_NO_URI, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log);
+ }
+
+ try {
+ OutputStream outStream = mProviderHelper.getContentResolver().openOutputStream
+ (outputUri);
+ return exportKeyRings(log, masterKeyIds, exportSecret, outStream);
+ } catch (FileNotFoundException e) {
+ log.add(LogType.MSG_EXPORT_ERROR_URI_OPEN, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log);
+ }
+
+ }
+
+ ExportResult exportKeyRings(OperationLog log, long[] masterKeyIds, boolean exportSecret,
+ OutputStream outStream) {
+
+ /* TODO isn't this checked above, with the isStorageMounted call?
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log);
+ }
+ */
+
+ if (!BufferedOutputStream.class.isInstance(outStream)) {
+ outStream = new BufferedOutputStream(outStream);
+ }
+
+ int okSecret = 0, okPublic = 0, progress = 0;
+
+ Cursor cursor = null;
+ try {
+
+ String selection = null, selectionArgs[] = null;
+
+ if (masterKeyIds != null) {
+ // convert long[] to String[]
+ selectionArgs = new String[masterKeyIds.length];
+ for (int i = 0; i < masterKeyIds.length; i++) {
+ selectionArgs[i] = Long.toString(masterKeyIds[i]);
+ }
+
+ // generates ?,?,? as placeholders for selectionArgs
+ String placeholders = TextUtils.join(",",
+ Collections.nCopies(masterKeyIds.length, "?"));
+
+ // put together selection string
+ selection = Tables.KEYS + "." + KeyRings.MASTER_KEY_ID
+ + " IN (" + placeholders + ")";
+ }
+
+ cursor = mProviderHelper.getContentResolver().query(
+ KeyRings.buildUnifiedKeyRingsUri(), new String[]{
+ KeyRings.MASTER_KEY_ID, KeyRings.PUBKEY_DATA,
+ KeyRings.PRIVKEY_DATA, KeyRings.HAS_ANY_SECRET
+ }, selection, selectionArgs, Tables.KEYS + "." + KeyRings.MASTER_KEY_ID
+ );
+
+ if (cursor == null || !cursor.moveToFirst()) {
+ log.add(LogType.MSG_EXPORT_ERROR_DB, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret);
+ }
+
+ int numKeys = cursor.getCount();
+
+ updateProgress(
+ mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
+ numKeys), 0, numKeys);
+
+ // For each public masterKey id
+ while (!cursor.isAfterLast()) {
+
+ long keyId = cursor.getLong(0);
+ ArmoredOutputStream arOutStream = null;
+
+ // Create an output stream
+ try {
+ arOutStream = new ArmoredOutputStream(outStream);
+
+ log.add(LogType.MSG_EXPORT_PUBLIC, 1, KeyFormattingUtils.beautifyKeyId(keyId));
+
+ byte[] data = cursor.getBlob(1);
+ CanonicalizedKeyRing ring =
+ UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true);
+ ring.encode(arOutStream);
+
+ okPublic += 1;
+ } catch (PgpGeneralException e) {
+ log.add(LogType.MSG_EXPORT_ERROR_KEY, 2);
+ updateProgress(progress++, numKeys);
+ continue;
+ } finally {
+ // make sure this is closed
+ if (arOutStream != null) {
+ arOutStream.close();
+ }
+ arOutStream = null;
+ }
+
+ if (exportSecret && cursor.getInt(3) > 0) {
+ try {
+ arOutStream = new ArmoredOutputStream(outStream);
+
+ // export secret key part
+ log.add(LogType.MSG_EXPORT_SECRET, 2, KeyFormattingUtils.beautifyKeyId
+ (keyId));
+ byte[] data = cursor.getBlob(2);
+ CanonicalizedKeyRing ring =
+ UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true);
+ ring.encode(arOutStream);
+
+ okSecret += 1;
+ } catch (PgpGeneralException e) {
+ log.add(LogType.MSG_EXPORT_ERROR_KEY, 2);
+ updateProgress(progress++, numKeys);
+ continue;
+ } finally {
+ // make sure this is closed
+ if (arOutStream != null) {
+ arOutStream.close();
+ }
+ }
+ }
+
+ updateProgress(progress++, numKeys);
+
+ cursor.moveToNext();
+ }
+
+ updateProgress(R.string.progress_done, numKeys, numKeys);
+
+ } catch (IOException e) {
+ log.add(LogType.MSG_EXPORT_ERROR_IO, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret);
+ } finally {
+ // Make sure the stream is closed
+ if (outStream != null) try {
+ outStream.close();
+ } catch (Exception e) {
+ Log.e(Constants.TAG, "error closing stream", e);
+ }
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+
+ log.add(LogType.MSG_EXPORT_SUCCESS, 1);
+ return new ExportResult(ExportResult.RESULT_OK, log, okPublic, okSecret);
+
+ }
+
+ @NonNull
+ public ExportResult execute(ExportKeyringParcel exportInput, CryptoInputParcel cryptoInput) {
+ switch (exportInput.mExportType) {
+ case UPLOAD_KEYSERVER: {
+ Proxy proxy;
+ if (cryptoInput.getParcelableProxy() == null) {
+ // explicit proxy not set
+ if (!OrbotHelper.isOrbotInRequiredState(mContext)) {
+ return new ExportResult(null,
+ RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput);
+ }
+ proxy = Preferences.getPreferences(mContext).getProxyPrefs()
+ .parcelableProxy.getProxy();
+ } else {
+ proxy = cryptoInput.getParcelableProxy().getProxy();
+ }
+
+ HkpKeyserver hkpKeyserver = new HkpKeyserver(exportInput.mKeyserver);
+ try {
+ if (exportInput.mCanonicalizedPublicKeyringUri != null) {
+ CanonicalizedPublicKeyRing keyring
+ = mProviderHelper.getCanonicalizedPublicKeyRing(
+ exportInput.mCanonicalizedPublicKeyringUri);
+ return uploadKeyRingToServer(hkpKeyserver, keyring, proxy);
+ } else {
+ return uploadKeyRingToServer(hkpKeyserver, exportInput.mUncachedKeyRing,
+ proxy);
+ }
+ } catch (ProviderHelper.NotFoundException e) {
+ Log.e(Constants.TAG, "error uploading key", e);
+ return new ExportResult(ExportResult.RESULT_ERROR, new OperationLog());
+ }
+ }
+ case EXPORT_FILE: {
+ return exportToFile(exportInput.mMasterKeyIds, exportInput.mExportSecret,
+ exportInput.mOutputFile);
+ }
+ case EXPORT_URI: {
+ return exportToUri(exportInput.mMasterKeyIds, exportInput.mExportSecret,
+ exportInput.mOutputUri);
+ }
+ default: { // can never happen, all enum types must be handled above
+ throw new AssertionError("must not happen, this is a bug!");
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java
deleted file mode 100644
index 86cfc21a3..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java
+++ /dev/null
@@ -1,583 +0,0 @@
-/*
- * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.operations;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-
-import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
-import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
-import org.sufficientlysecure.keychain.keyimport.Keyserver;
-import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
-import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
-import org.sufficientlysecure.keychain.operations.results.ExportResult;
-import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
-import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
-import org.sufficientlysecure.keychain.pgp.PgpHelper;
-import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-import org.sufficientlysecure.keychain.util.FileHelper;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.ParcelableFileCache;
-import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
-import org.sufficientlysecure.keychain.util.ProgressScaler;
-
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/** An operation class which implements high level import and export
- * operations.
- *
- * This class receives a source and/or destination of keys as input and performs
- * all steps for this import or export.
- *
- * For the import operation, the only valid source is an Iterator of
- * ParcelableKeyRing, each of which must contain either a single
- * keyring encoded as bytes, or a unique reference to a keyring
- * on keyservers and/or keybase.io.
- * It is important to note that public keys should generally be imported before
- * secret keys, because some implementations (notably Symantec PGP Desktop) do
- * not include self certificates for user ids in the secret keyring. The import
- * method here will generally import keyrings in the order given by the
- * iterator. so this should be ensured beforehand.
- * @see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries()
- *
- * For the export operation, the input consists of a set of key ids and
- * either the name of a file or an output uri to write to.
- *
- * TODO rework uploadKeyRingToServer
- *
- */
-public class ImportExportOperation extends BaseOperation {
-
- public ImportExportOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
- super(context, providerHelper, progressable);
- }
-
- public ImportExportOperation(Context context, ProviderHelper providerHelper,
- Progressable progressable, AtomicBoolean cancelled) {
- super(context, providerHelper, progressable, cancelled);
- }
-
- public void uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring) throws AddKeyException {
- uploadKeyRingToServer(server, keyring.getUncachedKeyRing());
- }
-
- public void uploadKeyRingToServer(HkpKeyserver server, UncachedKeyRing keyring) throws AddKeyException {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- ArmoredOutputStream aos = null;
- try {
- aos = new ArmoredOutputStream(bos);
- keyring.encode(aos);
- aos.close();
-
- String armoredKey = bos.toString("UTF-8");
- server.add(armoredKey);
- } catch (IOException e) {
- Log.e(Constants.TAG, "IOException", e);
- throw new AddKeyException();
- } finally {
- try {
- if (aos != null) {
- aos.close();
- }
- bos.close();
- } catch (IOException e) {
- // this is just a finally thing, no matter if it doesn't work out.
- }
- }
- }
-
- public ImportKeyResult importKeyRings(List<ParcelableKeyRing> entries, String keyServerUri) {
-
- Iterator<ParcelableKeyRing> it = entries.iterator();
- int numEntries = entries.size();
-
- return importKeyRings(it, numEntries, keyServerUri);
-
- }
-
- public ImportKeyResult importKeyRings(ParcelableFileCache<ParcelableKeyRing> cache, String keyServerUri) {
-
- // get entries from cached file
- try {
- IteratorWithSize<ParcelableKeyRing> it = cache.readCache();
- int numEntries = it.getSize();
-
- return importKeyRings(it, numEntries, keyServerUri);
- } catch (IOException e) {
-
- // Special treatment here, we need a lot
- OperationLog log = new OperationLog();
- log.add(LogType.MSG_IMPORT, 0, 0);
- log.add(LogType.MSG_IMPORT_ERROR_IO, 0, 0);
-
- return new ImportKeyResult(ImportKeyResult.RESULT_ERROR, log);
- }
-
- }
-
- public ImportKeyResult importKeyRings(Iterator<ParcelableKeyRing> entries, int num, String keyServerUri) {
- updateProgress(R.string.progress_importing, 0, 100);
-
- OperationLog log = new OperationLog();
- log.add(LogType.MSG_IMPORT, 0, num);
-
- // If there aren't even any keys, do nothing here.
- if (entries == null || !entries.hasNext()) {
- return new ImportKeyResult(ImportKeyResult.RESULT_FAIL_NOTHING, log);
- }
-
- int newKeys = 0, updatedKeys = 0, badKeys = 0, secret = 0;
- ArrayList<Long> importedMasterKeyIds = new ArrayList<>();
-
- boolean cancelled = false;
- int position = 0;
- double progSteps = 100.0 / num;
-
- KeybaseKeyserver keybaseServer = null;
- HkpKeyserver keyServer = null;
-
- // iterate over all entries
- while (entries.hasNext()) {
- ParcelableKeyRing entry = entries.next();
-
- // Has this action been cancelled? If so, don't proceed any further
- if (checkCancelled()) {
- cancelled = true;
- break;
- }
-
- try {
-
- UncachedKeyRing key = null;
-
- // If there is already byte data, use that
- if (entry.mBytes != null) {
- key = UncachedKeyRing.decodeFromData(entry.mBytes);
- }
- // Otherwise, we need to fetch the data from a server first
- else {
-
- // We fetch from keyservers first, because we tend to get more certificates
- // from there, so the number of certificates which are merged in later is smaller.
-
- // If we have a keyServerUri and a fingerprint or at least a keyId,
- // download from HKP
- if (keyServerUri != null
- && (entry.mKeyIdHex != null || entry.mExpectedFingerprint != null)) {
- // Make sure we have the keyserver instance cached
- if (keyServer == null) {
- log.add(LogType.MSG_IMPORT_KEYSERVER, 1, keyServerUri);
- keyServer = new HkpKeyserver(keyServerUri);
- }
-
- try {
- byte[] data;
- // Download by fingerprint, or keyId - whichever is available
- if (entry.mExpectedFingerprint != null) {
- log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, "0x" + entry.mExpectedFingerprint.substring(24));
- data = keyServer.get("0x" + entry.mExpectedFingerprint).getBytes();
- } else {
- log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, entry.mKeyIdHex);
- data = keyServer.get(entry.mKeyIdHex).getBytes();
- }
- key = UncachedKeyRing.decodeFromData(data);
- if (key != null) {
- log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER_OK, 3);
- } else {
- log.add(LogType.MSG_IMPORT_FETCH_ERROR_DECODE, 3);
- }
- } catch (Keyserver.QueryFailedException e) {
- Log.e(Constants.TAG, "query failed", e);
- log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER_ERROR, 3, e.getMessage());
- }
- }
-
- // If we have a keybase name, try to fetch from there
- if (entry.mKeybaseName != null) {
- // Make sure we have this cached
- if (keybaseServer == null) {
- keybaseServer = new KeybaseKeyserver();
- }
-
- try {
- log.add(LogType.MSG_IMPORT_FETCH_KEYBASE, 2, entry.mKeybaseName);
- byte[] data = keybaseServer.get(entry.mKeybaseName).getBytes();
- key = UncachedKeyRing.decodeFromData(data);
-
- // If there already is a key (of keybase origin), merge the two
- if (key != null) {
- log.add(LogType.MSG_IMPORT_MERGE, 3);
- UncachedKeyRing merged = UncachedKeyRing.decodeFromData(data);
- merged = key.merge(merged, log, 4);
- // If the merge didn't fail, use the new merged key
- if (merged != null) {
- key = merged;
- }
- } else {
- log.add(LogType.MSG_IMPORT_FETCH_ERROR_DECODE, 3);
- key = UncachedKeyRing.decodeFromData(data);
- }
- } catch (Keyserver.QueryFailedException e) {
- // download failed, too bad. just proceed
- Log.e(Constants.TAG, "query failed", e);
- log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER_ERROR, 3);
- }
- }
- }
-
- if (key == null) {
- log.add(LogType.MSG_IMPORT_FETCH_ERROR, 2);
- badKeys += 1;
- continue;
- }
-
- // If we have an expected fingerprint, make sure it matches
- if (entry.mExpectedFingerprint != null) {
- if (!key.containsSubkey(entry.mExpectedFingerprint)) {
- log.add(LogType.MSG_IMPORT_FINGERPRINT_ERROR, 2);
- badKeys += 1;
- continue;
- } else {
- log.add(LogType.MSG_IMPORT_FINGERPRINT_OK, 2);
- }
- }
-
- // Another check if we have been cancelled
- if (checkCancelled()) {
- cancelled = true;
- break;
- }
-
- SaveKeyringResult result;
- mProviderHelper.clearLog();
- if (key.isSecret()) {
- result = mProviderHelper.saveSecretKeyRing(key,
- new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100));
- } else {
- result = mProviderHelper.savePublicKeyRing(key,
- new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100));
- }
- if (!result.success()) {
- badKeys += 1;
- } else if (result.updated()) {
- updatedKeys += 1;
- importedMasterKeyIds.add(key.getMasterKeyId());
- } else {
- newKeys += 1;
- if (key.isSecret()) {
- secret += 1;
- }
- importedMasterKeyIds.add(key.getMasterKeyId());
- }
-
- log.add(result, 2);
-
- } catch (IOException | PgpGeneralException e) {
- Log.e(Constants.TAG, "Encountered bad key on import!", e);
- ++badKeys;
- }
- // update progress
- position++;
- }
-
- // Special: consolidate on secret key import (cannot be cancelled!)
- if (secret > 0) {
- setPreventCancel();
- ConsolidateResult result = mProviderHelper.consolidateDatabaseStep1(mProgressable);
- log.add(result, 1);
- }
-
- // Special: make sure new data is synced into contacts
- // disabling sync right now since it reduces speed while multi-threading
- // so, we expect calling functions to take care of it. KeychainIntentService handles this
- //ContactSyncAdapterService.requestSync();
-
- // convert to long array
- long[] importedMasterKeyIdsArray = new long[importedMasterKeyIds.size()];
- for (int i = 0; i < importedMasterKeyIds.size(); ++i) {
- importedMasterKeyIdsArray[i] = importedMasterKeyIds.get(i);
- }
-
- int resultType = 0;
- if (cancelled) {
- log.add(LogType.MSG_OPERATION_CANCELLED, 1);
- resultType |= ImportKeyResult.RESULT_CANCELLED;
- }
-
- // special return case: no new keys at all
- if (badKeys == 0 && newKeys == 0 && updatedKeys == 0) {
- resultType = ImportKeyResult.RESULT_FAIL_NOTHING;
- } else {
- if (newKeys > 0) {
- resultType |= ImportKeyResult.RESULT_OK_NEWKEYS;
- }
- if (updatedKeys > 0) {
- resultType |= ImportKeyResult.RESULT_OK_UPDATED;
- }
- if (badKeys > 0) {
- resultType |= ImportKeyResult.RESULT_WITH_ERRORS;
- if (newKeys == 0 && updatedKeys == 0) {
- resultType |= ImportKeyResult.RESULT_ERROR;
- }
- }
- if (log.containsWarnings()) {
- resultType |= ImportKeyResult.RESULT_WARNINGS;
- }
- }
-
- // Final log entry, it's easier to do this individually
- if ( (newKeys > 0 || updatedKeys > 0) && badKeys > 0) {
- log.add(LogType.MSG_IMPORT_PARTIAL, 1);
- } else if (newKeys > 0 || updatedKeys > 0) {
- log.add(LogType.MSG_IMPORT_SUCCESS, 1);
- } else {
- log.add(LogType.MSG_IMPORT_ERROR, 1);
- }
-
- ContactSyncAdapterService.requestSync();
-
- return new ImportKeyResult(resultType, log, newKeys, updatedKeys, badKeys, secret,
- importedMasterKeyIdsArray);
- }
-
- public ExportResult exportToFile(long[] masterKeyIds, boolean exportSecret, String outputFile) {
-
- OperationLog log = new OperationLog();
- if (masterKeyIds != null) {
- log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length);
- } else {
- log.add(LogType.MSG_EXPORT_ALL, 0);
- }
-
- // do we have a file name?
- if (outputFile == null) {
- log.add(LogType.MSG_EXPORT_ERROR_NO_FILE, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log);
- }
-
- // check if storage is ready
- if (!FileHelper.isStorageMounted(outputFile)) {
- log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log);
- }
-
- try {
- OutputStream outStream = new FileOutputStream(outputFile);
- ExportResult result = exportKeyRings(log, masterKeyIds, exportSecret, outStream);
- if (result.cancelled()) {
- //noinspection ResultOfMethodCallIgnored
- new File(outputFile).delete();
- }
- return result;
- } catch (FileNotFoundException e) {
- log.add(LogType.MSG_EXPORT_ERROR_FOPEN, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log);
- }
-
- }
-
- public ExportResult exportToUri(long[] masterKeyIds, boolean exportSecret, Uri outputUri) {
-
- OperationLog log = new OperationLog();
- if (masterKeyIds != null) {
- log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length);
- } else {
- log.add(LogType.MSG_EXPORT_ALL, 0);
- }
-
- // do we have a file name?
- if (outputUri == null) {
- log.add(LogType.MSG_EXPORT_ERROR_NO_URI, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log);
- }
-
- try {
- OutputStream outStream = mProviderHelper.getContentResolver().openOutputStream(outputUri);
- return exportKeyRings(log, masterKeyIds, exportSecret, outStream);
- } catch (FileNotFoundException e) {
- log.add(LogType.MSG_EXPORT_ERROR_URI_OPEN, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log);
- }
-
- }
-
- ExportResult exportKeyRings(OperationLog log, long[] masterKeyIds, boolean exportSecret,
- OutputStream outStream) {
-
- /* TODO isn't this checked above, with the isStorageMounted call?
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log);
- }
- */
-
- if ( ! BufferedOutputStream.class.isInstance(outStream)) {
- outStream = new BufferedOutputStream(outStream);
- }
-
- int okSecret = 0, okPublic = 0, progress = 0;
-
- Cursor cursor = null;
- try {
-
- String selection = null, ids[] = null;
-
- if (masterKeyIds != null) {
- // generate placeholders and string selection args
- ids = new String[masterKeyIds.length];
- StringBuilder placeholders = new StringBuilder("?");
- for (int i = 0; i < masterKeyIds.length; i++) {
- ids[i] = Long.toString(masterKeyIds[i]);
- if (i != 0) {
- placeholders.append(",?");
- }
- }
-
- // put together selection string
- selection = Tables.KEY_RINGS_PUBLIC + "." + KeyRings.MASTER_KEY_ID
- + " IN (" + placeholders + ")";
- }
-
- cursor = mProviderHelper.getContentResolver().query(
- KeyRings.buildUnifiedKeyRingsUri(), new String[]{
- KeyRings.MASTER_KEY_ID, KeyRings.PUBKEY_DATA,
- KeyRings.PRIVKEY_DATA, KeyRings.HAS_ANY_SECRET
- }, selection, ids, Tables.KEYS + "." + KeyRings.MASTER_KEY_ID
- );
-
- if (cursor == null || !cursor.moveToFirst()) {
- log.add(LogType.MSG_EXPORT_ERROR_DB, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret);
- }
-
- int numKeys = cursor.getCount();
-
- updateProgress(
- mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
- numKeys), 0, numKeys);
-
- // For each public masterKey id
- while (!cursor.isAfterLast()) {
-
- long keyId = cursor.getLong(0);
- ArmoredOutputStream arOutStream = null;
-
- // Create an output stream
- try {
- arOutStream = new ArmoredOutputStream(outStream);
-
- log.add(LogType.MSG_EXPORT_PUBLIC, 1, KeyFormattingUtils.beautifyKeyId(keyId));
-
- byte[] data = cursor.getBlob(1);
- CanonicalizedKeyRing ring =
- UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true);
- ring.encode(arOutStream);
-
- okPublic += 1;
- } catch (PgpGeneralException e) {
- log.add(LogType.MSG_EXPORT_ERROR_KEY, 2);
- updateProgress(progress++, numKeys);
- continue;
- } finally {
- // make sure this is closed
- if (arOutStream != null) {
- arOutStream.close();
- }
- arOutStream = null;
- }
-
- if (exportSecret && cursor.getInt(3) > 0) {
- try {
- arOutStream = new ArmoredOutputStream(outStream);
-
- // export secret key part
- log.add(LogType.MSG_EXPORT_SECRET, 2, KeyFormattingUtils.beautifyKeyId(keyId));
- byte[] data = cursor.getBlob(2);
- CanonicalizedKeyRing ring =
- UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true);
- ring.encode(arOutStream);
-
- okSecret += 1;
- } catch (PgpGeneralException e) {
- log.add(LogType.MSG_EXPORT_ERROR_KEY, 2);
- updateProgress(progress++, numKeys);
- continue;
- } finally {
- // make sure this is closed
- if (arOutStream != null) {
- arOutStream.close();
- }
- }
- }
-
- updateProgress(progress++, numKeys);
-
- cursor.moveToNext();
- }
-
- updateProgress(R.string.progress_done, numKeys, numKeys);
-
- } catch (IOException e) {
- log.add(LogType.MSG_EXPORT_ERROR_IO, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret);
- } finally {
- // Make sure the stream is closed
- if (outStream != null) try {
- outStream.close();
- } catch (Exception e) {
- Log.e(Constants.TAG, "error closing stream", e);
- }
- if (cursor != null) {
- cursor.close();
- }
- }
-
-
- log.add(LogType.MSG_EXPORT_SUCCESS, 1);
- return new ExportResult(ExportResult.RESULT_OK, log, okPublic, okSecret);
-
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java
new file mode 100644
index 000000000..7b224fe8e
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.operations;
+
+
+import java.io.IOException;
+import java.net.Proxy;
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
+import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
+import org.sufficientlysecure.keychain.keyimport.Keyserver;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
+import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
+import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ParcelableFileCache;
+import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
+import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
+
+/**
+ * An operation class which implements high level import
+ * operations.
+ * This class receives a source and/or destination of keys as input and performs
+ * all steps for this import.
+ * For the import operation, the only valid source is an Iterator of
+ * ParcelableKeyRing, each of which must contain either a single
+ * keyring encoded as bytes, or a unique reference to a keyring
+ * on keyservers and/or keybase.io.
+ * It is important to note that public keys should generally be imported before
+ * secret keys, because some implementations (notably Symantec PGP Desktop) do
+ * not include self certificates for user ids in the secret keyring. The import
+ * method here will generally import keyrings in the order given by the
+ * iterator, so this should be ensured beforehand.
+ *
+ * @see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries()
+ */
+public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
+
+ public ImportOperation(Context context, ProviderHelper providerHelper, Progressable
+ progressable) {
+ super(context, providerHelper, progressable);
+ }
+
+ public ImportOperation(Context context, ProviderHelper providerHelper,
+ Progressable progressable, AtomicBoolean cancelled) {
+ super(context, providerHelper, progressable, cancelled);
+ }
+
+ // Overloaded functions for using progressable supplied in constructor during import
+ public ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
+ String keyServerUri, Proxy proxy) {
+ return serialKeyRingImport(entries, num, keyServerUri, mProgressable, proxy);
+ }
+
+ @NonNull
+ private ImportKeyResult serialKeyRingImport(ParcelableFileCache<ParcelableKeyRing> cache,
+ String keyServerUri, Proxy proxy) {
+
+ // get entries from cached file
+ try {
+ IteratorWithSize<ParcelableKeyRing> it = cache.readCache();
+ int numEntries = it.getSize();
+
+ return serialKeyRingImport(it, numEntries, keyServerUri, mProgressable, proxy);
+ } catch (IOException e) {
+
+ // Special treatment here, we need a lot
+ OperationLog log = new OperationLog();
+ log.add(LogType.MSG_IMPORT, 0, 0);
+ log.add(LogType.MSG_IMPORT_ERROR_IO, 0, 0);
+
+ return new ImportKeyResult(ImportKeyResult.RESULT_ERROR, log);
+ }
+
+ }
+
+ /**
+ * Since the introduction of multithreaded import, we expect calling functions to handle the
+ * contact-to-key sync i.e ContactSyncAdapterService.requestSync()
+ *
+ * @param entries keys to import
+ * @param num number of keys to import
+ * @param keyServerUri contains uri of keyserver to import from, if it is an import from cloud
+ * @param progressable Allows multi-threaded import to supply a progressable that ignores the
+ * progress of a single key being imported
+ */
+ @NonNull
+ private ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
+ String keyServerUri, Progressable progressable,
+ Proxy proxy) {
+ if (progressable != null) {
+ progressable.setProgress(R.string.progress_importing, 0, 100);
+ }
+
+ OperationLog log = new OperationLog();
+ log.add(LogType.MSG_IMPORT, 0, num);
+
+ // If there aren't even any keys, do nothing here.
+ if (entries == null || !entries.hasNext()) {
+ return new ImportKeyResult(ImportKeyResult.RESULT_FAIL_NOTHING, log);
+ }
+
+ int newKeys = 0, updatedKeys = 0, badKeys = 0, secret = 0;
+ ArrayList<Long> importedMasterKeyIds = new ArrayList<>();
+
+ boolean cancelled = false;
+ int position = 0;
+ double progSteps = 100.0 / num;
+
+ KeybaseKeyserver keybaseServer = null;
+ HkpKeyserver keyServer = null;
+
+ // iterate over all entries
+ while (entries.hasNext()) {
+ ParcelableKeyRing entry = entries.next();
+
+ // Has this action been cancelled? If so, don't proceed any further
+ if (checkCancelled()) {
+ cancelled = true;
+ break;
+ }
+
+ try {
+
+ UncachedKeyRing key = null;
+
+ // If there is already byte data, use that
+ if (entry.mBytes != null) {
+ key = UncachedKeyRing.decodeFromData(entry.mBytes);
+ }
+ // Otherwise, we need to fetch the data from a server first
+ else {
+
+ // We fetch from keyservers first, because we tend to get more certificates
+ // from there, so the number of certificates which are merged in later is
+ // smaller.
+
+ // If we have a keyServerUri and a fingerprint or at least a keyId,
+ // download from HKP
+ if (keyServerUri != null
+ && (entry.mKeyIdHex != null || entry.mExpectedFingerprint != null)) {
+ // Make sure we have the keyserver instance cached
+ if (keyServer == null) {
+ log.add(LogType.MSG_IMPORT_KEYSERVER, 1, keyServerUri);
+ keyServer = new HkpKeyserver(keyServerUri);
+ }
+
+ try {
+ byte[] data;
+ // Download by fingerprint, or keyId - whichever is available
+ if (entry.mExpectedFingerprint != null) {
+ log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, "0x" +
+ entry.mExpectedFingerprint.substring(24));
+ data = keyServer.get("0x" + entry.mExpectedFingerprint, proxy)
+ .getBytes();
+ } else {
+ log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, entry.mKeyIdHex);
+ data = keyServer.get(entry.mKeyIdHex, proxy).getBytes();
+ }
+ key = UncachedKeyRing.decodeFromData(data);
+ if (key != null) {
+ log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER_OK, 3);
+ } else {
+ log.add(LogType.MSG_IMPORT_FETCH_ERROR_DECODE, 3);
+ }
+ } catch (Keyserver.QueryFailedException e) {
+ Log.d(Constants.TAG, "query failed", e);
+ log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER_ERROR, 3, e.getMessage());
+ }
+ }
+
+ // If we have a keybase name, try to fetch from there
+ if (entry.mKeybaseName != null) {
+ // Make sure we have this cached
+ if (keybaseServer == null) {
+ keybaseServer = new KeybaseKeyserver();
+ }
+
+ try {
+ log.add(LogType.MSG_IMPORT_FETCH_KEYBASE, 2, entry.mKeybaseName);
+ byte[] data = keybaseServer.get(entry.mKeybaseName, proxy).getBytes();
+ UncachedKeyRing keybaseKey = UncachedKeyRing.decodeFromData(data);
+
+ // If there already is a key, merge the two
+ if (key != null && keybaseKey != null) {
+ log.add(LogType.MSG_IMPORT_MERGE, 3);
+ keybaseKey = key.merge(keybaseKey, log, 4);
+ // If the merge didn't fail, use the new merged key
+ if (keybaseKey != null) {
+ key = keybaseKey;
+ } else {
+ log.add(LogType.MSG_IMPORT_MERGE_ERROR, 4);
+ }
+ } else if (keybaseKey != null) {
+ key = keybaseKey;
+ }
+ } catch (Keyserver.QueryFailedException e) {
+ // download failed, too bad. just proceed
+ Log.e(Constants.TAG, "query failed", e);
+ log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER_ERROR, 3, e.getMessage());
+ }
+ }
+ }
+
+ if (key == null) {
+ log.add(LogType.MSG_IMPORT_FETCH_ERROR, 2);
+ badKeys += 1;
+ continue;
+ }
+
+ // If we have an expected fingerprint, make sure it matches
+ if (entry.mExpectedFingerprint != null) {
+ if (!key.containsSubkey(entry.mExpectedFingerprint)) {
+ log.add(LogType.MSG_IMPORT_FINGERPRINT_ERROR, 2);
+ badKeys += 1;
+ continue;
+ } else {
+ log.add(LogType.MSG_IMPORT_FINGERPRINT_OK, 2);
+ }
+ }
+
+ // Another check if we have been cancelled
+ if (checkCancelled()) {
+ cancelled = true;
+ break;
+ }
+
+ SaveKeyringResult result;
+ // synchronizing prevents https://github.com/open-keychain/open-keychain/issues/1221
+ // and https://github.com/open-keychain/open-keychain/issues/1480
+ synchronized (mProviderHelper) {
+ mProviderHelper.clearLog();
+ if (key.isSecret()) {
+ result = mProviderHelper.saveSecretKeyRing(key,
+ new ProgressScaler(progressable, (int) (position * progSteps),
+ (int) ((position + 1) * progSteps), 100));
+ } else {
+ result = mProviderHelper.savePublicKeyRing(key,
+ new ProgressScaler(progressable, (int) (position * progSteps),
+ (int) ((position + 1) * progSteps), 100));
+ }
+ }
+ if (!result.success()) {
+ badKeys += 1;
+ } else {
+ if (result.updated()) {
+ updatedKeys += 1;
+ importedMasterKeyIds.add(key.getMasterKeyId());
+ } else {
+ newKeys += 1;
+ if (key.isSecret()) {
+ secret += 1;
+ }
+ importedMasterKeyIds.add(key.getMasterKeyId());
+ }
+ if (entry.mBytes == null) {
+ // synonymous to isDownloadFromKeyserver.
+ // If no byte data was supplied, import from keyserver took place
+ // this prevents file imports being noted as keyserver imports
+ mProviderHelper.renewKeyLastUpdatedTime(key.getMasterKeyId(),
+ GregorianCalendar.getInstance().getTimeInMillis(),
+ TimeUnit.MILLISECONDS);
+ }
+ }
+
+ log.add(result, 2);
+ } catch (IOException | PgpGeneralException e) {
+ Log.e(Constants.TAG, "Encountered bad key on import!", e);
+ ++badKeys;
+ }
+ // update progress
+ position++;
+ }
+
+ // Special: consolidate on secret key import (cannot be cancelled!)
+ // synchronized on mProviderHelper to prevent
+ // https://github.com/open-keychain/open-keychain/issues/1221 since a consolidate deletes
+ // and re-inserts keys, which could conflict with a parallel db key update
+ if (secret > 0) {
+ setPreventCancel();
+ ConsolidateResult result;
+ synchronized (mProviderHelper) {
+ result = mProviderHelper.consolidateDatabaseStep1(progressable);
+ }
+ log.add(result, 1);
+ }
+
+ // Special: make sure new data is synced into contacts
+ // disabling sync right now since it reduces speed while multi-threading
+ // so, we expect calling functions to take care of it. KeychainService handles this
+ // ContactSyncAdapterService.requestSync();
+
+ // convert to long array
+ long[] importedMasterKeyIdsArray = new long[importedMasterKeyIds.size()];
+ for (int i = 0; i < importedMasterKeyIds.size(); ++i) {
+ importedMasterKeyIdsArray[i] = importedMasterKeyIds.get(i);
+ }
+
+ int resultType = 0;
+ if (cancelled) {
+ log.add(LogType.MSG_OPERATION_CANCELLED, 1);
+ resultType |= ImportKeyResult.RESULT_CANCELLED;
+ }
+
+ // special return case: no new keys at all
+ if (badKeys == 0 && newKeys == 0 && updatedKeys == 0) {
+ resultType = ImportKeyResult.RESULT_FAIL_NOTHING;
+ } else {
+ if (newKeys > 0) {
+ resultType |= ImportKeyResult.RESULT_OK_NEWKEYS;
+ }
+ if (updatedKeys > 0) {
+ resultType |= ImportKeyResult.RESULT_OK_UPDATED;
+ }
+ if (badKeys > 0) {
+ resultType |= ImportKeyResult.RESULT_WITH_ERRORS;
+ if (newKeys == 0 && updatedKeys == 0) {
+ resultType |= ImportKeyResult.RESULT_ERROR;
+ }
+ }
+ if (log.containsWarnings()) {
+ resultType |= ImportKeyResult.RESULT_WARNINGS;
+ }
+ }
+
+ // Final log entry, it's easier to do this individually
+ if ((newKeys > 0 || updatedKeys > 0) && badKeys > 0) {
+ log.add(LogType.MSG_IMPORT_PARTIAL, 1);
+ } else if (newKeys > 0 || updatedKeys > 0) {
+ log.add(LogType.MSG_IMPORT_SUCCESS, 1);
+ } else {
+ log.add(LogType.MSG_IMPORT_ERROR, 1);
+ }
+
+ return new ImportKeyResult(resultType, log, newKeys, updatedKeys, badKeys, secret,
+ importedMasterKeyIdsArray);
+ }
+
+ @NonNull
+ @Override
+ public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) {
+ ArrayList<ParcelableKeyRing> keyList = importInput.mKeyList;
+ String keyServer = importInput.mKeyserver;
+
+ ImportKeyResult result;
+
+ if (keyList == null) {// import from file, do serially
+ ParcelableFileCache<ParcelableKeyRing> cache = new ParcelableFileCache<>(mContext,
+ "key_import.pcl");
+
+ result = serialKeyRingImport(cache, null, null);
+ } else {
+ Proxy proxy;
+ if (cryptoInput.getParcelableProxy() == null) {
+ // explicit proxy not set
+ if(!OrbotHelper.isOrbotInRequiredState(mContext)) {
+ // show dialog to enable/install dialog
+ return new ImportKeyResult(null,
+ RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput);
+ }
+ proxy = Preferences.getPreferences(mContext).getProxyPrefs().parcelableProxy
+ .getProxy();
+ } else {
+ proxy = cryptoInput.getParcelableProxy().getProxy();
+ }
+
+ result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer, proxy);
+ }
+
+ ContactSyncAdapterService.requestSync();
+ return result;
+ }
+
+ @NonNull
+ private ImportKeyResult multiThreadedKeyImport(Iterator<ParcelableKeyRing> keyListIterator,
+ int totKeys, final String keyServer,
+ final Proxy proxy) {
+ Log.d(Constants.TAG, "Multi-threaded key import starting");
+ if (keyListIterator != null) {
+ KeyImportAccumulator accumulator = new KeyImportAccumulator(totKeys, mProgressable);
+
+ final ProgressScaler ignoreProgressable = new ProgressScaler();
+
+ final int maxThreads = 200;
+ ExecutorService importExecutor = new ThreadPoolExecutor(0, maxThreads,
+ 30L, TimeUnit.SECONDS,
+ new SynchronousQueue<Runnable>());
+
+ ExecutorCompletionService<ImportKeyResult> importCompletionService =
+ new ExecutorCompletionService<>(importExecutor);
+
+ while (keyListIterator.hasNext()) { // submit all key rings to be imported
+
+ final ParcelableKeyRing pkRing = keyListIterator.next();
+
+ Callable<ImportKeyResult> importOperationCallable = new Callable<ImportKeyResult>
+ () {
+
+ @Override
+ public ImportKeyResult call() {
+
+ ArrayList<ParcelableKeyRing> list = new ArrayList<>();
+ list.add(pkRing);
+
+ return serialKeyRingImport(list.iterator(), 1, keyServer,
+ ignoreProgressable, proxy);
+ }
+ };
+
+ importCompletionService.submit(importOperationCallable);
+ }
+
+ while (!accumulator.isImportFinished()) { // accumulate the results of each import
+ try {
+ accumulator.accumulateKeyImport(importCompletionService.take().get());
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(Constants.TAG, "A key could not be imported during multi-threaded " +
+ "import", e);
+ // do nothing?
+ if (e instanceof ExecutionException) {
+ // Since serialKeyRingImport does not throw any exceptions, this is what
+ // would have happened if
+ // we were importing the key on this thread
+ throw new RuntimeException();
+ }
+ }
+ }
+ return accumulator.getConsolidatedResult();
+ }
+ return new ImportKeyResult(ImportKeyResult.RESULT_FAIL_NOTHING, new OperationLog());
+ }
+
+ /**
+ * Used to accumulate the results of individual key imports
+ */
+ public static class KeyImportAccumulator {
+ private OperationResult.OperationLog mImportLog = new OperationResult.OperationLog();
+ Progressable mProgressable;
+ private int mTotalKeys;
+ private int mImportedKeys = 0;
+ ArrayList<Long> mImportedMasterKeyIds = new ArrayList<>();
+ private int mBadKeys = 0;
+ private int mNewKeys = 0;
+ private int mUpdatedKeys = 0;
+ private int mSecret = 0;
+ private int mResultType = 0;
+
+ /**
+ * Accumulates keyring imports and updates the progressable whenever a new key is imported.
+ * Also sets the progress to 0 on instantiation.
+ *
+ * @param totalKeys total number of keys to be imported
+ * @param externalProgressable the external progressable to be updated every time a key
+ * is imported
+ */
+ public KeyImportAccumulator(int totalKeys, Progressable externalProgressable) {
+ mTotalKeys = totalKeys;
+ mProgressable = externalProgressable;
+ if (mProgressable != null) {
+ mProgressable.setProgress(0, totalKeys);
+ }
+ }
+
+ public synchronized void accumulateKeyImport(ImportKeyResult result) {
+ mImportedKeys++;
+
+ if (mProgressable != null) {
+ mProgressable.setProgress(mImportedKeys, mTotalKeys);
+ }
+
+ mImportLog.addAll(result.getLog().toList());//accumulates log
+ mBadKeys += result.mBadKeys;
+ mNewKeys += result.mNewKeys;
+ mUpdatedKeys += result.mUpdatedKeys;
+ mSecret += result.mSecret;
+
+ long[] masterKeyIds = result.getImportedMasterKeyIds();
+ for (long masterKeyId : masterKeyIds) {
+ mImportedMasterKeyIds.add(masterKeyId);
+ }
+
+ // if any key import has been cancelled, set result type to cancelled
+ // resultType is added to in getConsolidatedKayImport to account for remaining factors
+ mResultType |= result.getResult() & ImportKeyResult.RESULT_CANCELLED;
+ }
+
+ /**
+ * returns accumulated result of all imports so far
+ */
+ public ImportKeyResult getConsolidatedResult() {
+
+ // adding required information to mResultType
+ // special case,no keys requested for import
+ if (mBadKeys == 0 && mNewKeys == 0 && mUpdatedKeys == 0) {
+ mResultType = ImportKeyResult.RESULT_FAIL_NOTHING;
+ } else {
+ if (mNewKeys > 0) {
+ mResultType |= ImportKeyResult.RESULT_OK_NEWKEYS;
+ }
+ if (mUpdatedKeys > 0) {
+ mResultType |= ImportKeyResult.RESULT_OK_UPDATED;
+ }
+ if (mBadKeys > 0) {
+ mResultType |= ImportKeyResult.RESULT_WITH_ERRORS;
+ if (mNewKeys == 0 && mUpdatedKeys == 0) {
+ mResultType |= ImportKeyResult.RESULT_ERROR;
+ }
+ }
+ if (mImportLog.containsWarnings()) {
+ mResultType |= ImportKeyResult.RESULT_WARNINGS;
+ }
+ }
+
+ long masterKeyIds[] = new long[mImportedMasterKeyIds.size()];
+ for (int i = 0; i < masterKeyIds.length; i++) {
+ masterKeyIds[i] = mImportedMasterKeyIds.get(i);
+ }
+
+ return new ImportKeyResult(mResultType, mImportLog, mNewKeys, mUpdatedKeys, mBadKeys,
+ mSecret, masterKeyIds);
+ }
+
+ public boolean isImportFinished() {
+ return mTotalKeys == mImportedKeys;
+ }
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java
new file mode 100644
index 000000000..8f1abde83
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.operations;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.Proxy;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+
+import com.textuality.keybase.lib.Proof;
+import com.textuality.keybase.lib.prover.Prover;
+import de.measite.minidns.Client;
+import de.measite.minidns.DNSMessage;
+import de.measite.minidns.Question;
+import de.measite.minidns.Record;
+import de.measite.minidns.record.Data;
+import de.measite.minidns.record.TXT;
+import org.json.JSONObject;
+import org.spongycastle.openpgp.PGPUtil;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
+import org.sufficientlysecure.keychain.operations.results.KeybaseVerificationResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
+import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
+import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
+
+public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificationParcel> {
+
+ public KeybaseVerificationOperation(Context context, ProviderHelper providerHelper,
+ Progressable progressable) {
+ super(context, providerHelper, progressable);
+ }
+
+ @NonNull
+ @Override
+ public KeybaseVerificationResult execute(KeybaseVerificationParcel keybaseInput,
+ CryptoInputParcel cryptoInput) {
+ Proxy proxy;
+ if (cryptoInput.getParcelableProxy() == null) {
+ // explicit proxy not set
+ if (!OrbotHelper.isOrbotInRequiredState(mContext)) {
+ return new KeybaseVerificationResult(null,
+ RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput);
+ }
+ proxy = Preferences.getPreferences(mContext).getProxyPrefs()
+ .parcelableProxy.getProxy();
+ } else {
+ proxy = cryptoInput.getParcelableProxy().getProxy();
+ }
+
+ String requiredFingerprint = keybaseInput.mRequiredFingerprint;
+
+ OperationResult.OperationLog log = new OperationResult.OperationLog();
+ log.add(OperationResult.LogType.MSG_KEYBASE_VERIFICATION, 0, requiredFingerprint);
+
+ try {
+ String keybaseProof = keybaseInput.mKeybaseProof;
+ Proof proof = new Proof(new JSONObject(keybaseProof));
+ mProgressable.setProgress(R.string.keybase_message_fetching_data, 0, 100);
+
+ Prover prover = Prover.findProverFor(proof);
+
+ if (prover == null) {
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_NO_PROVER, 1,
+ proof.getPrettyName());
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+
+ if (!prover.fetchProofData(proxy)) {
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_FETCH_PROOF, 1);
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+
+ if (!prover.checkFingerprint(requiredFingerprint)) {
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_FINGERPRINT_MISMATCH, 1);
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+
+ String domain = prover.dnsTxtCheckRequired();
+ if (domain != null) {
+ DNSMessage dnsQuery = new Client().query(new Question(domain, Record.TYPE.TXT));
+ if (dnsQuery == null) {
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_DNS_FAIL, 1);
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 2,
+ getFlattenedProverLog(prover));
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+ Record[] records = dnsQuery.getAnswers();
+ List<List<byte[]>> extents = new ArrayList<>();
+ for (Record r : records) {
+ Data d = r.getPayload();
+ if (d instanceof TXT) {
+ extents.add(((TXT) d).getExtents());
+ }
+ }
+ if (!prover.checkDnsTxt(extents)) {
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 1,
+ getFlattenedProverLog(prover));
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+ }
+
+ byte[] messageBytes = prover.getPgpMessage().getBytes();
+ if (prover.rawMessageCheckRequired()) {
+ InputStream messageByteStream = PGPUtil.getDecoderStream(new
+ ByteArrayInputStream
+ (messageBytes));
+ if (!prover.checkRawMessageBytes(messageByteStream)) {
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 1,
+ getFlattenedProverLog(prover));
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+ }
+
+ PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(mContext, mProviderHelper, mProgressable);
+
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(messageBytes)
+ .setSignedLiteralData(true)
+ .setRequiredSignerFingerprint(requiredFingerprint);
+
+ DecryptVerifyResult decryptVerifyResult = op.execute(input, new CryptoInputParcel());
+
+ if (!decryptVerifyResult.success()) {
+ log.add(decryptVerifyResult, 1);
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+
+ if (!prover.validate(new String(decryptVerifyResult.getOutputBytes()))) {
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_PAYLOAD_MISMATCH, 1);
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+
+ return new KeybaseVerificationResult(OperationResult.RESULT_OK, log, prover);
+ } catch (Exception e) {
+ // just adds the passed parameter, in this case e.getMessage()
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 1, e.getMessage());
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+ }
+
+ private String getFlattenedProverLog(Prover prover) {
+ String log = "";
+ for (String line : prover.getLog()) {
+ log += line + "\n";
+ }
+ return log;
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java
index ef08b0b77..2f25b6926 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java
@@ -17,7 +17,11 @@
package org.sufficientlysecure.keychain.operations;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
import android.content.Context;
+import android.support.annotation.NonNull;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
@@ -25,17 +29,17 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
+import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ProgressScaler;
-import java.util.concurrent.atomic.AtomicBoolean;
-
/** An operation which promotes a public key ring to a secret one.
*
* This operation can only be applied to public key rings where no secret key
@@ -43,14 +47,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
* without secret key material, using a GNU_DUMMY s2k type.
*
*/
-public class PromoteKeyOperation extends BaseOperation {
+public class PromoteKeyOperation extends BaseOperation<PromoteKeyringParcel> {
public PromoteKeyOperation(Context context, ProviderHelper providerHelper,
Progressable progressable, AtomicBoolean cancelled) {
super(context, providerHelper, progressable, cancelled);
}
- public PromoteKeyResult execute(long masterKeyId, byte[] cardAid) {
+ @NonNull
+ @Override
+ public PromoteKeyResult execute(PromoteKeyringParcel promoteKeyringParcel,
+ CryptoInputParcel cryptoInputParcel) {
OperationLog log = new OperationLog();
log.add(LogType.MSG_PR, 0);
@@ -61,12 +68,29 @@ public class PromoteKeyOperation extends BaseOperation {
try {
log.add(LogType.MSG_PR_FETCHING, 1,
- KeyFormattingUtils.convertKeyIdToHex(masterKeyId));
+ KeyFormattingUtils.convertKeyIdToHex(promoteKeyringParcel.mKeyRingId));
CanonicalizedPublicKeyRing pubRing =
- mProviderHelper.getCanonicalizedPublicKeyRing(masterKeyId);
+ mProviderHelper.getCanonicalizedPublicKeyRing(promoteKeyringParcel.mKeyRingId);
+
+ if (promoteKeyringParcel.mSubKeyIds == null) {
+ log.add(LogType.MSG_PR_ALL, 1);
+ } else {
+ // sort for binary search
+ for (CanonicalizedPublicKey key : pubRing.publicKeyIterator()) {
+ long subKeyId = key.getKeyId();
+ if (naiveIndexOf(promoteKeyringParcel.mSubKeyIds, subKeyId) != null) {
+ log.add(LogType.MSG_PR_SUBKEY_MATCH, 1,
+ KeyFormattingUtils.convertKeyIdToHex(subKeyId));
+ } else {
+ log.add(LogType.MSG_PR_SUBKEY_NOMATCH, 1,
+ KeyFormattingUtils.convertKeyIdToHex(subKeyId));
+ }
+ }
+ }
// create divert-to-card secret key from public key
- promotedRing = pubRing.createDivertSecretRing(cardAid);
+ promotedRing = pubRing.createDivertSecretRing(promoteKeyringParcel.mCardAid,
+ promoteKeyringParcel.mSubKeyIds);
} catch (NotFoundException e) {
log.add(LogType.MSG_PR_ERROR_KEY_NOT_FOUND, 2);
@@ -106,4 +130,13 @@ public class PromoteKeyOperation extends BaseOperation {
}
+ static private Integer naiveIndexOf(long[] haystack, long needle) {
+ for (int i = 0; i < haystack.length; i++) {
+ if (needle == haystack[i]) {
+ return i;
+ }
+ }
+ return null;
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java
new file mode 100644
index 000000000..975cf541a
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.operations;
+
+import android.content.Context;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.operations.results.RevokeResult;
+import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
+import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.RevokeKeyringParcel;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class RevokeOperation extends BaseOperation<RevokeKeyringParcel> {
+
+ public RevokeOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
+ super(context, providerHelper, progressable);
+ }
+
+ @NonNull
+ @Override
+ public OperationResult execute(RevokeKeyringParcel revokeKeyringParcel,
+ CryptoInputParcel cryptoInputParcel) {
+
+ // we don't cache passphrases during revocation
+ cryptoInputParcel.mCachePassphrase = false;
+
+ long masterKeyId = revokeKeyringParcel.mMasterKeyId;
+
+ OperationResult.OperationLog log = new OperationResult.OperationLog();
+ log.add(OperationResult.LogType.MSG_REVOKE, 0,
+ KeyFormattingUtils.beautifyKeyId(masterKeyId));
+
+ try {
+
+ Uri secretUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(masterKeyId);
+ CachedPublicKeyRing keyRing = mProviderHelper.getCachedPublicKeyRing(secretUri);
+
+ // check if this is a master secret key we can work with
+ switch (keyRing.getSecretKeyType(masterKeyId)) {
+ case GNU_DUMMY:
+ log.add(OperationResult.LogType.MSG_EK_ERROR_DUMMY, 1);
+ return new RevokeResult(RevokeResult.RESULT_ERROR, log, masterKeyId);
+ }
+
+ SaveKeyringParcel saveKeyringParcel =
+ new SaveKeyringParcel(masterKeyId, keyRing.getFingerprint());
+
+ // all revoke operations are made atomic as of now
+ saveKeyringParcel.setUpdateOptions(revokeKeyringParcel.mUpload, true,
+ revokeKeyringParcel.mKeyserver);
+
+ saveKeyringParcel.mRevokeSubKeys.add(masterKeyId);
+
+ InputPendingResult revokeAndUploadResult = new EditKeyOperation(mContext,
+ mProviderHelper, mProgressable, mCancelled)
+ .execute(saveKeyringParcel, cryptoInputParcel);
+
+ if (revokeAndUploadResult.isPending()) {
+ return revokeAndUploadResult;
+ }
+
+ log.add(revokeAndUploadResult, 1);
+
+ if (revokeAndUploadResult.success()) {
+ log.add(OperationResult.LogType.MSG_REVOKE_OK, 1);
+ return new RevokeResult(RevokeResult.RESULT_OK, log, masterKeyId);
+ } else {
+ log.add(OperationResult.LogType.MSG_REVOKE_ERROR_KEY_FAIL, 1);
+ return new RevokeResult(RevokeResult.RESULT_ERROR, log, masterKeyId);
+ }
+
+ } catch (PgpKeyNotFoundException | ProviderHelper.NotFoundException e) {
+ Log.e(Constants.TAG, "could not find key to revoke", e);
+ log.add(OperationResult.LogType.MSG_REVOKE_ERROR_KEY_FAIL, 1);
+ return new RevokeResult(RevokeResult.RESULT_ERROR, log, masterKeyId);
+ }
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java
index 651d15e8f..843a55389 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java
@@ -19,13 +19,13 @@ package org.sufficientlysecure.keychain.operations;
import android.content.Context;
import android.net.Uri;
+import android.support.annotation.NonNull;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
@@ -37,6 +37,7 @@ import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSign
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType;
import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.InputData;
+import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.ByteArrayInputStream;
@@ -55,13 +56,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
* a pending result, it will terminate.
*
*/
-public class SignEncryptOperation extends BaseOperation {
+public class SignEncryptOperation extends BaseOperation<SignEncryptParcel> {
public SignEncryptOperation(Context context, ProviderHelper providerHelper,
Progressable progressable, AtomicBoolean cancelled) {
super(context, providerHelper, progressable, cancelled);
}
+ @NonNull
public SignEncryptResult execute(SignEncryptParcel input, CryptoInputParcel cryptoInput) {
OperationLog log = new OperationLog();
@@ -85,7 +87,7 @@ public class SignEncryptOperation extends BaseOperation {
input.getSignatureMasterKeyId()).getSecretSignId();
input.setSignatureSubKeyId(signKeyId);
} catch (PgpKeyNotFoundException e) {
- e.printStackTrace();
+ Log.e(Constants.TAG, "Key not found", e);
return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log, results);
}
}
@@ -153,7 +155,7 @@ public class SignEncryptOperation extends BaseOperation {
RequiredInputParcel requiredInput = result.getRequiredInputParcel();
// Passphrase returns immediately, nfc are aggregated
if (requiredInput.mType == RequiredInputType.PASSPHRASE) {
- return new SignEncryptResult(log, requiredInput, results);
+ return new SignEncryptResult(log, requiredInput, results, cryptoInput);
}
if (pendingInputBuilder == null) {
pendingInputBuilder = new NfcSignOperationsBuilder(requiredInput.mSignatureTime,
@@ -171,7 +173,7 @@ public class SignEncryptOperation extends BaseOperation {
} while (!inputUris.isEmpty());
if (pendingInputBuilder != null && !pendingInputBuilder.isEmpty()) {
- return new SignEncryptResult(log, pendingInputBuilder.build(), results);
+ return new SignEncryptResult(log, pendingInputBuilder.build(), results, cryptoInput);
}
if (!outputUris.isEmpty()) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java
index 0a0e63330..cf73f019c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java
@@ -23,6 +23,7 @@ import android.content.Intent;
import android.os.Parcel;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
@@ -38,8 +39,9 @@ public class CertifyResult extends InputPendingResult {
super(result, log);
}
- public CertifyResult(OperationLog log, RequiredInputParcel requiredInput) {
- super(log, requiredInput);
+ public CertifyResult(OperationLog log, RequiredInputParcel requiredInput,
+ CryptoInputParcel cryptoInputParcel) {
+ super(log, requiredInput, cryptoInputParcel);
}
public CertifyResult(int result, OperationLog log, int certifyOk, int certifyError, int uploadOk, int uploadError) {
@@ -132,7 +134,7 @@ public class CertifyResult extends InputPendingResult {
intent.putExtra(LogDisplayFragment.EXTRA_RESULT, CertifyResult.this);
activity.startActivity(intent);
}
- }, R.string.view_log);
+ }, R.string.snackbar_details);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java
index 917b3415f..e8be9fa78 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
*
* This program is free software: you can redistribute it and/or modify
@@ -20,19 +20,50 @@ package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel;
+import org.openintents.openpgp.OpenPgpDecryptionResult;
import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpSignatureResult;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
-import org.sufficientlysecure.keychain.util.Passphrase;
public class DecryptVerifyResult extends InputPendingResult {
+ public static final int RESULT_NO_DATA = RESULT_ERROR + 16;
+ public static final int RESULT_KEY_DISALLOWED = RESULT_ERROR + 32;
+
OpenPgpSignatureResult mSignatureResult;
- OpenPgpMetadata mDecryptMetadata;
+ OpenPgpDecryptionResult mDecryptionResult;
+ OpenPgpMetadata mDecryptionMetadata;
// This holds the charset which was specified in the ascii armor, if specified
// https://tools.ietf.org/html/rfc4880#page56
String mCharset;
+ CryptoInputParcel mCachedCryptoInputParcel;
+
+ byte[] mOutputBytes;
+
+ public DecryptVerifyResult(int result, OperationLog log) {
+ super(result, log);
+ }
+
+ public DecryptVerifyResult(OperationLog log, RequiredInputParcel requiredInput,
+ CryptoInputParcel cryptoInputParcel) {
+ super(log, requiredInput, cryptoInputParcel);
+ }
+
+ public DecryptVerifyResult(Parcel source) {
+ super(source);
+ mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
+ mDecryptionResult = source.readParcelable(OpenPgpDecryptionResult.class.getClassLoader());
+ mDecryptionMetadata = source.readParcelable(OpenPgpMetadata.class.getClassLoader());
+ mCachedCryptoInputParcel = source.readParcelable(CryptoInputParcel.class.getClassLoader());
+ }
+
+
+ public boolean isKeysDisallowed () {
+ return (mResult & RESULT_KEY_DISALLOWED) == RESULT_KEY_DISALLOWED;
+ }
+
public OpenPgpSignatureResult getSignatureResult() {
return mSignatureResult;
}
@@ -41,38 +72,44 @@ public class DecryptVerifyResult extends InputPendingResult {
mSignatureResult = signatureResult;
}
- public OpenPgpMetadata getDecryptMetadata() {
- return mDecryptMetadata;
+ public OpenPgpDecryptionResult getDecryptionResult() {
+ return mDecryptionResult;
}
- public void setDecryptMetadata(OpenPgpMetadata decryptMetadata) {
- mDecryptMetadata = decryptMetadata;
+ public void setDecryptionResult(OpenPgpDecryptionResult decryptionResult) {
+ mDecryptionResult = decryptionResult;
}
- public String getCharset () {
- return mCharset;
+ public CryptoInputParcel getCachedCryptoInputParcel() {
+ return mCachedCryptoInputParcel;
}
- public void setCharset(String charset) {
- mCharset = charset;
+ public void setCachedCryptoInputParcel(CryptoInputParcel cachedCryptoInputParcel) {
+ mCachedCryptoInputParcel = cachedCryptoInputParcel;
}
- public boolean isPending() {
- return (mResult & RESULT_PENDING) == RESULT_PENDING;
+ public OpenPgpMetadata getDecryptionMetadata() {
+ return mDecryptionMetadata;
}
- public DecryptVerifyResult(int result, OperationLog log) {
- super(result, log);
+ public void setDecryptionMetadata(OpenPgpMetadata decryptMetadata) {
+ mDecryptionMetadata = decryptMetadata;
}
- public DecryptVerifyResult(OperationLog log, RequiredInputParcel requiredInput) {
- super(log, requiredInput);
+ public String getCharset () {
+ return mCharset;
}
- public DecryptVerifyResult(Parcel source) {
- super(source);
- mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
- mDecryptMetadata = source.readParcelable(OpenPgpMetadata.class.getClassLoader());
+ public void setCharset(String charset) {
+ mCharset = charset;
+ }
+
+ public void setOutputBytes(byte[] outputBytes) {
+ mOutputBytes = outputBytes;
+ }
+
+ public byte[] getOutputBytes() {
+ return mOutputBytes;
}
public int describeContents() {
@@ -81,8 +118,10 @@ public class DecryptVerifyResult extends InputPendingResult {
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
- dest.writeParcelable(mSignatureResult, 0);
- dest.writeParcelable(mDecryptMetadata, 0);
+ dest.writeParcelable(mSignatureResult, flags);
+ dest.writeParcelable(mDecryptionResult, flags);
+ dest.writeParcelable(mDecryptionMetadata, flags);
+ dest.writeParcelable(mCachedCryptoInputParcel, flags);
}
public static final Creator<DecryptVerifyResult> CREATOR = new Creator<DecryptVerifyResult>() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java
index 50f49add2..1a8f10d4f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DeleteResult.java
@@ -21,8 +21,11 @@ package org.sufficientlysecure.keychain.operations.results;
import android.app.Activity;
import android.content.Intent;
import android.os.Parcel;
+import android.support.annotation.Nullable;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
@@ -30,7 +33,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
import org.sufficientlysecure.keychain.ui.util.Notify.Showable;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
-public class DeleteResult extends OperationResult {
+public class DeleteResult extends InputPendingResult {
final public int mOk, mFail;
@@ -40,6 +43,19 @@ public class DeleteResult extends OperationResult {
mFail = fail;
}
+ /**
+ * used when more input is required
+ * @param log operation log upto point of required input, if any
+ * @param requiredInput represents input required
+ */
+ public DeleteResult(@Nullable OperationLog log, RequiredInputParcel requiredInput,
+ CryptoInputParcel cryptoInputParcel) {
+ super(log, requiredInput, cryptoInputParcel);
+ // values are not to be used
+ mOk = -1;
+ mFail = -1;
+ }
+
/** Construct from a parcel - trivial because we have no extra data. */
public DeleteResult(Parcel source) {
super(source);
@@ -109,7 +125,10 @@ public class DeleteResult extends OperationResult {
} else {
duration = 0;
style = Style.ERROR;
- if (mFail == 0) {
+ if (mLog.getLast().mType == LogType.MSG_DEL_ERROR_MULTI_SECRET) {
+ str = activity.getString(R.string.secret_cannot_multiple);
+ }
+ else if (mFail == 0) {
str = activity.getString(R.string.delete_nothing);
} else {
str = activity.getResources().getQuantityString(R.plurals.delete_fail, mFail);
@@ -124,7 +143,7 @@ public class DeleteResult extends OperationResult {
intent.putExtra(LogDisplayFragment.EXTRA_RESULT, DeleteResult.this);
activity.startActivity(intent);
}
- }, R.string.view_log);
+ }, R.string.snackbar_details);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java
index 842b75c3b..6098d59d5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java
@@ -20,7 +20,10 @@ package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel;
-public class EditKeyResult extends OperationResult {
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
+
+public class EditKeyResult extends InputPendingResult {
public final Long mMasterKeyId;
@@ -29,6 +32,12 @@ public class EditKeyResult extends OperationResult {
mMasterKeyId = masterKeyId;
}
+ public EditKeyResult(OperationLog log, RequiredInputParcel requiredInput,
+ CryptoInputParcel cryptoInputParcel) {
+ super(log, requiredInput, cryptoInputParcel);
+ mMasterKeyId = null;
+ }
+
public EditKeyResult(Parcel source) {
super(source);
mMasterKeyId = source.readInt() != 0 ? source.readLong() : null;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ExportResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ExportResult.java
index c8edce259..e21ef949f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ExportResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ExportResult.java
@@ -19,7 +19,10 @@ package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel;
-public class ExportResult extends OperationResult {
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
+
+public class ExportResult extends InputPendingResult {
final int mOkPublic, mOkSecret;
@@ -33,6 +36,15 @@ public class ExportResult extends OperationResult {
mOkSecret = okSecret;
}
+
+ public ExportResult(OperationLog log, RequiredInputParcel requiredInputParcel,
+ CryptoInputParcel cryptoInputParcel) {
+ super(log, requiredInputParcel, cryptoInputParcel);
+ // we won't use these values
+ mOkPublic = -1;
+ mOkSecret = -1;
+ }
+
/** Construct from a parcel - trivial because we have no extra data. */
public ExportResult(Parcel source) {
super(source);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/GetKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/GetKeyResult.java
index 53bc545c5..bdc4d9a47 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/GetKeyResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/GetKeyResult.java
@@ -20,7 +20,10 @@ package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel;
-public class GetKeyResult extends OperationResult {
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
+
+public class GetKeyResult extends InputPendingResult {
public int mNonPgpPartsCount;
@@ -36,6 +39,11 @@ public class GetKeyResult extends OperationResult {
super(result, log);
}
+ public GetKeyResult(OperationLog log, RequiredInputParcel requiredInput,
+ CryptoInputParcel cryptoInputParcel) {
+ super(log, requiredInput, cryptoInputParcel);
+ }
+
public static final int RESULT_ERROR_NO_VALID_KEYS = RESULT_ERROR + 8;
public static final int RESULT_ERROR_NO_PGP_PARTS = RESULT_ERROR + 16;
public static final int RESULT_ERROR_QUERY_TOO_SHORT = RESULT_ERROR + 32;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ImportKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ImportKeyResult.java
index 1438ad698..5f5090bee 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ImportKeyResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/ImportKeyResult.java
@@ -23,6 +23,8 @@ import android.content.Intent;
import android.os.Parcel;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
@@ -30,7 +32,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
import org.sufficientlysecure.keychain.ui.util.Notify.Showable;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
-public class ImportKeyResult extends OperationResult {
+public class ImportKeyResult extends InputPendingResult {
public final int mNewKeys, mUpdatedKeys, mBadKeys, mSecret;
public final long[] mImportedMasterKeyIds;
@@ -80,7 +82,7 @@ public class ImportKeyResult extends OperationResult {
}
public ImportKeyResult(int result, OperationLog log) {
- this(result, log, 0, 0, 0, 0, new long[] { });
+ this(result, log, 0, 0, 0, 0, new long[]{});
}
public ImportKeyResult(int result, OperationLog log,
@@ -94,6 +96,17 @@ public class ImportKeyResult extends OperationResult {
mImportedMasterKeyIds = importedMasterKeyIds;
}
+ public ImportKeyResult(OperationLog log, RequiredInputParcel requiredInputParcel,
+ CryptoInputParcel cryptoInputParcel) {
+ super(log, requiredInputParcel, cryptoInputParcel);
+ // just assign default values, we won't use them anyway
+ mNewKeys = 0;
+ mUpdatedKeys = 0;
+ mBadKeys = 0;
+ mSecret = 0;
+ mImportedMasterKeyIds = new long[]{};
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
@@ -190,7 +203,7 @@ public class ImportKeyResult extends OperationResult {
intent.putExtra(LogDisplayFragment.EXTRA_RESULT, ImportKeyResult.this);
activity.startActivity(intent);
}
- }, R.string.view_log);
+ }, R.string.snackbar_details);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/InputPendingResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/InputPendingResult.java
index 0b7aa6d03..d767382ae 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/InputPendingResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/InputPendingResult.java
@@ -18,10 +18,9 @@
package org.sufficientlysecure.keychain.operations.results;
-import java.util.ArrayList;
-
import android.os.Parcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
public class InputPendingResult extends OperationResult {
@@ -30,26 +29,33 @@ public class InputPendingResult extends OperationResult {
public static final int RESULT_PENDING = RESULT_ERROR + 8;
final RequiredInputParcel mRequiredInput;
+ // in case operation needs to add to/changes the cryptoInputParcel sent to it
+ public final CryptoInputParcel mCryptoInputParcel;
public InputPendingResult(int result, OperationLog log) {
super(result, log);
mRequiredInput = null;
+ mCryptoInputParcel = null;
}
- public InputPendingResult(OperationLog log, RequiredInputParcel requiredInput) {
+ public InputPendingResult(OperationLog log, RequiredInputParcel requiredInput,
+ CryptoInputParcel cryptoInputParcel) {
super(RESULT_PENDING, log);
mRequiredInput = requiredInput;
+ mCryptoInputParcel = cryptoInputParcel;
}
public InputPendingResult(Parcel source) {
super(source);
mRequiredInput = source.readParcelable(getClass().getClassLoader());
+ mCryptoInputParcel = source.readParcelable(getClass().getClassLoader());
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeParcelable(mRequiredInput, 0);
+ dest.writeParcelable(mCryptoInputParcel, 0);
}
public boolean isPending() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/KeybaseVerificationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/KeybaseVerificationResult.java
new file mode 100644
index 000000000..84648d32c
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/KeybaseVerificationResult.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.operations.results;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import com.textuality.keybase.lib.KeybaseException;
+import com.textuality.keybase.lib.prover.Prover;
+
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
+
+public class KeybaseVerificationResult extends InputPendingResult {
+ public final String mProofUrl;
+ public final String mPresenceUrl;
+ public final String mPresenceLabel;
+
+ public KeybaseVerificationResult(int result, OperationLog log) {
+ super(result, log);
+ mProofUrl = null;
+ mPresenceLabel = null;
+ mPresenceUrl = null;
+ }
+
+ public KeybaseVerificationResult(int result, OperationLog log, Prover prover)
+ throws KeybaseException {
+ super(result, log);
+ mProofUrl = prover.getProofUrl();
+ mPresenceUrl = prover.getPresenceUrl();
+ mPresenceLabel = prover.getPresenceLabel();
+ }
+
+ public KeybaseVerificationResult(OperationLog log, RequiredInputParcel requiredInputParcel,
+ CryptoInputParcel cryptoInputParcel) {
+ super(log, requiredInputParcel, cryptoInputParcel);
+ mProofUrl = null;
+ mPresenceUrl = null;
+ mPresenceLabel = null;
+ }
+
+ protected KeybaseVerificationResult(Parcel in) {
+ super(in);
+ mProofUrl = in.readString();
+ mPresenceUrl = in.readString();
+ mPresenceLabel = in.readString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mProofUrl);
+ dest.writeString(mPresenceUrl);
+ dest.writeString(mPresenceLabel);
+ }
+
+ public static final Parcelable.Creator<KeybaseVerificationResult> CREATOR = new Parcelable.Creator<KeybaseVerificationResult>() {
+ @Override
+ public KeybaseVerificationResult createFromParcel(Parcel in) {
+ return new KeybaseVerificationResult(in);
+ }
+
+ @Override
+ public KeybaseVerificationResult[] newArray(int size) {
+ return new KeybaseVerificationResult[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
index c93db5c39..f213b1aad 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
@@ -22,6 +22,7 @@ import android.app.Activity;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.annotation.NonNull;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -101,9 +102,9 @@ public abstract class OperationResult implements Parcelable {
}
public OperationLog getLog() {
- // If there is only a single entry, and it's a compound one, return that log
- if (mLog.isSingleCompound()) {
- return ((SubLogEntryParcel) mLog.getFirst()).getSubResult().getLog();
+ SubLogEntryParcel singleSubLog = mLog.getSubResultIfSingle();
+ if (singleSubLog != null) {
+ return singleSubLog.getSubResult().getLog();
}
// Otherwse, return our regular log
return mLog;
@@ -169,9 +170,9 @@ public abstract class OperationResult implements Parcelable {
public static class SubLogEntryParcel extends LogEntryParcel {
- OperationResult mSubResult;
+ @NonNull OperationResult mSubResult;
- public SubLogEntryParcel(OperationResult subResult, LogType type, int indent, Object... parameters) {
+ public SubLogEntryParcel(@NonNull OperationResult subResult, LogType type, int indent, Object... parameters) {
super(type, indent, parameters);
mSubResult = subResult;
@@ -209,6 +210,10 @@ public abstract class OperationResult implements Parcelable {
String logText;
LogEntryParcel entryParcel = mLog.getLast();
+ if (entryParcel == null) {
+ Log.e(Constants.TAG, "Tried to show empty log!");
+ return Notify.create(activity, R.string.error_empty_log, Style.ERROR);
+ }
// special case: first parameter may be a quantity
if (entryParcel.mParameters != null && entryParcel.mParameters.length > 0
&& entryParcel.mParameters[0] instanceof Integer) {
@@ -248,7 +253,7 @@ public abstract class OperationResult implements Parcelable {
intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResult.this);
activity.startActivity(intent);
}
- }, R.string.view_log);
+ }, R.string.snackbar_details);
}
@@ -269,7 +274,7 @@ public abstract class OperationResult implements Parcelable {
* mark.
*
*/
- public static enum LogType {
+ public enum LogType {
MSG_INTERNAL_ERROR (LogLevel.ERROR, R.string.msg_internal_error),
MSG_OPERATION_CANCELLED (LogLevel.CANCELLED, R.string.msg_cancelled),
@@ -401,6 +406,7 @@ public abstract class OperationResult implements Parcelable {
MSG_KC_SUB_BAD_LOCAL(LogLevel.WARN, R.string.msg_kc_sub_bad_local),
MSG_KC_SUB_BAD_KEYID(LogLevel.WARN, R.string.msg_kc_sub_bad_keyid),
MSG_KC_SUB_BAD_TIME(LogLevel.WARN, R.string.msg_kc_sub_bad_time),
+ MSG_KC_SUB_BAD_TIME_EARLY(LogLevel.WARN, R.string.msg_kc_sub_bad_time_early),
MSG_KC_SUB_BAD_TYPE(LogLevel.WARN, R.string.msg_kc_sub_bad_type),
MSG_KC_SUB_DUP (LogLevel.DEBUG, R.string.msg_kc_sub_dup),
MSG_KC_SUB_PRIMARY_BAD(LogLevel.WARN, R.string.msg_kc_sub_primary_bad),
@@ -476,6 +482,7 @@ public abstract class OperationResult implements Parcelable {
// secret key modify
MSG_MF (LogLevel.START, R.string.msg_mr),
MSG_MF_DIVERT (LogLevel.DEBUG, R.string.msg_mf_divert),
+ MSG_MF_ERROR_DIVERT_NEWSUB (LogLevel.ERROR, R.string.msg_mf_error_divert_newsub),
MSG_MF_ERROR_DIVERT_SERIAL (LogLevel.ERROR, R.string.msg_mf_error_divert_serial),
MSG_MF_ERROR_ENCODE (LogLevel.ERROR, R.string.msg_mf_error_encode),
MSG_MF_ERROR_FINGERPRINT (LogLevel.ERROR, R.string.msg_mf_error_fingerprint),
@@ -493,11 +500,20 @@ public abstract class OperationResult implements Parcelable {
MSG_MF_ERROR_RESTRICTED(LogLevel.ERROR, R.string.msg_mf_error_restricted),
MSG_MF_ERROR_REVOKED_PRIMARY (LogLevel.ERROR, R.string.msg_mf_error_revoked_primary),
MSG_MF_ERROR_SIG (LogLevel.ERROR, R.string.msg_mf_error_sig),
+ MSG_MF_ERROR_SUB_STRIPPED(LogLevel.ERROR, R.string.msg_mf_error_sub_stripped),
MSG_MF_ERROR_SUBKEY_MISSING(LogLevel.ERROR, R.string.msg_mf_error_subkey_missing),
+ MSG_MF_ERROR_CONFLICTING_NFC_COMMANDS(LogLevel.ERROR, R.string.msg_mf_error_conflicting_nfc_commands),
+ MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT(LogLevel.ERROR, R.string.msg_mf_error_duplicate_keytocard_for_slot),
+ MSG_MF_ERROR_INVALID_FLAGS_FOR_KEYTOCARD(LogLevel.ERROR, R.string.msg_mf_error_invalid_flags_for_keytocard),
+ MSG_MF_ERROR_BAD_NFC_ALGO(LogLevel.ERROR, R.string.edit_key_error_bad_nfc_algo),
+ MSG_MF_ERROR_BAD_NFC_SIZE(LogLevel.ERROR, R.string.edit_key_error_bad_nfc_size),
+ MSG_MF_ERROR_BAD_NFC_STRIPPED(LogLevel.ERROR, R.string.edit_key_error_bad_nfc_stripped),
MSG_MF_MASTER (LogLevel.DEBUG, R.string.msg_mf_master),
MSG_MF_NOTATION_PIN (LogLevel.DEBUG, R.string.msg_mf_notation_pin),
MSG_MF_NOTATION_EMPTY (LogLevel.DEBUG, R.string.msg_mf_notation_empty),
MSG_MF_PASSPHRASE (LogLevel.INFO, R.string.msg_mf_passphrase),
+ MSG_MF_PIN (LogLevel.INFO, R.string.msg_mf_pin),
+ MSG_MF_ADMIN_PIN (LogLevel.INFO, R.string.msg_mf_admin_pin),
MSG_MF_PASSPHRASE_KEY (LogLevel.DEBUG, R.string.msg_mf_passphrase_key),
MSG_MF_PASSPHRASE_EMPTY_RETRY (LogLevel.DEBUG, R.string.msg_mf_passphrase_empty_retry),
MSG_MF_PASSPHRASE_FAIL (LogLevel.WARN, R.string.msg_mf_passphrase_fail),
@@ -511,6 +527,8 @@ public abstract class OperationResult implements Parcelable {
MSG_MF_SUBKEY_NEW (LogLevel.INFO, R.string.msg_mf_subkey_new),
MSG_MF_SUBKEY_REVOKE (LogLevel.INFO, R.string.msg_mf_subkey_revoke),
MSG_MF_SUBKEY_STRIP (LogLevel.INFO, R.string.msg_mf_subkey_strip),
+ MSG_MF_KEYTOCARD_START (LogLevel.INFO, R.string.msg_mf_keytocard_start),
+ MSG_MF_KEYTOCARD_FINISH (LogLevel.OK, R.string.msg_mf_keytocard_finish),
MSG_MF_SUCCESS (LogLevel.OK, R.string.msg_mf_success),
MSG_MF_UID_ADD (LogLevel.INFO, R.string.msg_mf_uid_add),
MSG_MF_UID_PRIMARY (LogLevel.INFO, R.string.msg_mf_uid_primary),
@@ -553,13 +571,18 @@ public abstract class OperationResult implements Parcelable {
MSG_ED_CACHING_NEW (LogLevel.DEBUG, R.string.msg_ed_caching_new),
MSG_ED_ERROR_NO_PARCEL (LogLevel.ERROR, R.string.msg_ed_error_no_parcel),
MSG_ED_ERROR_KEY_NOT_FOUND (LogLevel.ERROR, R.string.msg_ed_error_key_not_found),
+ MSG_ED_ERROR_EXTRACTING_PUBLIC_UPLOAD (LogLevel.ERROR,
+ R.string.msg_ed_error_extract_public_upload),
MSG_ED_FETCHING (LogLevel.DEBUG, R.string.msg_ed_fetching),
MSG_ED_SUCCESS (LogLevel.OK, R.string.msg_ed_success),
// promote key
MSG_PR (LogLevel.START, R.string.msg_pr),
+ MSG_PR_ALL (LogLevel.DEBUG, R.string.msg_pr_all),
MSG_PR_ERROR_KEY_NOT_FOUND (LogLevel.ERROR, R.string.msg_pr_error_key_not_found),
MSG_PR_FETCHING (LogLevel.DEBUG, R.string.msg_pr_fetching),
+ MSG_PR_SUBKEY_MATCH (LogLevel.DEBUG, R.string.msg_pr_subkey_match),
+ MSG_PR_SUBKEY_NOMATCH (LogLevel.WARN, R.string.msg_pr_subkey_nomatch),
MSG_PR_SUCCESS (LogLevel.OK, R.string.msg_pr_success),
// messages used in UI code
@@ -584,15 +607,16 @@ public abstract class OperationResult implements Parcelable {
MSG_DC_CLEAR_SIGNATURE_OK (LogLevel.OK, R.string.msg_dc_clear_signature_ok),
MSG_DC_CLEAR_SIGNATURE (LogLevel.DEBUG, R.string.msg_dc_clear_signature),
MSG_DC_ERROR_BAD_PASSPHRASE (LogLevel.ERROR, R.string.msg_dc_error_bad_passphrase),
+ MSG_DC_ERROR_SYM_PASSPHRASE (LogLevel.ERROR, R.string.msg_dc_error_sym_passphrase),
+ MSG_DC_ERROR_CORRUPT_DATA (LogLevel.ERROR, R.string.msg_dc_error_corrupt_data),
MSG_DC_ERROR_EXTRACT_KEY (LogLevel.ERROR, R.string.msg_dc_error_extract_key),
MSG_DC_ERROR_INTEGRITY_CHECK (LogLevel.ERROR, R.string.msg_dc_error_integrity_check),
- MSG_DC_ERROR_INTEGRITY_MISSING (LogLevel.ERROR, R.string.msg_dc_error_integrity_missing),
- MSG_DC_ERROR_INVALID_SIGLIST(LogLevel.ERROR, R.string.msg_dc_error_invalid_siglist),
+ MSG_DC_ERROR_INVALID_DATA (LogLevel.ERROR, R.string.msg_dc_error_invalid_data),
MSG_DC_ERROR_IO (LogLevel.ERROR, R.string.msg_dc_error_io),
+ MSG_DC_ERROR_INPUT (LogLevel.ERROR, R.string.msg_dc_error_input),
MSG_DC_ERROR_NO_DATA (LogLevel.ERROR, R.string.msg_dc_error_no_data),
MSG_DC_ERROR_NO_KEY (LogLevel.ERROR, R.string.msg_dc_error_no_key),
MSG_DC_ERROR_PGP_EXCEPTION (LogLevel.ERROR, R.string.msg_dc_error_pgp_exception),
- MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO (LogLevel.ERROR, R.string.msg_dc_error_unsupported_hash_algo),
MSG_DC_INTEGRITY_CHECK_OK (LogLevel.INFO, R.string.msg_dc_integrity_check_ok),
MSG_DC_OK_META_ONLY (LogLevel.OK, R.string.msg_dc_ok_meta_only),
MSG_DC_OK (LogLevel.OK, R.string.msg_dc_ok),
@@ -607,7 +631,10 @@ public abstract class OperationResult implements Parcelable {
MSG_DC_TRAIL_SYM (LogLevel.DEBUG, R.string.msg_dc_trail_sym),
MSG_DC_TRAIL_UNKNOWN (LogLevel.DEBUG, R.string.msg_dc_trail_unknown),
MSG_DC_UNLOCKING (LogLevel.INFO, R.string.msg_dc_unlocking),
- MSG_DC_OLD_SYMMETRIC_ENCRYPTION_ALGO (LogLevel.WARN, R.string.msg_dc_old_symmetric_encryption_algo),
+ MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO(LogLevel.WARN, R.string.msg_dc_insecure_symmetric_encryption_algo),
+ MSG_DC_INSECURE_HASH_ALGO(LogLevel.ERROR, R.string.msg_dc_insecure_hash_algo),
+ MSG_DC_INSECURE_MDC_MISSING(LogLevel.ERROR, R.string.msg_dc_insecure_mdc_missing),
+ MSG_DC_INSECURE_KEY(LogLevel.ERROR, R.string.msg_dc_insecure_key),
// verify signed literal data
MSG_VL (LogLevel.INFO, R.string.msg_vl),
@@ -634,7 +661,6 @@ public abstract class OperationResult implements Parcelable {
MSG_PSE_COMPRESSING (LogLevel.DEBUG, R.string.msg_pse_compressing),
MSG_PSE_ENCRYPTING (LogLevel.DEBUG, R.string.msg_pse_encrypting),
MSG_PSE_ERROR_BAD_PASSPHRASE (LogLevel.ERROR, R.string.msg_pse_error_bad_passphrase),
- MSG_PSE_ERROR_HASH_ALGO (LogLevel.ERROR, R.string.msg_pse_error_hash_algo),
MSG_PSE_ERROR_IO (LogLevel.ERROR, R.string.msg_pse_error_io),
MSG_PSE_ERROR_SIGN_KEY(LogLevel.ERROR, R.string.msg_pse_error_sign_key),
MSG_PSE_ERROR_KEY_SIGN (LogLevel.ERROR, R.string.msg_pse_error_key_sign),
@@ -672,6 +698,7 @@ public abstract class OperationResult implements Parcelable {
MSG_CRT_WARN_NOT_FOUND (LogLevel.WARN, R.string.msg_crt_warn_not_found),
MSG_CRT_WARN_CERT_FAILED (LogLevel.WARN, R.string.msg_crt_warn_cert_failed),
MSG_CRT_WARN_SAVE_FAILED (LogLevel.WARN, R.string.msg_crt_warn_save_failed),
+ MSG_CRT_WARN_UPLOAD_FAILED (LogLevel.WARN, R.string.msg_crt_warn_upload_failed),
MSG_IMPORT (LogLevel.START, R.plurals.msg_import),
@@ -683,6 +710,7 @@ public abstract class OperationResult implements Parcelable {
MSG_IMPORT_FETCH_KEYBASE (LogLevel.INFO, R.string.msg_import_fetch_keybase),
MSG_IMPORT_KEYSERVER (LogLevel.DEBUG, R.string.msg_import_keyserver),
MSG_IMPORT_MERGE (LogLevel.DEBUG, R.string.msg_import_merge),
+ MSG_IMPORT_MERGE_ERROR (LogLevel.ERROR, R.string.msg_import_merge_error),
MSG_IMPORT_FINGERPRINT_ERROR (LogLevel.ERROR, R.string.msg_import_fingerprint_error),
MSG_IMPORT_FINGERPRINT_OK (LogLevel.DEBUG, R.string.msg_import_fingerprint_ok),
MSG_IMPORT_ERROR (LogLevel.ERROR, R.string.msg_import_error),
@@ -691,6 +719,8 @@ public abstract class OperationResult implements Parcelable {
MSG_IMPORT_SUCCESS (LogLevel.OK, R.string.msg_import_success),
MSG_EXPORT (LogLevel.START, R.plurals.msg_export),
+ MSG_EXPORT_FILE_NAME (LogLevel.INFO, R.string.msg_export_file_name),
+ MSG_EXPORT_UPLOAD_PUBLIC (LogLevel.START, R.string.msg_export_upload_public),
MSG_EXPORT_PUBLIC (LogLevel.DEBUG, R.string.msg_export_public),
MSG_EXPORT_SECRET (LogLevel.DEBUG, R.string.msg_export_secret),
MSG_EXPORT_ALL (LogLevel.START, R.string.msg_export_all),
@@ -702,13 +732,16 @@ public abstract class OperationResult implements Parcelable {
MSG_EXPORT_ERROR_DB (LogLevel.ERROR, R.string.msg_export_error_db),
MSG_EXPORT_ERROR_IO (LogLevel.ERROR, R.string.msg_export_error_io),
MSG_EXPORT_ERROR_KEY (LogLevel.ERROR, R.string.msg_export_error_key),
+ MSG_EXPORT_ERROR_UPLOAD (LogLevel.ERROR, R.string.msg_export_error_upload),
MSG_EXPORT_SUCCESS (LogLevel.OK, R.string.msg_export_success),
+ MSG_EXPORT_UPLOAD_SUCCESS (LogLevel.OK, R.string.msg_export_upload_success),
MSG_CRT_UPLOAD_SUCCESS (LogLevel.OK, R.string.msg_crt_upload_success),
MSG_ACC_SAVED (LogLevel.INFO, R.string.api_settings_save_msg),
- MSG_WRONG_QR_CODE (LogLevel.INFO, R.string.import_qr_code_wrong),
+ MSG_WRONG_QR_CODE (LogLevel.ERROR, R.string.import_qr_code_wrong),
+ MSG_WRONG_QR_CODE_FP(LogLevel.ERROR, R.string.import_qr_code_fp),
MSG_NO_VALID_ENC (LogLevel.ERROR, R.string.error_invalid_data),
@@ -722,7 +755,7 @@ public abstract class OperationResult implements Parcelable {
MSG_GET_QUERY_FAILED(LogLevel.ERROR, R.string.msg_download_query_failed),
MSG_DEL_ERROR_EMPTY (LogLevel.ERROR, R.string.msg_del_error_empty),
- MSG_DEL_ERROR_MULTI_SECRET (LogLevel.DEBUG, R.string.msg_del_error_multi_secret),
+ MSG_DEL_ERROR_MULTI_SECRET (LogLevel.ERROR, R.string.msg_del_error_multi_secret),
MSG_DEL (LogLevel.START, R.plurals.msg_del),
MSG_DEL_KEY (LogLevel.DEBUG, R.string.msg_del_key),
MSG_DEL_KEY_FAIL (LogLevel.WARN, R.string.msg_del_key_fail),
@@ -730,6 +763,25 @@ public abstract class OperationResult implements Parcelable {
MSG_DEL_OK (LogLevel.OK, R.plurals.msg_del_ok),
MSG_DEL_FAIL (LogLevel.WARN, R.plurals.msg_del_fail),
+ MSG_REVOKE_ERROR_EMPTY (LogLevel.ERROR, R.string.msg_revoke_error_empty),
+ MSG_REVOKE_ERROR_NOT_FOUND (LogLevel.ERROR, R.string.msg_revoke_error_not_found),
+ MSG_REVOKE (LogLevel.DEBUG, R.string.msg_revoke_key),
+ MSG_REVOKE_ERROR_KEY_FAIL (LogLevel.ERROR, R.string.msg_revoke_key_fail),
+ MSG_REVOKE_OK (LogLevel.OK, R.string.msg_revoke_ok),
+
+ // keybase verification
+ MSG_KEYBASE_VERIFICATION(LogLevel.START, R.string.msg_keybase_verification),
+
+ MSG_KEYBASE_ERROR_NO_PROVER(LogLevel.ERROR, R.string.msg_keybase_error_no_prover),
+ MSG_KEYBASE_ERROR_FETCH_PROOF(LogLevel.ERROR, R.string.msg_keybase_error_fetching_evidence),
+ MSG_KEYBASE_ERROR_FINGERPRINT_MISMATCH(LogLevel.ERROR,
+ R.string.msg_keybase_error_key_mismatch),
+ MSG_KEYBASE_ERROR_DNS_FAIL(LogLevel.ERROR, R.string.msg_keybase_error_dns_fail),
+ MSG_KEYBASE_ERROR_SPECIFIC(LogLevel.ERROR, R.string.msg_keybase_error_specific),
+ MSG_KEYBASE_ERROR_PAYLOAD_MISMATCH(LogLevel.ERROR,
+ R.string.msg_keybase_error_msg_payload_mismatch),
+
+ // export log
MSG_LV (LogLevel.START, R.string.msg_lv),
MSG_LV_MATCH (LogLevel.DEBUG, R.string.msg_lv_match),
MSG_LV_MATCH_ERROR (LogLevel.ERROR, R.string.msg_lv_match_error),
@@ -770,7 +822,7 @@ public abstract class OperationResult implements Parcelable {
}
/** Enumeration of possible log levels. */
- public static enum LogLevel {
+ public enum LogLevel {
DEBUG,
INFO,
WARN,
@@ -810,8 +862,15 @@ public abstract class OperationResult implements Parcelable {
mParcels.add(new SubLogEntryParcel(subResult, subLog.getFirst().mType, indent, subLog.getFirst().mParameters));
}
- boolean isSingleCompound() {
- return mParcels.size() == 1 && getFirst() instanceof SubLogEntryParcel;
+ public SubLogEntryParcel getSubResultIfSingle() {
+ if (mParcels.size() != 1) {
+ return null;
+ }
+ LogEntryParcel first = getFirst();
+ if (first instanceof SubLogEntryParcel) {
+ return (SubLogEntryParcel) first;
+ }
+ return null;
}
public void clear() {
@@ -859,7 +918,11 @@ public abstract class OperationResult implements Parcelable {
if (mParcels.isEmpty()) {
return null;
}
- return mParcels.get(mParcels.size() -1);
+ LogEntryParcel last = mParcels.get(mParcels.size() -1);
+ if (last instanceof SubLogEntryParcel) {
+ return ((SubLogEntryParcel) last).getSubResult().getLog().getLast();
+ }
+ return last;
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpEditKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpEditKeyResult.java
index 38edbf6ee..30307ba46 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpEditKeyResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpEditKeyResult.java
@@ -22,6 +22,7 @@ import android.os.Parcel;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
@@ -37,8 +38,9 @@ public class PgpEditKeyResult extends InputPendingResult {
mRingMasterKeyId = ring != null ? ring.getMasterKeyId() : Constants.key.none;
}
- public PgpEditKeyResult(OperationLog log, RequiredInputParcel requiredInput) {
- super(log, requiredInput);
+ public PgpEditKeyResult(OperationLog log, RequiredInputParcel requiredInput,
+ CryptoInputParcel cryptoInputParcel) {
+ super(log, requiredInput, cryptoInputParcel);
mRingMasterKeyId = Constants.key.none;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java
index acb265462..2b33b8ace 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
@@ -38,8 +39,9 @@ public class PgpSignEncryptResult extends InputPendingResult {
super(result, log);
}
- public PgpSignEncryptResult(OperationLog log, RequiredInputParcel requiredInput) {
- super(log, requiredInput);
+ public PgpSignEncryptResult(OperationLog log, RequiredInputParcel requiredInput,
+ CryptoInputParcel cryptoInputParcel) {
+ super(log, requiredInput, cryptoInputParcel);
}
public PgpSignEncryptResult(Parcel source) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/RevokeResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/RevokeResult.java
new file mode 100644
index 000000000..b737f6e50
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/RevokeResult.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.operations.results;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.Nullable;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
+import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
+import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+
+public class RevokeResult extends InputPendingResult {
+
+ public final long mMasterKeyId;
+
+ public RevokeResult(int result, OperationLog log, long masterKeyId) {
+ super(result, log);
+ mMasterKeyId = masterKeyId;
+ }
+
+ /**
+ * used when more input is required
+ *
+ * @param log operation log upto point of required input, if any
+ * @param requiredInput represents input required
+ */
+ @SuppressWarnings("unused") // standard pattern across all results, we might need it later
+ public RevokeResult(@Nullable OperationLog log, RequiredInputParcel requiredInput,
+ CryptoInputParcel cryptoInputParcel) {
+ super(log, requiredInput, cryptoInputParcel);
+ // we won't use these values
+ mMasterKeyId = -1;
+ }
+
+ /** Construct from a parcel - trivial because we have no extra data. */
+ public RevokeResult(Parcel source) {
+ super(source);
+ mMasterKeyId = source.readLong();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeLong(mMasterKeyId);
+ }
+
+ public static final Parcelable.Creator<RevokeResult> CREATOR = new Parcelable.Creator<RevokeResult>() {
+ @Override
+ public RevokeResult createFromParcel(Parcel in) {
+ return new RevokeResult(in);
+ }
+
+ @Override
+ public RevokeResult[] newArray(int size) {
+ return new RevokeResult[size];
+ }
+ };
+
+ @Override
+ public Notify.Showable createNotify(final Activity activity) {
+
+ int resultType = getResult();
+
+ String str;
+ int duration;
+ Notify.Style style;
+
+ // Not an overall failure
+ if ((resultType & OperationResult.RESULT_ERROR) == 0) {
+
+ duration = Notify.LENGTH_LONG;
+
+ // New and updated keys
+ if (resultType == OperationResult.RESULT_OK) {
+ style = Notify.Style.OK;
+ str = activity.getString(R.string.revoke_ok);
+ } else {
+ duration = 0;
+ style = Notify.Style.ERROR;
+ str = "internal error";
+ }
+
+ } else {
+ duration = 0;
+ style = Notify.Style.ERROR;
+ str = activity.getString(R.string.revoke_fail);
+ }
+
+ return Notify.create(activity, str, duration, style, new Notify.ActionListener() {
+ @Override
+ public void onAction() {
+ Intent intent = new Intent(
+ activity, LogDisplayActivity.class);
+ intent.putExtra(LogDisplayFragment.EXTRA_RESULT, RevokeResult.this);
+ activity.startActivity(intent);
+ }
+ }, R.string.snackbar_details);
+
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java
index b05921b0d..0e0c5d598 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java
@@ -19,18 +19,20 @@ package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel;
-import java.util.ArrayList;
-
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
+import java.util.ArrayList;
public class SignEncryptResult extends InputPendingResult {
ArrayList<PgpSignEncryptResult> mResults;
byte[] mResultBytes;
- public SignEncryptResult(OperationLog log, RequiredInputParcel requiredInput, ArrayList<PgpSignEncryptResult> results) {
- super(log, requiredInput);
+ public SignEncryptResult(OperationLog log, RequiredInputParcel requiredInput,
+ ArrayList<PgpSignEncryptResult> results,
+ CryptoInputParcel cryptoInputParcel) {
+ super(log, requiredInput, cryptoInputParcel);
mResults = results;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
index 4adacaf23..770e8de91 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
@@ -19,6 +19,7 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPPublicKey;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.util.IterableIterator;
@@ -26,6 +27,9 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
/** A generic wrapped PGPKeyRing object.
*
@@ -90,6 +94,16 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
return getRing().getPublicKey().isEncryptionKey();
}
+ public Set<Long> getEncryptIds() {
+ HashSet<Long> result = new HashSet<>();
+ for(CanonicalizedPublicKey key : publicKeyIterator()) {
+ if (key.canEncrypt() && key.isValid()) {
+ result.add(key.getKeyId());
+ }
+ }
+ return result;
+ }
+
public long getEncryptId() throws PgpKeyNotFoundException {
for(CanonicalizedPublicKey key : publicKeyIterator()) {
if (key.canEncrypt() && key.isValid()) {
@@ -127,7 +141,11 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
}
public CanonicalizedPublicKey getPublicKey(long id) {
- return new CanonicalizedPublicKey(this, getRing().getPublicKey(id));
+ PGPPublicKey pubKey = getRing().getPublicKey(id);
+ if (pubKey == null) {
+ return null;
+ }
+ return new CanonicalizedPublicKey(this, pubKey);
}
public byte[] getEncoded() throws IOException {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
index 8432b8f9f..be5f21f23 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
@@ -18,10 +18,10 @@
package org.sufficientlysecure.keychain.pgp;
-import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.util.IterableIterator;
@@ -62,19 +62,6 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
return mRing;
}
- /** Getter that returns the subkey that should be used for signing. */
- CanonicalizedPublicKey getEncryptionSubKey() throws PgpKeyNotFoundException {
- PGPPublicKey key = getRing().getPublicKey(getEncryptId());
- if(key != null) {
- CanonicalizedPublicKey cKey = new CanonicalizedPublicKey(this, key);
- if(!cKey.canEncrypt()) {
- throw new PgpKeyNotFoundException("key error");
- }
- return cKey;
- }
- throw new PgpKeyNotFoundException("no encryption key available");
- }
-
public IterableIterator<CanonicalizedPublicKey> publicKeyIterator() {
@SuppressWarnings("unchecked")
final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
@@ -97,15 +84,25 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
}
/** Create a dummy secret ring from this key */
- public UncachedKeyRing createDummySecretRing () {
- PGPSecretKeyRing secRing = PGPSecretKeyRing.constructDummyFromPublic(getRing(), null);
- return new UncachedKeyRing(secRing);
- }
-
- /** Create a dummy secret ring from this key */
- public UncachedKeyRing createDivertSecretRing (byte[] cardAid) {
+ public UncachedKeyRing createDivertSecretRing (byte[] cardAid, long[] subKeyIds) {
PGPSecretKeyRing secRing = PGPSecretKeyRing.constructDummyFromPublic(getRing(), cardAid);
- return new UncachedKeyRing(secRing);
+
+ if (subKeyIds == null) {
+ return new UncachedKeyRing(secRing);
+ }
+
+ // if only specific subkeys should be promoted, construct a
+ // stripped dummy, then move divert-to-card keys over
+ PGPSecretKeyRing newRing = PGPSecretKeyRing.constructDummyFromPublic(getRing());
+ for (long subKeyId : subKeyIds) {
+ PGPSecretKey key = secRing.getSecretKey(subKeyId);
+ if (key != null) {
+ newRing = PGPSecretKeyRing.insertSecretKey(newRing, key);
+ }
+ }
+
+ return new UncachedKeyRing(newRing);
+
}
} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
index 39d0a2f1d..7394c07c3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
@@ -21,22 +21,18 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
-import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
-import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
-import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
+import org.spongycastle.openpgp.operator.jcajce.CachingDataDecryptorFactory;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyConverter;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
-import org.spongycastle.openpgp.operator.jcajce.NfcSyncPublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
@@ -45,10 +41,10 @@ import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.nio.ByteBuffer;
-import java.util.ArrayList;
+import java.security.PrivateKey;
+import java.security.interfaces.RSAPrivateCrtKey;
import java.util.Date;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
@@ -69,9 +65,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
private PGPPrivateKey mPrivateKey = null;
private int mPrivateKeyState = PRIVATE_KEY_STATE_LOCKED;
- private static int PRIVATE_KEY_STATE_LOCKED = 0;
- private static int PRIVATE_KEY_STATE_UNLOCKED = 1;
- private static int PRIVATE_KEY_STATE_DIVERT_TO_CARD = 2;
+ final private static int PRIVATE_KEY_STATE_LOCKED = 0;
+ final private static int PRIVATE_KEY_STATE_UNLOCKED = 1;
+ final private static int PRIVATE_KEY_STATE_DIVERT_TO_CARD = 2;
CanonicalizedSecretKey(CanonicalizedSecretKeyRing ring, PGPSecretKey key) {
super(ring, key.getPublicKey());
@@ -123,9 +119,10 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
}
public SecretKeyType getSecretKeyType() {
- if (mSecretKey.getS2K() != null && mSecretKey.getS2K().getType() == S2K.GNU_DUMMY_S2K) {
+ S2K s2k = mSecretKey.getS2K();
+ if (s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K) {
// divert to card is special
- if (mSecretKey.getS2K().getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD) {
+ if (s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD) {
return SecretKeyType.DIVERT_TO_CARD;
}
// no matter the exact protection mode, it's some kind of dummy key
@@ -156,9 +153,10 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
*/
public boolean unlock(Passphrase passphrase) throws PgpGeneralException {
// handle keys on OpenPGP cards like they were unlocked
- if (mSecretKey.getS2K() != null
- && mSecretKey.getS2K().getType() == S2K.GNU_DUMMY_S2K
- && mSecretKey.getS2K().getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD) {
+ S2K s2k = mSecretKey.getS2K();
+ if (s2k != null
+ && s2k.getType() == S2K.GNU_DUMMY_S2K
+ && s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD) {
mPrivateKeyState = PRIVATE_KEY_STATE_DIVERT_TO_CARD;
return true;
}
@@ -178,16 +176,6 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
return true;
}
- /**
- * Returns a list of all supported hash algorithms.
- */
- public ArrayList<Integer> getSupportedHashAlgorithms() {
- // TODO: intersection between preferred hash algos of this key and PgpConstants.PREFERRED_HASH_ALGORITHMS
- // choose best algo
-
- return PgpConstants.sPreferredHashAlgorithms;
- }
-
private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo,
Map<ByteBuffer,byte[]> signedHashes) {
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
@@ -206,7 +194,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
public PGPSignatureGenerator getCertSignatureGenerator(Map<ByteBuffer, byte[]> signedHashes) {
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(
- PgpConstants.CERTIFY_HASH_ALGO, signedHashes);
+ PgpSecurityConstants.CERTIFY_HASH_ALGO, signedHashes);
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
throw new PrivateKeyNotUnlockedException();
@@ -265,20 +253,42 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
}
}
- public PublicKeyDataDecryptorFactory getDecryptorFactory(CryptoInputParcel cryptoInput) {
+ public CachingDataDecryptorFactory getCachingDecryptorFactory(CryptoInputParcel cryptoInput) {
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
throw new PrivateKeyNotUnlockedException();
}
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
- return new NfcSyncPublicKeyDataDecryptorFactoryBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- cryptoInput.getCryptoData()
- );
+ return new CachingDataDecryptorFactory(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME,
+ cryptoInput.getCryptoData());
} else {
- return new JcePublicKeyDataDecryptorFactoryBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey);
+ return new CachingDataDecryptorFactory(
+ new JcePublicKeyDataDecryptorFactoryBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey),
+ cryptoInput.getCryptoData());
+ }
+ }
+
+ // For use only in card export; returns the secret key in Chinese Remainder Theorem format.
+ public RSAPrivateCrtKey getCrtSecretKey() throws PgpGeneralException {
+ if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
+ throw new PgpGeneralException("Cannot get secret key attributes while key is locked.");
}
+
+ if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
+ throw new PgpGeneralException("Cannot get secret key attributes of divert-to-card key.");
+ }
+
+ JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter();
+ PrivateKey retVal;
+ try {
+ retVal = keyConverter.getPrivateKey(mPrivateKey);
+ } catch (PGPException e) {
+ throw new PgpGeneralException("Error converting private key!", e);
+ }
+
+ return (RSAPrivateCrtKey)retVal;
}
public byte[] getIv() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
index 825795cc6..77977b691 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
@@ -22,6 +22,7 @@ import android.text.TextUtils;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
+import java.io.Serializable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -91,7 +92,7 @@ public abstract class KeyRing {
return userIdString;
}
- public static class UserId {
+ public static class UserId implements Serializable {
public final String name;
public final String email;
public final String comment;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java
new file mode 100644
index 000000000..c4525e5cd
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpDecryptionResultBuilder.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 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.pgp;
+
+import org.openintents.openpgp.OpenPgpDecryptionResult;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class OpenPgpDecryptionResultBuilder {
+
+ // builder
+ private boolean mInsecure = false;
+ private boolean mEncrypted = false;
+
+ public void setInsecure(boolean insecure) {
+ this.mInsecure = insecure;
+ }
+
+ public void setEncrypted(boolean encrypted) {
+ this.mEncrypted = encrypted;
+ }
+
+ public OpenPgpDecryptionResult build() {
+ OpenPgpDecryptionResult result = new OpenPgpDecryptionResult();
+
+ if (mInsecure) {
+ Log.d(Constants.TAG, "RESULT_INSECURE");
+ result.setResult(OpenPgpDecryptionResult.RESULT_INSECURE);
+ return result;
+ }
+
+ if (mEncrypted) {
+ Log.d(Constants.TAG, "RESULT_ENCRYPTED");
+ result.setResult(OpenPgpDecryptionResult.RESULT_ENCRYPTED);
+ } else {
+ Log.d(Constants.TAG, "RESULT_NOT_ENCRYPTED");
+ result.setResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED);
+ }
+
+ return result;
+ }
+
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java
index ed4715681..9d059b58f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OpenPgpSignatureResultBuilder.java
@@ -30,7 +30,6 @@ import java.util.ArrayList;
*/
public class OpenPgpSignatureResultBuilder {
// OpenPgpSignatureResult
- private boolean mSignatureOnly = false;
private String mPrimaryUserId;
private ArrayList<String> mUserIds = new ArrayList<>();
private long mKeyId;
@@ -42,10 +41,7 @@ public class OpenPgpSignatureResultBuilder {
private boolean mIsSignatureKeyCertified = false;
private boolean mIsKeyRevoked = false;
private boolean mIsKeyExpired = false;
-
- public void setSignatureOnly(boolean signatureOnly) {
- this.mSignatureOnly = signatureOnly;
- }
+ private boolean mInsecure = false;
public void setPrimaryUserId(String userId) {
this.mPrimaryUserId = userId;
@@ -63,6 +59,10 @@ public class OpenPgpSignatureResultBuilder {
this.mValidSignature = validSignature;
}
+ public void setInsecure(boolean insecure) {
+ this.mInsecure = insecure;
+ }
+
public void setSignatureKeyCertified(boolean isSignatureKeyCertified) {
this.mIsSignatureKeyCertified = isSignatureKeyCertified;
}
@@ -87,6 +87,10 @@ public class OpenPgpSignatureResultBuilder {
return mValidSignature;
}
+ public boolean isInsecure() {
+ return mInsecure;
+ }
+
public void initValid(CanonicalizedPublicKeyRing signingRing,
CanonicalizedPublicKey signingKey) {
setSignatureAvailable(true);
@@ -109,47 +113,50 @@ public class OpenPgpSignatureResultBuilder {
}
public OpenPgpSignatureResult build() {
- if (mSignatureAvailable) {
- OpenPgpSignatureResult result = new OpenPgpSignatureResult();
- result.setSignatureOnly(mSignatureOnly);
-
- // valid sig!
- if (mKnownKey) {
- if (mValidSignature) {
- result.setKeyId(mKeyId);
- result.setPrimaryUserId(mPrimaryUserId);
- result.setUserIds(mUserIds);
-
- if (mIsKeyRevoked) {
- Log.d(Constants.TAG, "SIGNATURE_KEY_REVOKED");
- result.setStatus(OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED);
- } else if (mIsKeyExpired) {
- Log.d(Constants.TAG, "SIGNATURE_KEY_EXPIRED");
- result.setStatus(OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED);
- } else if (mIsSignatureKeyCertified) {
- Log.d(Constants.TAG, "SIGNATURE_SUCCESS_CERTIFIED");
- result.setStatus(OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED);
- } else {
- Log.d(Constants.TAG, "SIGNATURE_SUCCESS_UNCERTIFIED");
- result.setStatus(OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED);
- }
- } else {
- Log.d(Constants.TAG, "Error! Invalid signature.");
- result.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR);
- }
- } else {
- result.setKeyId(mKeyId);
-
- Log.d(Constants.TAG, "SIGNATURE_KEY_MISSING");
- result.setStatus(OpenPgpSignatureResult.SIGNATURE_KEY_MISSING);
- }
+ OpenPgpSignatureResult result = new OpenPgpSignatureResult();
+ if (!mSignatureAvailable) {
+ Log.d(Constants.TAG, "RESULT_NO_SIGNATURE");
+ result.setResult(OpenPgpSignatureResult.RESULT_NO_SIGNATURE);
return result;
- } else {
- Log.d(Constants.TAG, "no signature found!");
+ }
+
+ if (!mKnownKey) {
+ result.setKeyId(mKeyId);
- return null;
+ Log.d(Constants.TAG, "RESULT_KEY_MISSING");
+ result.setResult(OpenPgpSignatureResult.RESULT_KEY_MISSING);
+ return result;
+ }
+
+ if (!mValidSignature) {
+ Log.d(Constants.TAG, "RESULT_INVALID_SIGNATURE");
+ result.setResult(OpenPgpSignatureResult.RESULT_INVALID_SIGNATURE);
+ return result;
}
+
+ result.setKeyId(mKeyId);
+ result.setPrimaryUserId(mPrimaryUserId);
+ result.setUserIds(mUserIds);
+
+ if (mIsKeyRevoked) {
+ Log.d(Constants.TAG, "RESULT_INVALID_KEY_REVOKED");
+ result.setResult(OpenPgpSignatureResult.RESULT_INVALID_KEY_REVOKED);
+ } else if (mIsKeyExpired) {
+ Log.d(Constants.TAG, "RESULT_INVALID_KEY_EXPIRED");
+ result.setResult(OpenPgpSignatureResult.RESULT_INVALID_KEY_EXPIRED);
+ } else if (mInsecure) {
+ Log.d(Constants.TAG, "RESULT_INVALID_INSECURE");
+ result.setResult(OpenPgpSignatureResult.RESULT_INVALID_INSECURE);
+ } else if (mIsSignatureKeyCertified) {
+ Log.d(Constants.TAG, "RESULT_VALID_CONFIRMED");
+ result.setResult(OpenPgpSignatureResult.RESULT_VALID_CONFIRMED);
+ } else {
+ Log.d(Constants.TAG, "RESULT_VALID_UNCONFIRMED");
+ result.setResult(OpenPgpSignatureResult.RESULT_VALID_UNCONFIRMED);
+ }
+
+ return result;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConstants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConstants.java
deleted file mode 100644
index f739b1e6d..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConstants.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2015 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.pgp;
-
-import org.spongycastle.bcpg.CompressionAlgorithmTags;
-import org.spongycastle.bcpg.HashAlgorithmTags;
-import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
-
-import java.util.ArrayList;
-
-public class PgpConstants {
-
- public static ArrayList<Integer> sPreferredSymmetricAlgorithms = new ArrayList<>();
- public static ArrayList<Integer> sPreferredHashAlgorithms = new ArrayList<>();
- public static ArrayList<Integer> sPreferredCompressionAlgorithms = new ArrayList<>();
-
- // TODO: use hashmaps for contains in O(1) and intersections!
-
- /*
- * Most preferred is first
- * These arrays are written as preferred algorithms into the keys on creation.
- * Other implementations may choose to honor this selection.
- *
- * These lists also define the only algorithms which are used in OpenKeychain.
- * We do not support algorithms such as MD5
- */
- static {
- sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.AES_256);
- sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.AES_192);
- sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.AES_128);
- sPreferredSymmetricAlgorithms.add(SymmetricKeyAlgorithmTags.TWOFISH);
-
- // NOTE: some implementations do not support SHA512, thus we choose SHA256 as default (Mailvelope?)
- sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA256);
- sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA512);
- sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA384);
- sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA224);
- sPreferredHashAlgorithms.add(HashAlgorithmTags.SHA1);
- sPreferredHashAlgorithms.add(HashAlgorithmTags.RIPEMD160);
-
- /*
- * Prefer ZIP
- * "ZLIB provides no benefit over ZIP and is more malleable"
- * - (OpenPGP WG mailinglist: "[openpgp] Intent to deprecate: Insecure primitives")
- * BZIP2: very slow
- */
- sPreferredCompressionAlgorithms.add(CompressionAlgorithmTags.ZIP);
- sPreferredCompressionAlgorithms.add(CompressionAlgorithmTags.ZLIB);
- sPreferredCompressionAlgorithms.add(CompressionAlgorithmTags.BZIP2);
- }
-
- public static final int CERTIFY_HASH_ALGO = HashAlgorithmTags.SHA256;
-
- /*
- * Note: s2kcount is a number between 0 and 0xff that controls the
- * number of times to iterate the password hash before use. More
- * iterations are useful against offline attacks, as it takes more
- * time to check each password. The actual number of iterations is
- * rather complex, and also depends on the hash function in use.
- * Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give
- * you more iterations. As a rough rule of thumb, when using
- * SHA256 as the hashing function, 0x10 gives you about 64
- * iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0,
- * or about 1 million iterations. The maximum you can go to is
- * 0xff, or about 2 million iterations.
- * from http://kbsriram.com/2013/01/generating-rsa-keys-with-bouncycastle.html
- *
- * Bouncy Castle default: 0x60
- * kbsriram proposes: 0xc0
- * OpenKeychain: 0x90
- */
- public static final int SECRET_KEY_ENCRYPTOR_S2K_COUNT = 0x90;
- public static final int SECRET_KEY_ENCRYPTOR_HASH_ALGO = HashAlgorithmTags.SHA256;
- public static final int SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO = SymmetricKeyAlgorithmTags.AES_256;
- public static final int SECRET_KEY_SIGNATURE_HASH_ALGO = HashAlgorithmTags.SHA256;
- // NOTE: only SHA1 is supported for key checksum calculations in OpenPGP,
- // see http://tools.ietf.org/html/rfc488 0#section-5.5.3
- public static final int SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO = HashAlgorithmTags.SHA1;
-
- public static interface OpenKeychainSymmetricKeyAlgorithmTags extends SymmetricKeyAlgorithmTags {
- public static final int USE_PREFERRED = -1;
- }
-
- public static interface OpenKeychainHashAlgorithmTags extends HashAlgorithmTags {
- public static final int USE_PREFERRED = -1;
- }
-
- public static interface OpenKeychainCompressionAlgorithmTags extends CompressionAlgorithmTags {
- public static final int USE_PREFERRED = -1;
- }
-
- public static int[] getAsArray(ArrayList<Integer> list) {
- int[] array = new int[list.size()];
- for (int i = 0; i < list.size(); i++) {
- array[i] = list.get(i);
- }
- return array;
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyInputParcel.java
new file mode 100644
index 000000000..a6d65688c
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyInputParcel.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.pgp;
+
+import java.util.HashSet;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class PgpDecryptVerifyInputParcel implements Parcelable {
+
+ private Uri mInputUri;
+ private Uri mOutputUri;
+ private byte[] mInputBytes;
+
+ private boolean mAllowSymmetricDecryption;
+ private HashSet<Long> mAllowedKeyIds;
+ private boolean mDecryptMetadataOnly;
+ private byte[] mDetachedSignature;
+ private String mRequiredSignerFingerprint;
+ private boolean mSignedLiteralData;
+
+ public PgpDecryptVerifyInputParcel() {
+ }
+
+ public PgpDecryptVerifyInputParcel(Uri inputUri, Uri outputUri) {
+ mInputUri = inputUri;
+ mOutputUri = outputUri;
+ }
+
+ public PgpDecryptVerifyInputParcel(byte[] inputBytes) {
+ mInputBytes = inputBytes;
+ }
+
+ PgpDecryptVerifyInputParcel(Parcel source) {
+ // we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable
+ mInputUri = source.readParcelable(getClass().getClassLoader());
+ mOutputUri = source.readParcelable(getClass().getClassLoader());
+ mInputBytes = source.createByteArray();
+
+ mAllowSymmetricDecryption = source.readInt() != 0;
+ mAllowedKeyIds = (HashSet<Long>) source.readSerializable();
+ mDecryptMetadataOnly = source.readInt() != 0;
+ mDetachedSignature = source.createByteArray();
+ mRequiredSignerFingerprint = source.readString();
+ mSignedLiteralData = source.readInt() != 0;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mInputUri, 0);
+ dest.writeParcelable(mOutputUri, 0);
+ dest.writeByteArray(mInputBytes);
+
+ dest.writeInt(mAllowSymmetricDecryption ? 1 : 0);
+ dest.writeSerializable(mAllowedKeyIds);
+ dest.writeInt(mDecryptMetadataOnly ? 1 : 0);
+ dest.writeByteArray(mDetachedSignature);
+ dest.writeString(mRequiredSignerFingerprint);
+ dest.writeInt(mSignedLiteralData ? 1 : 0);
+ }
+
+ byte[] getInputBytes() {
+ return mInputBytes;
+ }
+
+ Uri getInputUri() {
+ return mInputUri;
+ }
+
+ Uri getOutputUri() {
+ return mOutputUri;
+ }
+
+ boolean isAllowSymmetricDecryption() {
+ return mAllowSymmetricDecryption;
+ }
+
+ public PgpDecryptVerifyInputParcel setAllowSymmetricDecryption(boolean allowSymmetricDecryption) {
+ mAllowSymmetricDecryption = allowSymmetricDecryption;
+ return this;
+ }
+
+ HashSet<Long> getAllowedKeyIds() {
+ return mAllowedKeyIds;
+ }
+
+ public PgpDecryptVerifyInputParcel setAllowedKeyIds(HashSet<Long> allowedKeyIds) {
+ mAllowedKeyIds = allowedKeyIds;
+ return this;
+ }
+
+ boolean isDecryptMetadataOnly() {
+ return mDecryptMetadataOnly;
+ }
+
+ public PgpDecryptVerifyInputParcel setDecryptMetadataOnly(boolean decryptMetadataOnly) {
+ mDecryptMetadataOnly = decryptMetadataOnly;
+ return this;
+ }
+
+ byte[] getDetachedSignature() {
+ return mDetachedSignature;
+ }
+
+ public PgpDecryptVerifyInputParcel setDetachedSignature(byte[] detachedSignature) {
+ mDetachedSignature = detachedSignature;
+ return this;
+ }
+
+ String getRequiredSignerFingerprint() {
+ return mRequiredSignerFingerprint;
+ }
+
+ public PgpDecryptVerifyInputParcel setRequiredSignerFingerprint(String requiredSignerFingerprint) {
+ mRequiredSignerFingerprint = requiredSignerFingerprint;
+ return this;
+ }
+
+ boolean isSignedLiteralData() {
+ return mSignedLiteralData;
+ }
+
+ public PgpDecryptVerifyInputParcel setSignedLiteralData(boolean signedLiteralData) {
+ mSignedLiteralData = signedLiteralData;
+ return this;
+ }
+
+ public static final Creator<PgpDecryptVerifyInputParcel> CREATOR = new Creator<PgpDecryptVerifyInputParcel>() {
+ public PgpDecryptVerifyInputParcel createFromParcel(final Parcel source) {
+ return new PgpDecryptVerifyInputParcel(source);
+ }
+
+ public PgpDecryptVerifyInputParcel[] newArray(final int size) {
+ return new PgpDecryptVerifyInputParcel[size];
+ }
+ };
+
+}
+
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java
index f6580b85a..dd30156f9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java
@@ -19,15 +19,19 @@
package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
+import android.support.annotation.NonNull;
import android.webkit.MimeTypeMap;
+import org.openintents.openpgp.OpenPgpDecryptionResult;
import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.bcpg.ArmoredInputStream;
import org.spongycastle.openpgp.PGPCompressedData;
+import org.spongycastle.openpgp.PGPDataValidationException;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.spongycastle.openpgp.PGPEncryptedDataList;
import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPKeyValidationException;
import org.spongycastle.openpgp.PGPLiteralData;
import org.spongycastle.openpgp.PGPOnePassSignature;
import org.spongycastle.openpgp.PGPOnePassSignatureList;
@@ -39,12 +43,13 @@ import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
-import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
+import org.spongycastle.openpgp.operator.jcajce.CachingDataDecryptorFactory;
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.NfcSyncPublicKeyDataDecryptorFactoryBuilder;
+import org.spongycastle.util.encoders.DecoderException;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Constants.key;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.BaseOperation;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
@@ -57,6 +62,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
@@ -65,154 +71,99 @@ import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.net.URLConnection;
import java.security.SignatureException;
import java.util.Date;
import java.util.Iterator;
-import java.util.Set;
-/**
- * This class uses a Builder pattern!
- */
-public class PgpDecryptVerify extends BaseOperation {
-
- private InputData mData;
- private OutputStream mOutStream;
-
- private boolean mAllowSymmetricDecryption;
- private Set<Long> mAllowedKeyIds;
- private boolean mDecryptMetadataOnly;
- private byte[] mDetachedSignature;
- private String mRequiredSignerFingerprint;
- private boolean mSignedLiteralData;
-
- protected PgpDecryptVerify(Builder builder) {
- super(builder.mContext, builder.mProviderHelper, builder.mProgressable);
-
- // private Constructor can only be called from Builder
- this.mData = builder.mData;
- this.mOutStream = builder.mOutStream;
-
- this.mAllowSymmetricDecryption = builder.mAllowSymmetricDecryption;
- this.mAllowedKeyIds = builder.mAllowedKeyIds;
- this.mDecryptMetadataOnly = builder.mDecryptMetadataOnly;
- this.mDetachedSignature = builder.mDetachedSignature;
- this.mSignedLiteralData = builder.mSignedLiteralData;
- this.mRequiredSignerFingerprint = builder.mRequiredSignerFingerprint;
- }
+public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInputParcel> {
- public static class Builder {
- // mandatory parameter
- private Context mContext;
- private ProviderHelper mProviderHelper;
- private InputData mData;
-
- // optional
- private OutputStream mOutStream = null;
- private Progressable mProgressable = null;
- private boolean mAllowSymmetricDecryption = true;
- private Set<Long> mAllowedKeyIds = null;
- private boolean mDecryptMetadataOnly = false;
- private byte[] mDetachedSignature = null;
- private String mRequiredSignerFingerprint = null;
- private boolean mSignedLiteralData = false;
-
- public Builder(Context context, ProviderHelper providerHelper,
- Progressable progressable,
- InputData data, OutputStream outStream) {
- mContext = context;
- mProviderHelper = providerHelper;
- mProgressable = progressable;
- mData = data;
- mOutStream = outStream;
- }
+ public PgpDecryptVerifyOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
+ super(context, providerHelper, progressable);
+ }
- /**
- * This is used when verifying signed literals to check that they are signed with
- * the required key
- */
- public Builder setRequiredSignerFingerprint(String fingerprint) {
- mRequiredSignerFingerprint = fingerprint;
- return this;
- }
+ /** Decrypts and/or verifies data based on parameters of PgpDecryptVerifyInputParcel. */
+ @NonNull
+ public DecryptVerifyResult execute(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput) {
+ InputData inputData;
+ OutputStream outputStream;
- /**
- * This is to force a mode where the message is just the signature key id and
- * then a literal data packet; used in Keybase.io proofs
- */
- public Builder setSignedLiteralData(boolean signedLiteralData) {
- mSignedLiteralData = signedLiteralData;
- return this;
+ if (input.getInputBytes() != null) {
+ byte[] inputBytes = input.getInputBytes();
+ inputData = new InputData(new ByteArrayInputStream(inputBytes), inputBytes.length);
+ } else {
+ try {
+ InputStream inputStream = mContext.getContentResolver().openInputStream(input.getInputUri());
+ long inputSize = FileHelper.getFileSize(mContext, input.getInputUri(), 0);
+ inputData = new InputData(inputStream, inputSize);
+ } catch (FileNotFoundException e) {
+ Log.e(Constants.TAG, "Input URI could not be opened: " + input.getInputUri(), e);
+ OperationLog log = new OperationLog();
+ log.add(LogType.MSG_DC_ERROR_INPUT, 1);
+ return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ }
}
- public Builder setAllowSymmetricDecryption(boolean allowSymmetricDecryption) {
- mAllowSymmetricDecryption = allowSymmetricDecryption;
- return this;
+ if (input.getOutputUri() == null) {
+ outputStream = new ByteArrayOutputStream();
+ } else {
+ try {
+ outputStream = mContext.getContentResolver().openOutputStream(input.getOutputUri());
+ } catch (FileNotFoundException e) {
+ Log.e(Constants.TAG, "Output URI could not be opened: " + input.getOutputUri(), e);
+ OperationLog log = new OperationLog();
+ log.add(LogType.MSG_DC_ERROR_IO, 1);
+ return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ }
}
- /**
- * Allow these key ids alone for decryption.
- * This means only ciphertexts encrypted for one of these private key can be decrypted.
- */
- public Builder setAllowedKeyIds(Set<Long> allowedKeyIds) {
- mAllowedKeyIds = allowedKeyIds;
- return this;
+ DecryptVerifyResult result = executeInternal(input, cryptoInput, inputData, outputStream);
+ if (outputStream instanceof ByteArrayOutputStream) {
+ byte[] outputData = ((ByteArrayOutputStream) outputStream).toByteArray();
+ result.setOutputBytes(outputData);
}
- /**
- * If enabled, the actual decryption/verification of the content will not be executed.
- * The metadata only will be decrypted and returned.
- */
- public Builder setDecryptMetadataOnly(boolean decryptMetadataOnly) {
- mDecryptMetadataOnly = decryptMetadataOnly;
- return this;
- }
+ return result;
- /**
- * If detachedSignature != null, it will be used exclusively to verify the signature
- */
- public Builder setDetachedSignature(byte[] detachedSignature) {
- mDetachedSignature = detachedSignature;
- return this;
- }
+ }
- public PgpDecryptVerify build() {
- return new PgpDecryptVerify(this);
- }
+ @NonNull
+ public DecryptVerifyResult execute(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput,
+ InputData inputData, OutputStream outputStream) {
+ return executeInternal(input, cryptoInput, inputData, outputStream);
}
- /**
- * Decrypts and/or verifies data based on parameters of class
- */
- public DecryptVerifyResult execute(CryptoInputParcel cryptoInput) {
+ @NonNull
+ private DecryptVerifyResult executeInternal(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput,
+ InputData inputData, OutputStream outputStream) {
try {
- if (mDetachedSignature != null) {
+ if (input.getDetachedSignature() != null) {
Log.d(Constants.TAG, "Detached signature present, verifying with this signature only");
- return verifyDetachedSignature(mData.getInputStream(), 0);
+ return verifyDetachedSignature(input, inputData, outputStream, 0);
} else {
// automatically works with PGP ascii armor and PGP binary
- InputStream in = PGPUtil.getDecoderStream(mData.getInputStream());
+ InputStream in = PGPUtil.getDecoderStream(inputData.getInputStream());
if (in instanceof ArmoredInputStream) {
ArmoredInputStream aIn = (ArmoredInputStream) in;
// it is ascii armored
Log.d(Constants.TAG, "ASCII Armor Header Line: " + aIn.getArmorHeaderLine());
- if (mSignedLiteralData) {
- return verifySignedLiteralData(aIn, 0);
+ if (input.isSignedLiteralData()) {
+ return verifySignedLiteralData(input, aIn, outputStream, 0);
} else if (aIn.isClearText()) {
// a cleartext signature, verify it with the other method
- return verifyCleartextSignature(aIn, 0);
+ return verifyCleartextSignature(aIn, outputStream, 0);
} else {
// else: ascii armored encryption! go on...
- return decryptVerify(cryptoInput, in, 0);
+ return decryptVerify(input, cryptoInput, in, outputStream, 0);
}
} else {
- return decryptVerify(cryptoInput, in, 0);
+ return decryptVerify(input, cryptoInput, in, outputStream, 0);
}
}
} catch (PGPException e) {
@@ -220,6 +171,13 @@ public class PgpDecryptVerify extends BaseOperation {
OperationLog log = new OperationLog();
log.add(LogType.MSG_DC_ERROR_PGP_EXCEPTION, 1);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ } catch (DecoderException | ArrayIndexOutOfBoundsException e) {
+ // these can happen if assumptions in JcaPGPObjectFactory.nextObject() aren't
+ // fulfilled, so we need to catch them here to handle this gracefully
+ Log.d(Constants.TAG, "data error", e);
+ OperationLog log = new OperationLog();
+ log.add(LogType.MSG_DC_ERROR_IO, 1);
+ return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
} catch (IOException e) {
Log.d(Constants.TAG, "IOException", e);
OperationLog log = new OperationLog();
@@ -228,10 +186,10 @@ public class PgpDecryptVerify extends BaseOperation {
}
}
- /**
- * Verify Keybase.io style signed literal data
- */
- private DecryptVerifyResult verifySignedLiteralData(InputStream in, int indent)
+ /**Verify signed plaintext data (PGP/INLINE). */
+ @NonNull
+ private DecryptVerifyResult verifySignedLiteralData(
+ PgpDecryptVerifyInputParcel input, InputStream in, OutputStream out, int indent)
throws IOException, PGPException {
OperationLog log = new OperationLog();
log.add(LogType.MSG_VL, indent);
@@ -282,9 +240,9 @@ public class PgpDecryptVerify extends BaseOperation {
}
String fingerprint = KeyFormattingUtils.convertFingerprintToHex(signingRing.getFingerprint());
- if (!(mRequiredSignerFingerprint.equals(fingerprint))) {
+ if (!(input.getRequiredSignerFingerprint().equals(fingerprint))) {
log.add(LogType.MSG_VL_ERROR_MISSING_KEY, indent);
- Log.d(Constants.TAG, "Fingerprint mismatch; wanted " + mRequiredSignerFingerprint +
+ Log.d(Constants.TAG, "Fingerprint mismatch; wanted " + input.getRequiredSignerFingerprint() +
" got " + fingerprint + "!");
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
@@ -316,7 +274,7 @@ public class PgpDecryptVerify extends BaseOperation {
int length;
byte[] buffer = new byte[1 << 16];
while ((length = dataIn.read(buffer)) > 0) {
- mOutStream.write(buffer, 0, length);
+ out.write(buffer, 0, length);
signature.update(buffer, 0, length);
}
@@ -326,10 +284,6 @@ public class PgpDecryptVerify extends BaseOperation {
PGPSignatureList signatureList = (PGPSignatureList) pgpF.nextObject();
PGPSignature messageSignature = signatureList.get(signatureIndex);
- // these are not cleartext signatures!
- // TODO: what about binary signatures?
- signatureResultBuilder.setSignatureOnly(false);
-
// Verify signature and check binding signatures
boolean validSignature = signature.verify(messageSignature);
if (validSignature) {
@@ -341,8 +295,8 @@ public class PgpDecryptVerify extends BaseOperation {
OpenPgpSignatureResult signatureResult = signatureResultBuilder.build();
- if (signatureResult.getStatus() != OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED
- && signatureResult.getStatus() != OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED) {
+ if (signatureResult.getResult() != OpenPgpSignatureResult.RESULT_VALID_CONFIRMED
+ && signatureResult.getResult() != OpenPgpSignatureResult.RESULT_VALID_UNCONFIRMED) {
log.add(LogType.MSG_VL_ERROR_INTEGRITY_CHECK, indent);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
@@ -352,19 +306,22 @@ public class PgpDecryptVerify extends BaseOperation {
log.add(LogType.MSG_VL_OK, indent);
// Return a positive result, with metadata and verification info
- DecryptVerifyResult result =
- new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
+ DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
result.setSignatureResult(signatureResult);
+ result.setDecryptionResult(
+ new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED));
return result;
}
- /**
- * Decrypt and/or verifies binary or ascii armored pgp
- */
- private DecryptVerifyResult decryptVerify(CryptoInputParcel cryptoInput,
- InputStream in, int indent) throws IOException, PGPException {
+ /** Decrypt and/or verify binary or ascii armored pgp data. */
+ @NonNull
+ private DecryptVerifyResult decryptVerify(
+ PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput,
+ InputStream in, OutputStream out, int indent) throws IOException, PGPException {
+ OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
+ OpenPgpDecryptionResultBuilder decryptionResultBuilder = new OpenPgpDecryptionResultBuilder();
OperationLog log = new OperationLog();
log.add(LogType.MSG_DC, indent);
@@ -384,7 +341,7 @@ public class PgpDecryptVerify extends BaseOperation {
}
if (enc == null) {
- log.add(LogType.MSG_DC_ERROR_INVALID_SIGLIST, indent);
+ log.add(LogType.MSG_DC_ERROR_INVALID_DATA, indent);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
@@ -419,6 +376,7 @@ public class PgpDecryptVerify extends BaseOperation {
}
Passphrase passphrase = null;
+ boolean skippedDisallowedKey = false;
// go through all objects and find one we can decrypt
while (it.hasNext()) {
@@ -451,29 +409,31 @@ public class PgpDecryptVerify extends BaseOperation {
log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1);
continue;
}
- // get subkey which has been used for this encryption packet
- secretEncryptionKey = secretKeyRing.getSecretKey(subKeyId);
- if (secretEncryptionKey == null) {
- // should actually never happen, so no need to be more specific.
- log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1);
- continue;
- }
// allow only specific keys for decryption?
- if (mAllowedKeyIds != null) {
+ if (input.getAllowedKeyIds() != null) {
long masterKeyId = secretKeyRing.getMasterKeyId();
Log.d(Constants.TAG, "encData.getKeyID(): " + subKeyId);
- Log.d(Constants.TAG, "mAllowedKeyIds: " + mAllowedKeyIds);
+ Log.d(Constants.TAG, "mAllowedKeyIds: " + input.getAllowedKeyIds());
Log.d(Constants.TAG, "masterKeyId: " + masterKeyId);
- if (!mAllowedKeyIds.contains(masterKeyId)) {
+ if (!input.getAllowedKeyIds().contains(masterKeyId)) {
// this key is in our db, but NOT allowed!
// continue with the next packet in the while loop
+ skippedDisallowedKey = true;
log.add(LogType.MSG_DC_ASKIP_NOT_ALLOWED, indent + 1);
continue;
}
}
+ // get subkey which has been used for this encryption packet
+ secretEncryptionKey = secretKeyRing.getSecretKey(subKeyId);
+ if (secretEncryptionKey == null) {
+ // should actually never happen, so no need to be more specific.
+ log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1);
+ continue;
+ }
+
/* secret key exists in database and is allowed! */
asymmetricPacketFound = true;
@@ -499,10 +459,17 @@ public class PgpDecryptVerify extends BaseOperation {
log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1);
return new DecryptVerifyResult(log,
RequiredInputParcel.createRequiredDecryptPassphrase(
- secretKeyRing.getMasterKeyId(), secretEncryptionKey.getKeyId()));
+ secretKeyRing.getMasterKeyId(), secretEncryptionKey.getKeyId()),
+ cryptoInput);
}
}
+ // check for insecure encryption key
+ if ( ! PgpSecurityConstants.isSecureKey(secretEncryptionKey)) {
+ log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
+ decryptionResultBuilder.setInsecure(true);
+ }
+
// break out of while, only decrypt the first packet where we have a key
break;
@@ -511,7 +478,7 @@ public class PgpDecryptVerify extends BaseOperation {
log.add(LogType.MSG_DC_SYM, indent);
- if (!mAllowSymmetricDecryption) {
+ if (!input.isAllowSymmetricDecryption()) {
log.add(LogType.MSG_DC_SYM_SKIP, indent + 1);
continue;
}
@@ -527,12 +494,24 @@ public class PgpDecryptVerify extends BaseOperation {
// if no passphrase is given, return here
// indicating that a passphrase is missing!
if (!cryptoInput.hasPassphrase()) {
- log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1);
- return new DecryptVerifyResult(log,
- RequiredInputParcel.createRequiredSymmetricPassphrase());
- }
- passphrase = cryptoInput.getPassphrase();
+ try {
+ passphrase = getCachedPassphrase(key.symmetric);
+ log.add(LogType.MSG_DC_PASS_CACHED, indent + 1);
+ } catch (PassphraseCacheInterface.NoSecretKeyException e) {
+ // nvm
+ }
+
+ if (passphrase == null) {
+ log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1);
+ return new DecryptVerifyResult(log,
+ RequiredInputParcel.createRequiredSymmetricPassphrase(),
+ cryptoInput);
+ }
+
+ } else {
+ passphrase = cryptoInput.getPassphrase();
+ }
// break out of while, only decrypt the first packet
break;
@@ -568,7 +547,14 @@ public class PgpDecryptVerify extends BaseOperation {
digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
passphrase.getCharArray());
- clear = encryptedDataSymmetric.getDataStream(decryptorFactory);
+ try {
+ clear = encryptedDataSymmetric.getDataStream(decryptorFactory);
+ } catch (PGPDataValidationException e) {
+ log.add(LogType.MSG_DC_ERROR_SYM_PASSPHRASE, indent +1);
+ return new DecryptVerifyResult(log,
+ RequiredInputParcel.createRequiredSymmetricPassphrase(), cryptoInput);
+ }
+
encryptedData = encryptedDataSymmetric;
symmetricEncryptionAlgo = encryptedDataSymmetric.getSymmetricAlgorithm(decryptorFactory);
@@ -590,35 +576,60 @@ public class PgpDecryptVerify extends BaseOperation {
currentProgress += 2;
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
- try {
- PublicKeyDataDecryptorFactory decryptorFactory
- = secretEncryptionKey.getDecryptorFactory(cryptoInput);
- clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
+ CachingDataDecryptorFactory decryptorFactory
+ = secretEncryptionKey.getCachingDecryptorFactory(cryptoInput);
+
+ // special case: if the decryptor does not have a session key cached for this encrypted
+ // data, and can't actually decrypt on its own, return a pending intent
+ if (!decryptorFactory.canDecrypt()
+ && !decryptorFactory.hasCachedSessionData(encryptedDataAsymmetric)) {
- symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);
- } catch (NfcSyncPublicKeyDataDecryptorFactoryBuilder.NfcInteractionNeeded e) {
log.add(LogType.MSG_DC_PENDING_NFC, indent + 1);
return new DecryptVerifyResult(log, RequiredInputParcel.createNfcDecryptOperation(
- e.encryptedSessionKey, secretEncryptionKey.getKeyId()
- ));
+ secretEncryptionKey.getRing().getMasterKeyId(),
+ secretEncryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0]
+ ),
+ cryptoInput);
+
+ }
+
+ try {
+ clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
+ } catch (PGPKeyValidationException | ArrayIndexOutOfBoundsException e) {
+ log.add(LogType.MSG_DC_ERROR_CORRUPT_DATA, indent + 1);
+ return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
+
+ symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);
+
+ cryptoInput.addCryptoData(decryptorFactory.getCachedSessionKeys());
+
encryptedData = encryptedDataAsymmetric;
} else {
- // If we didn't find any useful data, error out
+ // there wasn't even any useful data
+ if (!anyPacketFound) {
+ log.add(LogType.MSG_DC_ERROR_NO_DATA, indent + 1);
+ return new DecryptVerifyResult(DecryptVerifyResult.RESULT_NO_DATA, log);
+ }
+ // there was data but key wasn't allowed
+ if (skippedDisallowedKey) {
+ log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1);
+ return new DecryptVerifyResult(DecryptVerifyResult.RESULT_KEY_DISALLOWED, log);
+ }
// no packet has been found where we have the corresponding secret key in our db
- log.add(
- anyPacketFound ? LogType.MSG_DC_ERROR_NO_KEY : LogType.MSG_DC_ERROR_NO_DATA, indent + 1);
+ log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
+ decryptionResultBuilder.setEncrypted(true);
- // Warn about old encryption algorithms!
- if (!PgpConstants.sPreferredSymmetricAlgorithms.contains(symmetricEncryptionAlgo)) {
- log.add(LogType.MSG_DC_OLD_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
+ // Check for insecure encryption algorithms!
+ if (!PgpSecurityConstants.isSecureSymmetricAlgorithm(symmetricEncryptionAlgo)) {
+ log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
+ decryptionResultBuilder.setInsecure(true);
}
JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear);
Object dataChunk = plainFact.nextObject();
- OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
int signatureIndex = -1;
CanonicalizedPublicKeyRing signingRing = null;
CanonicalizedPublicKey signingKey = null;
@@ -682,6 +693,13 @@ public class PgpDecryptVerify extends BaseOperation {
}
}
+ // check for insecure signing key
+ // TODO: checks on signingRing ?
+ if (signingKey != null && ! PgpSecurityConstants.isSecureKey(signingKey)) {
+ log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
+ signatureResultBuilder.setInsecure(true);
+ }
+
dataChunk = plainFact.nextObject();
}
@@ -700,17 +718,12 @@ public class PgpDecryptVerify extends BaseOperation {
PGPLiteralData literalData = (PGPLiteralData) dataChunk;
- // reported size may be null if partial packets are involved (highly unlikely though)
- Long originalSize = literalData.getDataLengthIfAvailable();
-
String originalFilename = literalData.getFileName();
String mimeType = null;
if (literalData.getFormat() == PGPLiteralData.TEXT
|| literalData.getFormat() == PGPLiteralData.UTF8) {
mimeType = "text/plain";
} else {
- // TODO: better would be: https://github.com/open-keychain/open-keychain/issues/753
-
// try to guess from file ending
String extension = MimeTypeMap.getFileExtensionFromUrl(originalFilename);
if (extension != null) {
@@ -718,19 +731,10 @@ public class PgpDecryptVerify extends BaseOperation {
mimeType = mime.getMimeTypeFromExtension(extension);
}
if (mimeType == null) {
- mimeType = URLConnection.guessContentTypeFromName(originalFilename);
- }
- if (mimeType == null) {
- mimeType = "*/*";
+ mimeType = "application/octet-stream";
}
}
- metadata = new OpenPgpMetadata(
- originalFilename,
- mimeType,
- literalData.getModificationTime().getTime(),
- originalSize == null ? 0 : originalSize);
-
if (!"".equals(originalFilename)) {
log.add(LogType.MSG_DC_CLEAR_META_FILE, indent + 1, originalFilename);
}
@@ -738,20 +742,31 @@ public class PgpDecryptVerify extends BaseOperation {
mimeType);
log.add(LogType.MSG_DC_CLEAR_META_TIME, indent + 1,
new Date(literalData.getModificationTime().getTime()).toString());
- if (originalSize != null) {
- log.add(LogType.MSG_DC_CLEAR_META_SIZE, indent + 1,
- Long.toString(originalSize));
- } else {
- log.add(LogType.MSG_DC_CLEAR_META_SIZE_UNKNOWN, indent + 1);
- }
// return here if we want to decrypt the metadata only
- if (mDecryptMetadataOnly) {
+ if (input.isDecryptMetadataOnly()) {
+
+ // this operation skips the entire stream to find the data length!
+ Long originalSize = literalData.findDataLength();
+
+ if (originalSize != null) {
+ log.add(LogType.MSG_DC_CLEAR_META_SIZE, indent + 1,
+ Long.toString(originalSize));
+ } else {
+ log.add(LogType.MSG_DC_CLEAR_META_SIZE_UNKNOWN, indent + 1);
+ }
+
+ metadata = new OpenPgpMetadata(
+ originalFilename,
+ mimeType,
+ literalData.getModificationTime().getTime(),
+ originalSize == null ? 0 : originalSize);
+
log.add(LogType.MSG_DC_OK_META_ONLY, indent);
DecryptVerifyResult result =
new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
result.setCharset(charset);
- result.setDecryptMetadata(metadata);
+ result.setDecryptionMetadata(metadata);
return result;
}
@@ -769,13 +784,13 @@ public class PgpDecryptVerify extends BaseOperation {
InputStream dataIn = literalData.getInputStream();
long alreadyWritten = 0;
- long wholeSize = mData.getSize() - mData.getStreamPosition();
+ long wholeSize = 0; // TODO inputData.getSize() - inputData.getStreamPosition();
int length;
byte[] buffer = new byte[1 << 16];
while ((length = dataIn.read(buffer)) > 0) {
- Log.d(Constants.TAG, "read bytes: " + length);
- if (mOutStream != null) {
- mOutStream.write(buffer, 0, length);
+ // Log.d(Constants.TAG, "read bytes: " + length);
+ if (out != null) {
+ out.write(buffer, 0, length);
}
// update signature buffer if signature is also present
@@ -795,6 +810,12 @@ public class PgpDecryptVerify extends BaseOperation {
// TODO: slow annealing to fake a progress?
}
+ metadata = new OpenPgpMetadata(
+ originalFilename,
+ mimeType,
+ literalData.getModificationTime().getTime(),
+ alreadyWritten);
+
if (signature != null) {
updateProgress(R.string.progress_verifying_signature, 90, 100);
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent);
@@ -802,11 +823,9 @@ public class PgpDecryptVerify extends BaseOperation {
PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
PGPSignature messageSignature = signatureList.get(signatureIndex);
- // these are not cleartext signatures!
// TODO: what about binary signatures?
- signatureResultBuilder.setSignatureOnly(false);
- // Verify signature and check binding signatures
+ // Verify signature
boolean validSignature = signature.verify(messageSignature);
if (validSignature) {
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1);
@@ -814,10 +833,10 @@ public class PgpDecryptVerify extends BaseOperation {
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
}
- // Don't allow verification of old hash algorithms!
- if (!PgpConstants.sPreferredHashAlgorithms.contains(signature.getHashAlgorithm())) {
- validSignature = false;
- log.add(LogType.MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO, indent + 1);
+ // check for insecure hash algorithms
+ if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) {
+ log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1);
+ signatureResultBuilder.setInsecure(true);
}
signatureResultBuilder.setValidSignature(validSignature);
@@ -844,8 +863,8 @@ public class PgpDecryptVerify extends BaseOperation {
// The MDC packet can be stripped by an attacker!
Log.d(Constants.TAG, "MDC fail");
if (!signatureResultBuilder.isValidSignature()) {
- log.add(LogType.MSG_DC_ERROR_INTEGRITY_MISSING, indent);
- return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ log.add(LogType.MSG_DC_INSECURE_MDC_MISSING, indent);
+ decryptionResultBuilder.setInsecure(true);
}
}
@@ -854,11 +873,12 @@ public class PgpDecryptVerify extends BaseOperation {
log.add(LogType.MSG_DC_OK, indent);
// Return a positive result, with metadata and verification info
- DecryptVerifyResult result =
- new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
- result.setDecryptMetadata(metadata);
+ DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
+ result.setCachedCryptoInputParcel(cryptoInput);
result.setSignatureResult(signatureResultBuilder.build());
result.setCharset(charset);
+ result.setDecryptionResult(decryptionResultBuilder.build());
+ result.setDecryptionMetadata(metadata);
return result;
}
@@ -870,14 +890,13 @@ public class PgpDecryptVerify extends BaseOperation {
* The method is heavily based on
* pg/src/main/java/org/spongycastle/openpgp/examples/ClearSignedFileProcessor.java
*/
+ @NonNull
private DecryptVerifyResult verifyCleartextSignature(
- ArmoredInputStream aIn, int indent) throws IOException, PGPException {
+ ArmoredInputStream aIn, OutputStream outputStream, int indent) throws IOException, PGPException {
OperationLog log = new OperationLog();
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
- // cleartext signatures are never encrypted ;)
- signatureResultBuilder.setSignatureOnly(true);
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -901,8 +920,9 @@ public class PgpDecryptVerify extends BaseOperation {
out.close();
byte[] clearText = out.toByteArray();
- if (mOutStream != null) {
- mOutStream.write(clearText);
+ if (outputStream != null) {
+ outputStream.write(clearText);
+ outputStream.close();
}
updateProgress(R.string.progress_processing_signature, 60, 100);
@@ -910,11 +930,11 @@ public class PgpDecryptVerify extends BaseOperation {
PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject();
if (sigList == null) {
- log.add(LogType.MSG_DC_ERROR_INVALID_SIGLIST, 0);
+ log.add(LogType.MSG_DC_ERROR_INVALID_DATA, 0);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
- PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder);
+ PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder, log, indent);
if (signature != null) {
try {
@@ -946,10 +966,10 @@ public class PgpDecryptVerify extends BaseOperation {
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
}
- // Don't allow verification of old hash algorithms!
- if (!PgpConstants.sPreferredHashAlgorithms.contains(signature.getHashAlgorithm())) {
- validSignature = false;
- log.add(LogType.MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO, indent + 1);
+ // check for insecure hash algorithms
+ if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) {
+ log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1);
+ signatureResultBuilder.setInsecure(true);
}
signatureResultBuilder.setValidSignature(validSignature);
@@ -964,22 +984,31 @@ public class PgpDecryptVerify extends BaseOperation {
log.add(LogType.MSG_DC_OK, indent);
+ OpenPgpMetadata metadata = new OpenPgpMetadata(
+ "",
+ "text/plain",
+ -1,
+ clearText.length);
+
DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
result.setSignatureResult(signatureResultBuilder.build());
+ result.setDecryptionResult(
+ new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED));
+ result.setDecryptionMetadata(metadata);
return result;
}
- private DecryptVerifyResult verifyDetachedSignature(InputStream in, int indent)
+ @NonNull
+ private DecryptVerifyResult verifyDetachedSignature(
+ PgpDecryptVerifyInputParcel input, InputData inputData, OutputStream out, int indent)
throws IOException, PGPException {
OperationLog log = new OperationLog();
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
- // detached signatures are never encrypted
- signatureResultBuilder.setSignatureOnly(true);
updateProgress(R.string.progress_processing_signature, 0, 100);
- InputStream detachedSigIn = new ByteArrayInputStream(mDetachedSignature);
+ InputStream detachedSigIn = new ByteArrayInputStream(input.getDetachedSignature());
detachedSigIn = PGPUtil.getDecoderStream(detachedSigIn);
JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(detachedSigIn);
@@ -993,23 +1022,24 @@ public class PgpDecryptVerify extends BaseOperation {
} else if (o instanceof PGPSignatureList) {
sigList = (PGPSignatureList) o;
} else {
- log.add(LogType.MSG_DC_ERROR_INVALID_SIGLIST, 0);
+ log.add(LogType.MSG_DC_ERROR_INVALID_DATA, 0);
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
}
- PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder);
+ PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder, log, indent);
if (signature != null) {
updateProgress(R.string.progress_reading_data, 60, 100);
ProgressScaler progressScaler = new ProgressScaler(mProgressable, 60, 90, 100);
long alreadyWritten = 0;
- long wholeSize = mData.getSize() - mData.getStreamPosition();
+ long wholeSize = inputData.getSize() - inputData.getStreamPosition();
int length;
byte[] buffer = new byte[1 << 16];
+ InputStream in = inputData.getInputStream();
while ((length = in.read(buffer)) > 0) {
- if (mOutStream != null) {
- mOutStream.write(buffer, 0, length);
+ if (out != null) {
+ out.write(buffer, 0, length);
}
// update signature buffer if signature is also present
@@ -1030,9 +1060,6 @@ public class PgpDecryptVerify extends BaseOperation {
updateProgress(R.string.progress_verifying_signature, 90, 100);
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent);
- // these are not cleartext signatures!
- signatureResultBuilder.setSignatureOnly(false);
-
// Verify signature and check binding signatures
boolean validSignature = signature.verify();
if (validSignature) {
@@ -1041,10 +1068,10 @@ public class PgpDecryptVerify extends BaseOperation {
log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
}
- // Don't allow verification of old hash algorithms!
- if (!PgpConstants.sPreferredHashAlgorithms.contains(signature.getHashAlgorithm())) {
- validSignature = false;
- log.add(LogType.MSG_DC_ERROR_UNSUPPORTED_HASH_ALGO, indent + 1);
+ // check for insecure hash algorithms
+ if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) {
+ log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1);
+ signatureResultBuilder.setInsecure(true);
}
signatureResultBuilder.setValidSignature(validSignature);
@@ -1056,10 +1083,15 @@ public class PgpDecryptVerify extends BaseOperation {
DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
result.setSignatureResult(signatureResultBuilder.build());
+ result.setDecryptionResult(
+ new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED));
return result;
}
- private PGPSignature processPGPSignatureList(PGPSignatureList sigList, OpenPgpSignatureResultBuilder signatureResultBuilder) throws PGPException {
+ private PGPSignature processPGPSignatureList(
+ PGPSignatureList sigList, OpenPgpSignatureResultBuilder signatureResultBuilder,
+ OperationLog log, int indent)
+ throws PGPException {
CanonicalizedPublicKeyRing signingRing = null;
CanonicalizedPublicKey signingKey = null;
int signatureIndex = -1;
@@ -1100,6 +1132,13 @@ public class PgpDecryptVerify extends BaseOperation {
}
}
+ // check for insecure signing key
+ // TODO: checks on signingRing ?
+ if (signingKey != null && ! PgpSecurityConstants.isSecureKey(signingKey)) {
+ log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
+ signatureResultBuilder.setInsecure(true);
+ }
+
return signature;
}
@@ -1195,12 +1234,6 @@ public class PgpDecryptVerify extends BaseOperation {
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;
+ return nl.getBytes();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
index d8b86a18c..e8d1d3111 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
@@ -21,6 +21,8 @@ package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -31,6 +33,7 @@ import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.security.SecureRandom;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PgpHelper {
@@ -52,9 +55,6 @@ public class PgpHelper {
* <p/>
* TODO: Does this really help on flash storage?
*
- * @param context
- * @param progressable
- * @param file
* @throws IOException
*/
public static void deleteFileSecurely(Context context, Progressable progressable, File file)
@@ -78,4 +78,67 @@ public class PgpHelper {
raf.close();
file.delete();
}
+
+ /**
+ * Fixing broken PGP MESSAGE Strings coming from GMail/AOSP Mail
+ */
+ public static String fixPgpMessage(String message) {
+ // windows newline -> unix newline
+ message = message.replaceAll("\r\n", "\n");
+ // Mac OS before X newline -> unix newline
+ message = message.replaceAll("\r", "\n");
+
+ // remove whitespaces before newline
+ message = message.replaceAll(" +\n", "\n");
+ // only two consecutive newlines are allowed
+ message = message.replaceAll("\n\n+", "\n\n");
+
+ // replace non breakable spaces
+ message = message.replaceAll("\\xa0", " ");
+
+ return message;
+ }
+
+ /**
+ * Fixing broken PGP SIGNED MESSAGE Strings coming from GMail/AOSP Mail
+ */
+ public static String fixPgpCleartextSignature(CharSequence input) {
+ if (!TextUtils.isEmpty(input)) {
+ String text = input.toString();
+
+ // windows newline -> unix newline
+ text = text.replaceAll("\r\n", "\n");
+ // Mac OS before X newline -> unix newline
+ text = text.replaceAll("\r", "\n");
+
+ return text;
+ } else {
+ return null;
+ }
+ }
+
+ public static String getPgpContent(@NonNull CharSequence input) {
+ Log.dEscaped(Constants.TAG, "input: " + input);
+
+ Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(input);
+ if (matcher.matches()) {
+ String text = matcher.group(1);
+ text = fixPgpMessage(text);
+
+ Log.dEscaped(Constants.TAG, "input fixed: " + text);
+ return text;
+ } else {
+ matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(input);
+ if (matcher.matches()) {
+ String text = matcher.group(1);
+ text = fixPgpCleartextSignature(text);
+
+ Log.dEscaped(Constants.TAG, "input fixed: " + text);
+ return text;
+ } else {
+ return null;
+ }
+ }
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index 89db378a9..6f156c201 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -18,9 +18,11 @@
package org.sufficientlysecure.keychain.pgp;
+import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.bcpg.sig.Features;
import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.bcpg.sig.RevocationReasonTags;
import org.spongycastle.jce.spec.ElGamalParameterSpec;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyFlags;
@@ -45,8 +47,10 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded;
+import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
@@ -59,6 +63,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcKeyToCardOperationsBuilder;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
@@ -68,6 +73,7 @@ import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.IOException;
import java.math.BigInteger;
+import java.nio.ByteBuffer;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
@@ -151,7 +157,7 @@ public class PgpKeyOperation {
}
/** Creates new secret key. */
- private PGPKeyPair createKey(SubkeyAdd add, OperationLog log, int indent) {
+ private PGPKeyPair createKey(SubkeyAdd add, Date creationTime, OperationLog log, int indent) {
try {
// Some safety checks
@@ -249,7 +255,7 @@ public class PgpKeyOperation {
}
// build new key pair
- return new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date());
+ return new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), creationTime);
} catch(NoSuchProviderException | InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
@@ -295,8 +301,10 @@ public class PgpKeyOperation {
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
+ Date creationTime = new Date();
+
subProgressPush(10, 30);
- PGPKeyPair keyPair = createKey(add, log, indent);
+ PGPKeyPair keyPair = createKey(add, creationTime, log, indent);
subProgressPop();
// return null if this failed (an error will already have been logged by createKey)
@@ -308,14 +316,14 @@ public class PgpKeyOperation {
// Build key encrypter and decrypter based on passphrase
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder()
- .build().get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
+ .build().get(PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
- PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO,
- encryptorHashCalc, PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
+ PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO,
+ encryptorHashCalc, PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
- .build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
+ .build().get(PgpSecurityConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
PGPSecretKey masterSecretKey = new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
sha1Calc, true, keyEncryptor);
@@ -323,8 +331,8 @@ public class PgpKeyOperation {
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
subProgressPush(50, 100);
- CryptoInputParcel cryptoInput = new CryptoInputParcel(new Date(), new Passphrase(""));
- return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, cryptoInput, saveParcel, log);
+ CryptoInputParcel cryptoInput = new CryptoInputParcel(creationTime, new Passphrase(""));
+ return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, cryptoInput, saveParcel, log, indent);
} catch (PGPException e) {
log.add(LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
@@ -356,8 +364,8 @@ public class PgpKeyOperation {
*
*/
public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR,
- CryptoInputParcel cryptoInput,
- SaveKeyringParcel saveParcel) {
+ CryptoInputParcel cryptoInput,
+ SaveKeyringParcel saveParcel) {
OperationLog log = new OperationLog();
int indent = 0;
@@ -400,9 +408,61 @@ public class PgpKeyOperation {
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
+ // Ensure we don't have multiple keys for the same slot.
+ boolean hasSign = false;
+ boolean hasEncrypt = false;
+ boolean hasAuth = false;
+ for(SaveKeyringParcel.SubkeyChange change : saveParcel.mChangeSubKeys) {
+ if (change.mMoveKeyToCard) {
+ // If this is a keytocard operation, see if it was completed: look for a hash
+ // matching the given subkey ID in cryptoData.
+ byte[] subKeyId = new byte[8];
+ ByteBuffer buf = ByteBuffer.wrap(subKeyId);
+ buf.putLong(change.mKeyId).rewind();
+
+ byte[] serialNumber = cryptoInput.getCryptoData().get(buf);
+ if (serialNumber != null) {
+ change.mMoveKeyToCard = false;
+ change.mDummyDivert = serialNumber;
+ }
+ }
+
+ if (change.mMoveKeyToCard) {
+ // Pending keytocard operation. Need to make sure that we don't have multiple
+ // subkeys pending for the same slot.
+ CanonicalizedSecretKey wsK = wsKR.getSecretKey(change.mKeyId);
+
+ if ((wsK.canSign() || wsK.canCertify())) {
+ if (hasSign) {
+ log.add(LogType.MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT, indent + 1);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ } else {
+ hasSign = true;
+ }
+ } else if ((wsK.canEncrypt())) {
+ if (hasEncrypt) {
+ log.add(LogType.MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT, indent + 1);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ } else {
+ hasEncrypt = true;
+ }
+ } else if ((wsK.canAuthenticate())) {
+ if (hasAuth) {
+ log.add(LogType.MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT, indent + 1);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ } else {
+ hasAuth = true;
+ }
+ } else {
+ log.add(LogType.MSG_MF_ERROR_INVALID_FLAGS_FOR_KEYTOCARD, indent + 1);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ }
+ }
+ }
+
if (isDummy(masterSecretKey) || saveParcel.isRestrictedOnly()) {
log.add(LogType.MSG_MF_RESTRICTED_MODE, indent);
- return internalRestricted(sKR, saveParcel, log);
+ return internalRestricted(sKR, saveParcel, log, indent + 1);
}
// Do we require a passphrase? If so, pass it along
@@ -410,7 +470,7 @@ public class PgpKeyOperation {
log.add(LogType.MSG_MF_REQUIRE_PASSPHRASE, indent);
return new PgpEditKeyResult(log, RequiredInputParcel.createRequiredSignPassphrase(
masterSecretKey.getKeyID(), masterSecretKey.getKeyID(),
- cryptoInput.getSignatureTime()));
+ cryptoInput.getSignatureTime()), cryptoInput);
}
// read masterKeyFlags, and use the same as before.
@@ -420,7 +480,7 @@ public class PgpKeyOperation {
Date expiryTime = wsKR.getPublicKey().getExpiryTime();
long masterKeyExpiry = expiryTime != null ? expiryTime.getTime() / 1000 : 0L;
- return internal(sKR, masterSecretKey, masterKeyFlags, masterKeyExpiry, cryptoInput, saveParcel, log);
+ return internal(sKR, masterSecretKey, masterKeyFlags, masterKeyExpiry, cryptoInput, saveParcel, log, indent);
}
@@ -428,13 +488,14 @@ public class PgpKeyOperation {
int masterKeyFlags, long masterKeyExpiry,
CryptoInputParcel cryptoInput,
SaveKeyringParcel saveParcel,
- OperationLog log) {
-
- int indent = 1;
+ OperationLog log,
+ int indent) {
NfcSignOperationsBuilder nfcSignOps = new NfcSignOperationsBuilder(
cryptoInput.getSignatureTime(), masterSecretKey.getKeyID(),
masterSecretKey.getKeyID());
+ NfcKeyToCardOperationsBuilder nfcKeyToCardOps = new NfcKeyToCardOperationsBuilder(
+ masterSecretKey.getKeyID());
progress(R.string.progress_modify, 0);
@@ -553,7 +614,8 @@ public class PgpKeyOperation {
PGPSignature cert = generateUserAttributeSignature(
getSignatureGenerator(masterSecretKey, cryptoInput),
cryptoInput.getSignatureTime(),
- masterPrivateKey, masterPublicKey, vector);
+ masterPrivateKey, masterPublicKey, vector,
+ masterKeyFlags, masterKeyExpiry);
modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, vector, cert);
} catch (NfcInteractionNeeded e) {
nfcSignOps.addHash(e.hashToSign, e.hashAlgo);
@@ -743,22 +805,36 @@ public class PgpKeyOperation {
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
- if (change.mDummyStrip || change.mDummyDivert != null) {
+ if (change.mDummyStrip) {
// IT'S DANGEROUS~
// no really, it is. this operation irrevocably removes the private key data from the key
- if (change.mDummyStrip) {
- sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
+ sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
+ } else if (change.mMoveKeyToCard) {
+ if (checkSmartCardCompatibility(sKey, log, indent + 1)) {
+ log.add(LogType.MSG_MF_KEYTOCARD_START, indent + 1,
+ KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
+ nfcKeyToCardOps.addSubkey(change.mKeyId);
} else {
- // the serial number must be 16 bytes in length
- if (change.mDummyDivert.length != 16) {
- log.add(LogType.MSG_MF_ERROR_DIVERT_SERIAL,
- indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
- return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
- }
+ // Appropriate log message already set by checkSmartCardCompatibility
+ return new PgpEditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
+ } else if (change.mDummyDivert != null) {
+ // NOTE: Does this code get executed? Or always handled in internalRestricted?
+ if (change.mDummyDivert.length != 16) {
+ log.add(LogType.MSG_MF_ERROR_DIVERT_SERIAL,
+ indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ }
+ log.add(LogType.MSG_MF_KEYTOCARD_FINISH, indent + 1,
+ KeyFormattingUtils.convertKeyIdToHex(change.mKeyId),
+ Hex.toHexString(change.mDummyDivert, 8, 6));
+ sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(), change.mDummyDivert);
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
}
+
+
// This doesn't concern us any further
if (!change.mRecertify && (change.mExpiry == null && change.mFlags == null)) {
continue;
@@ -822,18 +898,35 @@ public class PgpKeyOperation {
pKey = PGPPublicKey.removeCertification(pKey, sig);
}
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- cryptoInput.getPassphrase().getCharArray());
- PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
- PGPSignature sig = generateSubkeyBindingSignature(
- getSignatureGenerator(masterSecretKey, cryptoInput),
- cryptoInput.getSignatureTime(),
- masterPublicKey, masterPrivateKey, subPrivateKey, pKey, flags, expiry);
+ PGPPrivateKey subPrivateKey;
+ if (!isDivertToCard(sKey)) {
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ cryptoInput.getPassphrase().getCharArray());
+ subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
+ // super special case: subkey is allowed to sign, but isn't available
+ if (subPrivateKey == null) {
+ log.add(LogType.MSG_MF_ERROR_SUB_STRIPPED,
+ indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ }
+ } else {
+ subPrivateKey = null;
+ }
+ try {
+ PGPSignature sig = generateSubkeyBindingSignature(
+ getSignatureGenerator(masterSecretKey, cryptoInput),
+ cryptoInput.getSignatureTime(), masterPublicKey, masterPrivateKey,
+ getSignatureGenerator(sKey, cryptoInput), subPrivateKey,
+ pKey, flags, expiry);
+
+ // generate and add new signature
+ pKey = PGPPublicKey.addCertification(pKey, sig);
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
+ } catch (NfcInteractionNeeded e) {
+ nfcSignOps.addHash(e.hashToSign, e.hashAlgo);
+ }
- // generate and add new signature
- pKey = PGPPublicKey.addCertification(pKey, sig);
- sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
}
subProgressPop();
@@ -884,6 +977,11 @@ public class PgpKeyOperation {
log.add(LogType.MSG_MF_SUBKEY_NEW, indent,
KeyFormattingUtils.getAlgorithmInfo(add.mAlgorithm, add.mKeySize, add.mCurve) );
+ if (isDivertToCard(masterSecretKey)) {
+ log.add(LogType.MSG_MF_ERROR_DIVERT_NEWSUB, indent +1);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ }
+
if (add.mExpiry == null) {
log.add(LogType.MSG_MF_ERROR_NULL_EXPIRY, indent +1);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
@@ -899,7 +997,7 @@ public class PgpKeyOperation {
(i-1) * (100 / saveParcel.mAddSubKeys.size()),
i * (100 / saveParcel.mAddSubKeys.size())
);
- PGPKeyPair keyPair = createKey(add, log, indent);
+ PGPKeyPair keyPair = createKey(add, cryptoInput.getSignatureTime(), log, indent);
subProgressPop();
if (keyPair == null) {
log.add(LogType.MSG_MF_ERROR_PGP, indent +1);
@@ -912,7 +1010,8 @@ public class PgpKeyOperation {
PGPSignature cert = generateSubkeyBindingSignature(
getSignatureGenerator(masterSecretKey, cryptoInput),
cryptoInput.getSignatureTime(),
- masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey,
+ masterPublicKey, masterPrivateKey,
+ getSignatureGenerator(pKey, cryptoInput, false), keyPair.getPrivateKey(), pKey,
add.mFlags, add.mExpiry);
pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
} catch (NfcInteractionNeeded e) {
@@ -922,15 +1021,15 @@ public class PgpKeyOperation {
PGPSecretKey sKey; {
// Build key encrypter and decrypter based on passphrase
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder()
- .build().get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
+ .build().get(PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
- PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
- PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
+ PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
+ PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
cryptoInput.getPassphrase().getCharArray());
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
- .build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
+ .build().get(PgpSecurityConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey, sha1Calc, false, keyEncryptor);
}
@@ -964,6 +1063,26 @@ public class PgpKeyOperation {
indent -= 1;
}
+ // 7. if requested, change PIN and/or Admin PIN on card
+ if (saveParcel.mCardPin != null) {
+ progress(R.string.progress_modify_pin, 90);
+ log.add(LogType.MSG_MF_PIN, indent);
+ indent += 1;
+
+ nfcKeyToCardOps.setPin(saveParcel.mCardPin);
+
+ indent -= 1;
+ }
+ if (saveParcel.mCardAdminPin != null) {
+ progress(R.string.progress_modify_admin_pin, 90);
+ log.add(LogType.MSG_MF_ADMIN_PIN, indent);
+ indent += 1;
+
+ nfcKeyToCardOps.setAdminPin(saveParcel.mCardAdminPin);
+
+ indent -= 1;
+ }
+
} catch (IOException e) {
Log.e(Constants.TAG, "encountered IOException while modifying key", e);
log.add(LogType.MSG_MF_ERROR_ENCODE, indent+1);
@@ -980,9 +1099,19 @@ public class PgpKeyOperation {
progress(R.string.progress_done, 100);
+ if (!nfcSignOps.isEmpty() && !nfcKeyToCardOps.isEmpty()) {
+ log.add(LogType.MSG_MF_ERROR_CONFLICTING_NFC_COMMANDS, indent+1);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ }
+
if (!nfcSignOps.isEmpty()) {
log.add(LogType.MSG_MF_REQUIRE_DIVERT, indent);
- return new PgpEditKeyResult(log, nfcSignOps.build());
+ return new PgpEditKeyResult(log, nfcSignOps.build(), cryptoInput);
+ }
+
+ if (!nfcKeyToCardOps.isEmpty()) {
+ log.add(LogType.MSG_MF_REQUIRE_DIVERT, indent);
+ return new PgpEditKeyResult(log, nfcKeyToCardOps.build(), cryptoInput);
}
log.add(LogType.MSG_MF_SUCCESS, indent);
@@ -995,9 +1124,7 @@ public class PgpKeyOperation {
* otherwise.
*/
private PgpEditKeyResult internalRestricted(PGPSecretKeyRing sKR, SaveKeyringParcel saveParcel,
- OperationLog log) {
-
- int indent = 1;
+ OperationLog log, int indent) {
progress(R.string.progress_modify, 0);
@@ -1042,6 +1169,9 @@ public class PgpKeyOperation {
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
+ log.add(LogType.MSG_MF_KEYTOCARD_FINISH, indent + 1,
+ KeyFormattingUtils.convertKeyIdToHex(change.mKeyId),
+ Hex.toHexString(change.mDummyDivert, 8, 6));
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(), change.mDummyDivert);
}
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
@@ -1076,7 +1206,7 @@ public class PgpKeyOperation {
// add packet with EMPTY notation data (updates old one, but will be stripped later)
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
- PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
+ PgpSecurityConstants.SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
{ // set subpackets
@@ -1103,7 +1233,7 @@ public class PgpKeyOperation {
// add packet with "pin" notation data
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
masterPrivateKey.getPublicKeyPacket().getAlgorithm(),
- PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
+ PgpSecurityConstants.SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
{ // set subpackets
@@ -1150,13 +1280,13 @@ public class PgpKeyOperation {
OperationLog log, int indent) throws PGPException {
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build()
- .get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
+ .get(PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray());
// Build key encryptor based on new passphrase
PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
- PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
- PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
+ PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
+ PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(newPassphrase.getCharArray());
// noinspection unchecked
@@ -1296,22 +1426,27 @@ public class PgpKeyOperation {
static PGPSignatureGenerator getSignatureGenerator(
PGPSecretKey secretKey, CryptoInputParcel cryptoInput) {
- PGPContentSignerBuilder builder;
-
S2K s2k = secretKey.getS2K();
- if (s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K
- && s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD) {
+ boolean isDivertToCard = s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K
+ && s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD;
+
+ return getSignatureGenerator(secretKey.getPublicKey(), cryptoInput, isDivertToCard);
+ }
+
+ static PGPSignatureGenerator getSignatureGenerator(
+ PGPPublicKey pKey, CryptoInputParcel cryptoInput, boolean divertToCard) {
+
+ PGPContentSignerBuilder builder;
+ if (divertToCard) {
// use synchronous "NFC based" SignerBuilder
builder = new NfcSyncPGPContentSignerBuilder(
- secretKey.getPublicKey().getAlgorithm(),
- PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO,
- secretKey.getKeyID(), cryptoInput.getCryptoData())
+ pKey.getAlgorithm(), PgpSecurityConstants.SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO,
+ pKey.getKeyID(), cryptoInput.getCryptoData())
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
} else {
// content signer based on signing key algorithm and chosen hash algorithm
builder = new JcaPGPContentSignerBuilder(
- secretKey.getPublicKey().getAlgorithm(),
- PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
+ pKey.getAlgorithm(), PgpSecurityConstants.SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
}
@@ -1319,11 +1454,9 @@ public class PgpKeyOperation {
}
- private PGPSignature generateUserIdSignature(
- PGPSignatureGenerator sGen, Date creationTime,
- PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary,
- int flags, long expiry)
- throws IOException, PGPException, SignatureException {
+ private static PGPSignatureSubpacketGenerator generateHashedSelfSigSubpackets(
+ Date creationTime, PGPPublicKey pKey, boolean primary, int flags, long expiry
+ ) {
PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
{
@@ -1339,11 +1472,11 @@ public class PgpKeyOperation {
*/
/* non-critical subpackets: */
hashedPacketsGen.setPreferredSymmetricAlgorithms(false,
- PgpConstants.getAsArray(PgpConstants.sPreferredSymmetricAlgorithms));
+ PgpSecurityConstants.PREFERRED_SYMMETRIC_ALGORITHMS);
hashedPacketsGen.setPreferredHashAlgorithms(false,
- PgpConstants.getAsArray(PgpConstants.sPreferredHashAlgorithms));
+ PgpSecurityConstants.PREFERRED_HASH_ALGORITHMS);
hashedPacketsGen.setPreferredCompressionAlgorithms(false,
- PgpConstants.getAsArray(PgpConstants.sPreferredCompressionAlgorithms));
+ PgpSecurityConstants.PREFERRED_COMPRESSION_ALGORITHMS);
hashedPacketsGen.setPrimaryUserID(false, primary);
/* critical subpackets: we consider those important for a modern pgp implementation */
@@ -1357,6 +1490,17 @@ public class PgpKeyOperation {
}
}
+ return hashedPacketsGen;
+ }
+
+ private static PGPSignature generateUserIdSignature(
+ PGPSignatureGenerator sGen, Date creationTime,
+ PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary,
+ int flags, long expiry)
+ throws IOException, PGPException, SignatureException {
+
+ PGPSignatureSubpacketGenerator hashedPacketsGen =
+ generateHashedSelfSigSubpackets(creationTime, pKey, primary, flags, expiry);
sGen.setHashedSubpackets(hashedPacketsGen.generate());
sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
return sGen.generateCertification(userId, pKey);
@@ -1365,15 +1509,12 @@ public class PgpKeyOperation {
private static PGPSignature generateUserAttributeSignature(
PGPSignatureGenerator sGen, Date creationTime,
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey,
- PGPUserAttributeSubpacketVector vector)
+ PGPUserAttributeSubpacketVector vector,
+ int flags, long expiry)
throws IOException, PGPException, SignatureException {
- PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
- {
- /* critical subpackets: we consider those important for a modern pgp implementation */
- hashedPacketsGen.setSignatureCreationTime(true, creationTime);
- }
-
+ PGPSignatureSubpacketGenerator hashedPacketsGen =
+ generateHashedSelfSigSubpackets(creationTime, pKey, false, flags, expiry);
sGen.setHashedSubpackets(hashedPacketsGen.generate());
sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
return sGen.generateCertification(vector, pKey);
@@ -1385,6 +1526,9 @@ public class PgpKeyOperation {
throws IOException, PGPException, SignatureException {
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ // we use the tag NO_REASON since gnupg does not care about the tag while verifying
+ // signatures with a revoked key, the warning is the same
+ subHashedPacketsGen.setRevocationReason(true, RevocationReasonTags.NO_REASON, "");
subHashedPacketsGen.setSignatureCreationTime(true, creationTime);
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
sGen.init(PGPSignature.CERTIFICATION_REVOCATION, masterPrivateKey);
@@ -1397,6 +1541,9 @@ public class PgpKeyOperation {
throws IOException, PGPException, SignatureException {
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ // we use the tag NO_REASON since gnupg does not care about the tag while verifying
+ // signatures with a revoked key, the warning is the same
+ subHashedPacketsGen.setRevocationReason(true, RevocationReasonTags.NO_REASON, "");
subHashedPacketsGen.setSignatureCreationTime(true, creationTime);
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
// Generate key revocation or subkey revocation, depending on master/subkey-ness
@@ -1412,7 +1559,8 @@ public class PgpKeyOperation {
static PGPSignature generateSubkeyBindingSignature(
PGPSignatureGenerator sGen, Date creationTime,
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
- PGPPrivateKey subPrivateKey, PGPPublicKey pKey, int flags, long expiry)
+ PGPSignatureGenerator subSigGen, PGPPrivateKey subPrivateKey, PGPPublicKey pKey,
+ int flags, long expiry)
throws IOException, PGPException, SignatureException {
PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
@@ -1422,10 +1570,6 @@ public class PgpKeyOperation {
// cross-certify signing keys
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
subHashedPacketsGen.setSignatureCreationTime(false, creationTime);
- PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- pKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- PGPSignatureGenerator subSigGen = new PGPSignatureGenerator(signerBuilder);
subSigGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
subSigGen.setHashedSubpackets(subHashedPacketsGen.generate());
PGPSignature certification = subSigGen.generateCertification(masterPublicKey, pKey);
@@ -1471,14 +1615,39 @@ public class PgpKeyOperation {
private static boolean isDummy(PGPSecretKey secretKey) {
S2K s2k = secretKey.getS2K();
- return s2k.getType() == S2K.GNU_DUMMY_S2K
- && s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY;
+ return s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K
+ && s2k.getProtectionMode() != S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD;
}
private static boolean isDivertToCard(PGPSecretKey secretKey) {
S2K s2k = secretKey.getS2K();
- return s2k.getType() == S2K.GNU_DUMMY_S2K
+ return s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K
&& s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD;
}
+ private static boolean checkSmartCardCompatibility(PGPSecretKey key, OperationLog log, int indent) {
+ PGPPublicKey publicKey = key.getPublicKey();
+ int algorithm = publicKey.getAlgorithm();
+ if (algorithm != PublicKeyAlgorithmTags.RSA_ENCRYPT &&
+ algorithm != PublicKeyAlgorithmTags.RSA_SIGN &&
+ algorithm != PublicKeyAlgorithmTags.RSA_GENERAL) {
+ log.add(LogType.MSG_MF_ERROR_BAD_NFC_ALGO, indent + 1);
+ return false;
+ }
+
+ // Key size must be 2048
+ int keySize = publicKey.getBitStrength();
+ if (keySize != 2048) {
+ log.add(LogType.MSG_MF_ERROR_BAD_NFC_SIZE, indent + 1);
+ return false;
+ }
+
+ // Secret key parts must be available
+ if (isDivertToCard(key) || isDummy(key)) {
+ log.add(LogType.MSG_MF_ERROR_BAD_NFC_STRIPPED, indent + 1);
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSecurityConstants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSecurityConstants.java
new file mode 100644
index 000000000..3fa549946
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSecurityConstants.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2015 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.pgp;
+
+import org.spongycastle.asn1.nist.NISTNamedCurves;
+import org.spongycastle.bcpg.CompressionAlgorithmTags;
+import org.spongycastle.bcpg.HashAlgorithmTags;
+import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
+import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
+
+import java.util.HashSet;
+
+/**
+ * NIST requirements for 2011-2030 (http://www.keylength.com/en/4/):
+ * - RSA: 2048 bit
+ * - ECC: 224 bit
+ * - Symmetric: 3TDEA
+ * - Digital Signature (hash A): SHA-224 - SHA-512
+ *
+ * Extreme Decisions for Yahoo's End-to-End:
+ * https://github.com/yahoo/end-to-end/issues/31
+ * https://gist.github.com/coruus/68a8c65571e2b4225a69
+ */
+public class PgpSecurityConstants {
+
+ /**
+ * Whitelist of accepted symmetric encryption algorithms
+ * all other algorithms are rejected with OpenPgpDecryptionResult.RESULT_INSECURE
+ */
+ private static HashSet<Integer> sSymmetricAlgorithmsWhitelist = new HashSet<>();
+ static {
+ // General remarks: We try to keep the whitelist short to reduce attack surface
+ // TODO: block IDEA?: Bad key schedule (weak keys), implementation difficulties (easy to make errors)
+ sSymmetricAlgorithmsWhitelist.add(SymmetricKeyAlgorithmTags.IDEA);
+ sSymmetricAlgorithmsWhitelist.add(SymmetricKeyAlgorithmTags.TRIPLE_DES); // a MUST in RFC
+ sSymmetricAlgorithmsWhitelist.add(SymmetricKeyAlgorithmTags.CAST5); // default in many gpg, pgp versions, 128 bit key
+ // BLOWFISH: Twofish is the successor
+ // SAFER: not used widely
+ // DES: < 128 bit security
+ sSymmetricAlgorithmsWhitelist.add(SymmetricKeyAlgorithmTags.AES_128);
+ sSymmetricAlgorithmsWhitelist.add(SymmetricKeyAlgorithmTags.AES_192);
+ sSymmetricAlgorithmsWhitelist.add(SymmetricKeyAlgorithmTags.AES_256);
+ sSymmetricAlgorithmsWhitelist.add(SymmetricKeyAlgorithmTags.TWOFISH); // 128 bit
+ // CAMELLIA_128: not used widely
+ // CAMELLIA_192: not used widely
+ // CAMELLIA_256: not used widely
+ }
+
+ public static boolean isSecureSymmetricAlgorithm(int id) {
+ return sSymmetricAlgorithmsWhitelist.contains(id);
+ }
+
+ /**
+ * Whitelist of accepted hash algorithms
+ * all other algorithms are rejected with OpenPgpSignatureResult.RESULT_INSECURE
+ *
+ * coorus:
+ * Implementations SHOULD use SHA-512 for RSA or DSA signatures. They SHOULD NOT use SHA-384.
+ * ((cite to affine padding attacks; unproven status of RSA-PKCSv15))
+ *
+ * Implementations MUST NOT sign SHA-224 hashes. They SHOULD NOT accept signatures over SHA-224 hashes.
+ * ((collision resistance of 112-bits))
+ * Implementations SHOULD NOT sign SHA-256 hashes. They MUST NOT default to signing SHA-256 hashes.
+ */
+ private static HashSet<Integer> sHashAlgorithmsWhitelist = new HashSet<>();
+ static {
+ // MD5: broken
+ // SHA1: broken
+ // RIPEMD160: same security properties as SHA1
+ // DOUBLE_SHA: not used widely
+ // MD2: not used widely
+ // TIGER_192: not used widely
+ // HAVAL_5_160: not used widely
+ sHashAlgorithmsWhitelist.add(HashAlgorithmTags.SHA256); // compatibility for old Mailvelope versions
+ sHashAlgorithmsWhitelist.add(HashAlgorithmTags.SHA384);
+ sHashAlgorithmsWhitelist.add(HashAlgorithmTags.SHA512);
+ // SHA224: Not used widely, Yahoo argues against it
+ }
+
+ public static boolean isSecureHashAlgorithm(int id) {
+ return sHashAlgorithmsWhitelist.contains(id);
+ }
+
+ /**
+ * Whitelist of accepted asymmetric algorithms in switch statement
+ * all other algorithms are rejected with OpenPgpSignatureResult.RESULT_INSECURE or
+ * OpenPgpDecryptionResult.RESULT_INSECURE
+ *
+ * coorus:
+ * Implementations MUST NOT accept, or treat any signature as valid, by an RSA key with
+ * bitlength less than 1023 bits.
+ * Implementations MUST NOT accept any RSA keys with bitlength less than 2047 bits after January 1, 2016.
+ */
+ private static HashSet<String> sCurveWhitelist = new HashSet<>();
+ static {
+ sCurveWhitelist.add(NISTNamedCurves.getOID("P-256").getId());
+ sCurveWhitelist.add(NISTNamedCurves.getOID("P-384").getId());
+ sCurveWhitelist.add(NISTNamedCurves.getOID("P-521").getId());
+ }
+
+ public static boolean isSecureKey(CanonicalizedPublicKey key) {
+ switch (key.getAlgorithm()) {
+ case PublicKeyAlgorithmTags.RSA_GENERAL: {
+ return (key.getBitStrength() >= 2048);
+ }
+ // RSA_ENCRYPT, RSA_SIGN: deprecated in RFC 4880, use RSA_GENERAL with key flags
+ case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: {
+ return (key.getBitStrength() >= 2048);
+ }
+ case PublicKeyAlgorithmTags.DSA: {
+ return (key.getBitStrength() >= 2048);
+ }
+ case PublicKeyAlgorithmTags.ECDH:
+ case PublicKeyAlgorithmTags.ECDSA: {
+ return PgpSecurityConstants.sCurveWhitelist.contains(key.getCurveOid());
+ }
+ // ELGAMAL_GENERAL: deprecated in RFC 4880, use ELGAMAL_ENCRYPT
+ // DIFFIE_HELLMAN: unsure
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * These array is written as a list of preferred encryption algorithms into keys created by us.
+ * Other implementations may choose to honor this selection.
+ * (Most preferred is first)
+ *
+ * REASON: See corresponding whitelist. AES received most cryptanalysis over the years
+ * and is still secure!
+ */
+ public static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
+ SymmetricKeyAlgorithmTags.AES_256,
+ SymmetricKeyAlgorithmTags.AES_192,
+ SymmetricKeyAlgorithmTags.AES_128,
+ };
+
+ /**
+ * These array is written as a list of preferred hash algorithms into keys created by us.
+ * Other implementations may choose to honor this selection.
+ * (Most preferred is first)
+ *
+ * REASON: See corresponding whitelist. If possible use SHA-512, this is state of the art!
+ */
+ public static final int[] PREFERRED_HASH_ALGORITHMS = new int[]{
+ HashAlgorithmTags.SHA512,
+ };
+
+ /**
+ * These array is written as a list of preferred compression algorithms into keys created by us.
+ * Other implementations may choose to honor this selection.
+ * (Most preferred is first)
+ *
+ * REASON: See DEFAULT_COMPRESSION_ALGORITHM
+ */
+ public static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[]{
+ CompressionAlgorithmTags.ZIP,
+ };
+
+ /**
+ * Hash algorithm used to certify public keys
+ */
+ public static final int CERTIFY_HASH_ALGO = HashAlgorithmTags.SHA512;
+
+
+ /**
+ * Always use AES-256! We always ignore the preferred encryption algos of the recipient!
+ *
+ * coorus:
+ * Implementations SHOULD ignore the symmetric algorithm preferences of a recipient's public key;
+ * in particular, implementations MUST NOT choose an algorithm forbidden by this
+ * document because a recipient prefers it.
+ *
+ * NEEDCITE downgrade attacks on TLS, other protocols
+ */
+ public static final int DEFAULT_SYMMETRIC_ALGORITHM = SymmetricKeyAlgorithmTags.AES_256;
+
+ public interface OpenKeychainSymmetricKeyAlgorithmTags extends SymmetricKeyAlgorithmTags {
+ int USE_DEFAULT = -1;
+ }
+
+ /**
+ * Always use SHA-512! We always ignore the preferred hash algos of the recipient!
+ *
+ * coorus:
+ * Implementations MUST ignore the hash algorithm preferences of a recipient when signing
+ * a message to a recipient. The difficulty of forging a signature under a given key,
+ * using generic attacks on hash functions, is the difficulty of the weakest hash signed by that key.
+ *
+ * Implementations MUST default to using SHA-512 for RSA signatures,
+ *
+ * and either SHA-512 or the matched instance of SHA-2 for ECDSA signatures.
+ * TODO: Ed25519
+ * CITE: zooko's hash function table CITE: distinguishers on SHA-256
+ */
+ public static final int DEFAULT_HASH_ALGORITHM = HashAlgorithmTags.SHA512;
+
+ public interface OpenKeychainHashAlgorithmTags extends HashAlgorithmTags {
+ int USE_DEFAULT = -1;
+ }
+
+ /**
+ * Compression is disabled by default.
+ *
+ * The default compression algorithm is only used if explicitly enabled in the activity's
+ * overflow menu or via the OpenPGP API's extra OpenPgpApi.EXTRA_ENABLE_COMPRESSION
+ *
+ * REASON: Enabling compression can lead to a sidechannel. Consider a voting that is done via
+ * OpenPGP. Compression can lead to different ciphertext lengths based on the user's voting.
+ * This has happened in a voting done by Wikipedia (Google it).
+ *
+ * ZLIB: the format provides no benefits over DEFLATE, and is more malleable
+ * BZIP2: very slow
+ */
+ public static final int DEFAULT_COMPRESSION_ALGORITHM = CompressionAlgorithmTags.ZIP;
+
+ public interface OpenKeychainCompressionAlgorithmTags extends CompressionAlgorithmTags {
+ int USE_DEFAULT = -1;
+ }
+
+ /**
+ * Note: s2kcount is a number between 0 and 0xff that controls the
+ * number of times to iterate the password hash before use. More
+ * iterations are useful against offline attacks, as it takes more
+ * time to check each password. The actual number of iterations is
+ * rather complex, and also depends on the hash function in use.
+ * Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give
+ * you more iterations. As a rough rule of thumb, when using
+ * SHA256 as the hashing function, 0x10 gives you about 64
+ * iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0,
+ * or about 1 million iterations. The maximum you can go to is
+ * 0xff, or about 2 million iterations.
+ * from http://kbsriram.com/2013/01/generating-rsa-keys-with-bouncycastle.html
+ *
+ * Bouncy Castle default: 0x60
+ * kbsriram proposes: 0xc0
+ * Yahoo's End-to-End: 96=0x60 (65536 iterations) (https://github.com/yahoo/end-to-end/blob/master/src/javascript/crypto/e2e/openpgp/keyring.js)
+ */
+ public static final int SECRET_KEY_ENCRYPTOR_S2K_COUNT = 0x90;
+ public static final int SECRET_KEY_ENCRYPTOR_HASH_ALGO = HashAlgorithmTags.SHA512;
+ public static final int SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO = SymmetricKeyAlgorithmTags.AES_256;
+ public static final int SECRET_KEY_BINDING_SIGNATURE_HASH_ALGO = HashAlgorithmTags.SHA512;
+ // NOTE: only SHA1 is supported for key checksum calculations in OpenPGP,
+ // see http://tools.ietf.org/html/rfc488 0#section-5.5.3
+ public static final int SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO = HashAlgorithmTags.SHA1;
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java
index fd3c4910c..36d1a07cb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java
@@ -20,13 +20,8 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.util.Passphrase;
-import java.nio.ByteBuffer;
-import java.util.Date;
-import java.util.Map;
-
import android.os.Parcel;
import android.os.Parcelable;
@@ -35,19 +30,20 @@ public class PgpSignEncryptInputParcel implements Parcelable {
protected String mVersionHeader = null;
protected boolean mEnableAsciiArmorOutput = false;
- protected int mCompressionId = CompressionAlgorithmTags.UNCOMPRESSED;
+ protected int mCompressionAlgorithm = CompressionAlgorithmTags.UNCOMPRESSED;
protected long[] mEncryptionMasterKeyIds = null;
protected Passphrase mSymmetricPassphrase = null;
- protected int mSymmetricEncryptionAlgorithm = PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED;
+ protected int mSymmetricEncryptionAlgorithm = PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT;
protected long mSignatureMasterKeyId = Constants.key.none;
protected Long mSignatureSubKeyId = null;
- protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED;
+ protected int mSignatureHashAlgorithm = PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT;
protected long mAdditionalEncryptId = Constants.key.none;
protected boolean mFailOnMissingEncryptionKeyIds = false;
protected String mCharset;
protected boolean mCleartextSignature;
protected boolean mDetachedSignature = false;
protected boolean mHiddenRecipients = false;
+ protected boolean mIntegrityProtected = true;
public PgpSignEncryptInputParcel() {
@@ -60,7 +56,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
// we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable
mVersionHeader = source.readString();
mEnableAsciiArmorOutput = source.readInt() == 1;
- mCompressionId = source.readInt();
+ mCompressionAlgorithm = source.readInt();
mEncryptionMasterKeyIds = source.createLongArray();
mSymmetricPassphrase = source.readParcelable(loader);
mSymmetricEncryptionAlgorithm = source.readInt();
@@ -73,6 +69,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
mCleartextSignature = source.readInt() == 1;
mDetachedSignature = source.readInt() == 1;
mHiddenRecipients = source.readInt() == 1;
+ mIntegrityProtected = source.readInt() == 1;
}
@Override
@@ -84,7 +81,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mVersionHeader);
dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0);
- dest.writeInt(mCompressionId);
+ dest.writeInt(mCompressionAlgorithm);
dest.writeLongArray(mEncryptionMasterKeyIds);
dest.writeParcelable(mSymmetricPassphrase, 0);
dest.writeInt(mSymmetricEncryptionAlgorithm);
@@ -102,6 +99,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
dest.writeInt(mCleartextSignature ? 1 : 0);
dest.writeInt(mDetachedSignature ? 1 : 0);
dest.writeInt(mHiddenRecipients ? 1 : 0);
+ dest.writeInt(mIntegrityProtected ? 1 : 0);
}
public String getCharset() {
@@ -179,12 +177,12 @@ public class PgpSignEncryptInputParcel implements Parcelable {
return this;
}
- public int getCompressionId() {
- return mCompressionId;
+ public int getCompressionAlgorithm() {
+ return mCompressionAlgorithm;
}
- public PgpSignEncryptInputParcel setCompressionId(int compressionId) {
- mCompressionId = compressionId;
+ public PgpSignEncryptInputParcel setCompressionAlgorithm(int compressionAlgorithm) {
+ mCompressionAlgorithm = compressionAlgorithm;
return this;
}
@@ -234,6 +232,18 @@ public class PgpSignEncryptInputParcel implements Parcelable {
return this;
}
+ public boolean isIntegrityProtected() {
+ return mIntegrityProtected;
+ }
+
+ /**
+ * Only use for testing! Never disable integrity protection!
+ */
+ public PgpSignEncryptInputParcel setIntegrityProtected(boolean integrityProtected) {
+ this.mIntegrityProtected = integrityProtected;
+ return this;
+ }
+
public boolean isHiddenRecipients() {
return mHiddenRecipients;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
index 9073e81b9..29b2ef727 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java
@@ -20,6 +20,8 @@
package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.BCPGOutputStream;
@@ -36,11 +38,11 @@ import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.BaseOperation;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
@@ -60,9 +62,9 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.SignatureException;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -99,6 +101,13 @@ public class PgpSignEncryptOperation extends BaseOperation {
super(context, providerHelper, progressable);
}
+ @NonNull
+ @Override
+ // TODO this is horrible, refactor ASAP!!
+ public OperationResult execute(Parcelable input, CryptoInputParcel cryptoInput) {
+ return null;
+ }
+
/**
* Signs and/or encrypts data based on parameters of class
*/
@@ -114,7 +123,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
boolean enableSignature = input.getSignatureMasterKeyId() != Constants.key.none;
boolean enableEncryption = ((input.getEncryptionMasterKeyIds() != null && input.getEncryptionMasterKeyIds().length > 0)
|| input.getSymmetricPassphrase() != null);
- boolean enableCompression = (input.getCompressionId() != CompressionAlgorithmTags.UNCOMPRESSED);
+ boolean enableCompression = (input.getCompressionAlgorithm() != CompressionAlgorithmTags.UNCOMPRESSED);
Log.d(Constants.TAG, "enableSignature:" + enableSignature
+ "\nenableEncryption:" + enableEncryption
@@ -189,7 +198,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
log.add(LogType.MSG_PSE_PENDING_PASSPHRASE, indent + 1);
return new PgpSignEncryptResult(log, RequiredInputParcel.createRequiredSignPassphrase(
signingKeyRing.getMasterKeyId(), signingKey.getKeyId(),
- cryptoInput.getSignatureTime()));
+ cryptoInput.getSignatureTime()), cryptoInput);
}
if (!signingKey.unlock(localPassphrase)) {
log.add(LogType.MSG_PSE_ERROR_BAD_PASSPHRASE, indent);
@@ -216,15 +225,10 @@ public class PgpSignEncryptOperation extends BaseOperation {
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
- // Use preferred hash algo
+ // Use requested hash algo
int requestedAlgorithm = input.getSignatureHashAlgorithm();
- ArrayList<Integer> supported = signingKey.getSupportedHashAlgorithms();
- if (requestedAlgorithm == PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED) {
- // get most preferred
- input.setSignatureHashAlgorithm(supported.get(0));
- } else if (!supported.contains(requestedAlgorithm)) {
- log.add(LogType.MSG_PSE_ERROR_HASH_ALGO, indent);
- return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
+ if (requestedAlgorithm == PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT) {
+ input.setSignatureHashAlgorithm(PgpSecurityConstants.DEFAULT_HASH_ALGORITHM);
}
}
updateProgress(R.string.progress_preparing_streams, 2, 100);
@@ -233,18 +237,15 @@ public class PgpSignEncryptOperation extends BaseOperation {
PGPEncryptedDataGenerator cPk = null;
if (enableEncryption) {
- // Use preferred encryption algo
+ // Use requested encryption algo
int algo = input.getSymmetricEncryptionAlgorithm();
- if (algo == PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED) {
- // get most preferred
- // TODO: get from recipients
- algo = PgpConstants.sPreferredSymmetricAlgorithms.get(0);
+ if (algo == PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT) {
+ algo = PgpSecurityConstants.DEFAULT_SYMMETRIC_ALGORITHM;
}
- // has Integrity packet enabled!
JcePGPDataEncryptorBuilder encryptorBuilder =
new JcePGPDataEncryptorBuilder(algo)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME)
- .setWithIntegrityPacket(true);
+ .setWithIntegrityPacket(input.isIntegrityProtected());
cPk = new PGPEncryptedDataGenerator(encryptorBuilder);
@@ -263,15 +264,19 @@ public class PgpSignEncryptOperation extends BaseOperation {
try {
CanonicalizedPublicKeyRing keyRing = mProviderHelper.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(id));
- CanonicalizedPublicKey key = keyRing.getEncryptionSubKey();
- cPk.addMethod(key.getPubKeyEncryptionGenerator(input.isHiddenRecipients()));
- log.add(LogType.MSG_PSE_KEY_OK, indent + 1,
- KeyFormattingUtils.convertKeyIdToHex(id));
- } catch (PgpKeyNotFoundException e) {
- log.add(LogType.MSG_PSE_KEY_WARN, indent + 1,
- KeyFormattingUtils.convertKeyIdToHex(id));
- if (input.isFailOnMissingEncryptionKeyIds()) {
- return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
+ Set<Long> encryptSubKeyIds = keyRing.getEncryptIds();
+ for (Long subKeyId : encryptSubKeyIds) {
+ CanonicalizedPublicKey key = keyRing.getPublicKey(subKeyId);
+ cPk.addMethod(key.getPubKeyEncryptionGenerator(input.isHiddenRecipients()));
+ log.add(LogType.MSG_PSE_KEY_OK, indent + 1,
+ KeyFormattingUtils.convertKeyIdToHex(subKeyId));
+ }
+ if (encryptSubKeyIds.isEmpty()) {
+ log.add(LogType.MSG_PSE_KEY_WARN, indent + 1,
+ KeyFormattingUtils.convertKeyIdToHex(id));
+ if (input.isFailOnMissingEncryptionKeyIds()) {
+ return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
+ }
}
} catch (ProviderHelper.NotFoundException e) {
log.add(LogType.MSG_PSE_KEY_UNKNOWN, indent + 1,
@@ -327,7 +332,13 @@ public class PgpSignEncryptOperation extends BaseOperation {
if (enableCompression) {
log.add(LogType.MSG_PSE_COMPRESSING, indent);
- compressGen = new PGPCompressedDataGenerator(input.getCompressionId());
+
+ // Use preferred compression algo
+ int algo = input.getCompressionAlgorithm();
+ if (algo == PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.USE_DEFAULT) {
+ algo = PgpSecurityConstants.DEFAULT_COMPRESSION_ALGORITHM;
+ }
+ compressGen = new PGPCompressedDataGenerator(algo);
bcpgOut = new BCPGOutputStream(compressGen.open(encryptionOut));
} else {
bcpgOut = new BCPGOutputStream(encryptionOut);
@@ -450,7 +461,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
InputStream in = inputData.getInputStream();
if (enableCompression) {
- compressGen = new PGPCompressedDataGenerator(input.getCompressionId());
+ compressGen = new PGPCompressedDataGenerator(input.getCompressionAlgorithm());
bcpgOut = new BCPGOutputStream(compressGen.open(out));
} else {
bcpgOut = new BCPGOutputStream(out);
@@ -497,7 +508,8 @@ public class PgpSignEncryptOperation extends BaseOperation {
// this secret key diverts to a OpenPGP card, throw exception with hash that will be signed
log.add(LogType.MSG_PSE_PENDING_NFC, indent);
return new PgpSignEncryptResult(log, RequiredInputParcel.createNfcSignOperation(
- e.hashToSign, e.hashAlgo, cryptoInput.getSignatureTime()));
+ signingKey.getRing().getMasterKeyId(), signingKey.getKeyId(),
+ e.hashToSign, e.hashAlgo, cryptoInput.getSignatureTime()), cryptoInput);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java
index 464de37f5..8f80a4802 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java
@@ -57,6 +57,10 @@ public class SignEncryptParcel extends PgpSignEncryptInputParcel {
}
+ public boolean isIncomplete() {
+ return mInputUris.size() > mOutputUris.size();
+ }
+
public byte[] getBytes() {
return mBytes;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
index 2bb4f7dc4..a7baddf8b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -48,6 +48,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -78,7 +79,7 @@ import java.util.TreeSet;
*
*/
@SuppressWarnings("unchecked")
-public class UncachedKeyRing {
+public class UncachedKeyRing implements Serializable {
final PGPKeyRing mRing;
final boolean mIsSecret;
@@ -219,7 +220,7 @@ public class UncachedKeyRing {
Iterator<PGPPublicKey> it = mRing.getPublicKeys();
while (it.hasNext()) {
if (KeyFormattingUtils.convertFingerprintToHex(
- it.next().getFingerprint()).equals(expectedFingerprint)) {
+ it.next().getFingerprint()).equalsIgnoreCase(expectedFingerprint)) {
return true;
}
}
@@ -820,6 +821,15 @@ public class UncachedKeyRing {
continue;
}
+ Date keyCreationTime = key.getCreationTime(), keyCreationTimeLenient;
+ {
+ Calendar keyCreationCal = Calendar.getInstance();
+ keyCreationCal.setTime(keyCreationTime);
+ // allow for diverging clocks up to one day when checking creation time
+ keyCreationCal.add(Calendar.MINUTE, -5);
+ keyCreationTimeLenient = keyCreationCal.getTime();
+ }
+
// A subkey needs exactly one subkey binding certificate, and optionally one revocation
// certificate.
PGPPublicKey modified = key;
@@ -851,6 +861,18 @@ public class UncachedKeyRing {
continue;
}
+ if (cert.getCreationTime().before(keyCreationTime)) {
+ // Signature is earlier than key creation time
+ log.add(LogType.MSG_KC_SUB_BAD_TIME_EARLY, indent);
+ // due to an earlier accident, we generated keys which had creation timestamps
+ // a few seconds after their signature timestamp. for compatibility, we only
+ // error out with some margin of error
+ if (cert.getCreationTime().before(keyCreationTimeLenient)) {
+ badCerts += 1;
+ continue;
+ }
+ }
+
if (cert.isLocal()) {
// Creation date in the future? No way!
log.add(LogType.MSG_KC_SUB_BAD_LOCAL, indent);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
index 0173a1d83..013a6bf14 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
@@ -211,12 +211,19 @@ public class UncachedPublicKey {
return getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT;
}
+ public boolean isRSA() {
+ return getAlgorithm() == PGPPublicKey.RSA_GENERAL
+ || getAlgorithm() == PGPPublicKey.RSA_ENCRYPT
+ || getAlgorithm() == PGPPublicKey.RSA_SIGN;
+ }
+
public boolean isDSA() {
return getAlgorithm() == PGPPublicKey.DSA;
}
public boolean isEC() {
- return getAlgorithm() == PGPPublicKey.ECDH || getAlgorithm() == PGPPublicKey.ECDSA;
+ return getAlgorithm() == PGPPublicKey.ECDH
+ || getAlgorithm() == PGPPublicKey.ECDSA;
}
public byte[] getFingerprint() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
index 11d6728e2..ee28b5f36 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -51,6 +51,11 @@ public class KeychainContract {
String EXPIRY = "expiry";
}
+ interface UpdatedKeysColumns {
+ String MASTER_KEY_ID = "master_key_id"; // not a database id
+ String LAST_UPDATED = "last_updated"; // time since epoch in seconds
+ }
+
interface UserPacketsColumns {
String MASTER_KEY_ID = "master_key_id"; // foreign key to key_rings._ID
String TYPE = "type"; // not a database id
@@ -90,13 +95,15 @@ public class KeychainContract {
String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name
}
- public static final String CONTENT_AUTHORITY = Constants.PACKAGE_NAME + ".provider";
+ public static final String CONTENT_AUTHORITY = Constants.PROVIDER_AUTHORITY;
private static final Uri BASE_CONTENT_URI_INTERNAL = Uri
.parse("content://" + CONTENT_AUTHORITY);
public static final String BASE_KEY_RINGS = "key_rings";
+ public static final String BASE_UPDATED_KEYS = "updated_keys";
+
public static final String PATH_UNIFIED = "unified";
public static final String PATH_FIND = "find";
@@ -235,6 +242,16 @@ public class KeychainContract {
}
+ public static class UpdatedKeys implements UpdatedKeysColumns, BaseColumns {
+ public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
+ .appendPath(BASE_UPDATED_KEYS).build();
+
+ public static final String CONTENT_TYPE
+ = "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.updated_keys";
+ public static final String CONTENT_ITEM_TYPE
+ = "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.provider.updated_keys";
+ }
+
public static class UserPackets implements UserPacketsColumns, BaseColumns {
public static final String VERIFIED = "verified";
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
index ff661e494..d7fb738fc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -34,6 +34,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeysColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns;
import org.sufficientlysecure.keychain.ui.ConsolidateDialogActivity;
import org.sufficientlysecure.keychain.util.Log;
@@ -53,7 +54,7 @@ import java.io.IOException;
*/
public class KeychainDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "openkeychain.db";
- private static final int DATABASE_VERSION = 9;
+ private static final int DATABASE_VERSION = 12;
static Boolean apgHack = false;
private Context mContext;
@@ -61,6 +62,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
String KEY_RINGS_PUBLIC = "keyrings_public";
String KEY_RINGS_SECRET = "keyrings_secret";
String KEYS = "keys";
+ String UPDATED_KEYS = "updated_keys";
String USER_PACKETS = "user_packets";
String CERTS = "certs";
String API_APPS = "api_apps";
@@ -144,6 +146,14 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ Tables.USER_PACKETS + "(" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.RANK + ") ON DELETE CASCADE"
+ ")";
+ private static final String CREATE_UPDATE_KEYS =
+ "CREATE TABLE IF NOT EXISTS " + Tables.UPDATED_KEYS + " ("
+ + UpdatedKeysColumns.MASTER_KEY_ID + " INTEGER PRIMARY KEY, "
+ + UpdatedKeysColumns.LAST_UPDATED + " INTEGER, "
+ + "FOREIGN KEY(" + UpdatedKeysColumns.MASTER_KEY_ID + ") REFERENCES "
+ + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE"
+ + ")";
+
private static final String CREATE_API_APPS =
"CREATE TABLE IF NOT EXISTS " + Tables.API_APPS + " ("
+ BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
@@ -179,7 +189,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ Tables.API_APPS + "(" + ApiAppsAllowedKeysColumns.PACKAGE_NAME + ") ON DELETE CASCADE"
+ ")";
- KeychainDatabase(Context context) {
+ public KeychainDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mContext = context;
@@ -206,6 +216,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
db.execSQL(CREATE_KEYS);
db.execSQL(CREATE_USER_PACKETS);
db.execSQL(CREATE_CERTS);
+ db.execSQL(CREATE_UPDATE_KEYS);
db.execSQL(CREATE_API_APPS);
db.execSQL(CREATE_API_APPS_ACCOUNTS);
db.execSQL(CREATE_API_APPS_ALLOWED_KEYS);
@@ -272,6 +283,19 @@ public class KeychainDatabase extends SQLiteOpenHelper {
db.execSQL("DROP TABLE IF EXISTS user_ids");
db.execSQL(CREATE_USER_PACKETS);
db.execSQL(CREATE_CERTS);
+ case 10:
+ // do nothing here, just consolidate
+ case 11:
+ // fix problems in database, see #1402 for details
+ // https://github.com/open-keychain/open-keychain/issues/1402
+ db.execSQL("DELETE FROM api_accounts WHERE key_id BETWEEN 0 AND 3");
+ case 12:
+ db.execSQL(CREATE_UPDATE_KEYS);
+ if (oldVersion == 10) {
+ // no consolidate if we are updating from 10, we're just here for
+ // the api_accounts fix and the new update keys table
+ return;
+ }
}
@@ -295,10 +319,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
// It's the Java way =(
String[] dbs = context.databaseList();
for (String db : dbs) {
- if (db.equals("apg.db")) {
+ if ("apg.db".equals(db)) {
hasApgDb = true;
- } else if (db.equals("apg_old.db")) {
+ } else if ("apg_old.db".equals(db)) {
Log.d(Constants.TAG, "Found apg_old.db, delete it!");
+ // noinspection ResultOfMethodCallIgnored - if it doesn't happen, it doesn't happen.
context.getDatabasePath("apg_old.db").delete();
}
}
@@ -382,17 +407,22 @@ public class KeychainDatabase extends SQLiteOpenHelper {
}
}
- // delete old database
+ // noinspection ResultOfMethodCallIgnored - not much we can do if this doesn't work
context.getDatabasePath("apg.db").delete();
}
private static void copy(File in, File out) throws IOException {
FileInputStream is = new FileInputStream(in);
FileOutputStream os = new FileOutputStream(out);
- byte[] buf = new byte[512];
- while (is.available() > 0) {
- int count = is.read(buf, 0, 512);
- os.write(buf, 0, count);
+ try {
+ byte[] buf = new byte[512];
+ while (is.available() > 0) {
+ int count = is.read(buf, 0, 512);
+ os.write(buf, 0, count);
+ }
+ } finally {
+ is.close();
+ os.close();
}
}
@@ -409,6 +439,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
} else {
in = context.getDatabasePath(DATABASE_NAME);
out = context.getDatabasePath("debug_backup.db");
+ // noinspection ResultOfMethodCallIgnored - this is a pure debug feature, anyways
out.createNewFile();
}
if (!in.canRead()) {
@@ -423,6 +454,9 @@ public class KeychainDatabase extends SQLiteOpenHelper {
// DANGEROUS, use in test code ONLY!
public void clearDatabase() {
getWritableDatabase().execSQL("delete from " + Tables.KEY_RINGS_PUBLIC);
+ getWritableDatabase().execSQL("delete from " + Tables.API_ACCOUNTS);
+ getWritableDatabase().execSQL("delete from " + Tables.API_ALLOWED_KEYS);
+ getWritableDatabase().execSQL("delete from " + Tables.API_APPS);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
index ecb26b56a..d722fa9e7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -39,6 +39,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
@@ -75,6 +76,9 @@ public class KeychainProvider extends ContentProvider {
private static final int KEY_RINGS_FIND_BY_EMAIL = 400;
private static final int KEY_RINGS_FIND_BY_SUBKEY = 401;
+ private static final int UPDATED_KEYS = 500;
+ private static final int UPDATED_KEYS_SPECIFIC = 501;
+
protected UriMatcher mUriMatcher;
/**
@@ -192,6 +196,12 @@ public class KeychainProvider extends ContentProvider {
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
+ KeychainContract.PATH_ALLOWED_KEYS, API_ALLOWED_KEYS);
+ /**
+ * to access table containing last updated dates of keys
+ */
+ matcher.addURI(authority, KeychainContract.BASE_UPDATED_KEYS, UPDATED_KEYS);
+ matcher.addURI(authority, KeychainContract.BASE_UPDATED_KEYS + "/*", UPDATED_KEYS_SPECIFIC);
+
return matcher;
}
@@ -231,6 +241,11 @@ public class KeychainProvider extends ContentProvider {
case KEY_RING_SECRET:
return KeyRings.CONTENT_ITEM_TYPE;
+ case UPDATED_KEYS:
+ return UpdatedKeys.CONTENT_TYPE;
+ case UPDATED_KEYS_SPECIFIC:
+ return UpdatedKeys.CONTENT_ITEM_TYPE;
+
case API_APPS:
return ApiApps.CONTENT_TYPE;
@@ -536,7 +551,6 @@ public class KeychainProvider extends ContentProvider {
}
break;
-
}
case KEY_RINGS_PUBLIC:
@@ -631,23 +645,42 @@ public class KeychainProvider extends ContentProvider {
break;
}
- case API_APPS:
+ case UPDATED_KEYS:
+ case UPDATED_KEYS_SPECIFIC: {
+ HashMap<String, String> projectionMap = new HashMap<>();
+ qb.setTables(Tables.UPDATED_KEYS);
+ projectionMap.put(UpdatedKeys.MASTER_KEY_ID, Tables.UPDATED_KEYS + "."
+ + UpdatedKeys.MASTER_KEY_ID);
+ projectionMap.put(UpdatedKeys.LAST_UPDATED, Tables.UPDATED_KEYS + "."
+ + UpdatedKeys.LAST_UPDATED);
+ qb.setProjectionMap(projectionMap);
+ if (match == UPDATED_KEYS_SPECIFIC) {
+ qb.appendWhere(UpdatedKeys.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
+ }
+ break;
+ }
+
+ case API_APPS: {
qb.setTables(Tables.API_APPS);
break;
- case API_APPS_BY_PACKAGE_NAME:
+ }
+ case API_APPS_BY_PACKAGE_NAME: {
qb.setTables(Tables.API_APPS);
qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
qb.appendWhereEscapeString(uri.getLastPathSegment());
break;
- case API_ACCOUNTS:
+ }
+ case API_ACCOUNTS: {
qb.setTables(Tables.API_ACCOUNTS);
qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(1));
break;
- case API_ACCOUNTS_BY_ACCOUNT_NAME:
+ }
+ case API_ACCOUNTS_BY_ACCOUNT_NAME: {
qb.setTables(Tables.API_ACCOUNTS);
qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(1));
@@ -656,14 +689,17 @@ public class KeychainProvider extends ContentProvider {
qb.appendWhereEscapeString(uri.getLastPathSegment());
break;
- case API_ALLOWED_KEYS:
+ }
+ case API_ALLOWED_KEYS: {
qb.setTables(Tables.API_ALLOWED_KEYS);
qb.appendWhere(Tables.API_ALLOWED_KEYS + "." + ApiAccounts.PACKAGE_NAME + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(1));
break;
- default:
+ }
+ default: {
throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")");
+ }
}
@@ -708,47 +744,53 @@ public class KeychainProvider extends ContentProvider {
final int match = mUriMatcher.match(uri);
switch (match) {
- case KEY_RING_PUBLIC:
+ case KEY_RING_PUBLIC: {
db.insertOrThrow(Tables.KEY_RINGS_PUBLIC, null, values);
keyId = values.getAsLong(KeyRings.MASTER_KEY_ID);
break;
-
- case KEY_RING_SECRET:
+ }
+ case KEY_RING_SECRET: {
db.insertOrThrow(Tables.KEY_RINGS_SECRET, null, values);
keyId = values.getAsLong(KeyRings.MASTER_KEY_ID);
break;
-
- case KEY_RING_KEYS:
+ }
+ case KEY_RING_KEYS: {
db.insertOrThrow(Tables.KEYS, null, values);
keyId = values.getAsLong(Keys.MASTER_KEY_ID);
break;
-
- case KEY_RING_USER_IDS:
+ }
+ case KEY_RING_USER_IDS: {
// iff TYPE is null, user_id MUST be null as well
- if ( ! (values.get(UserPacketsColumns.TYPE) == null
+ if (!(values.get(UserPacketsColumns.TYPE) == null
? (values.get(UserPacketsColumns.USER_ID) != null && values.get(UserPacketsColumns.ATTRIBUTE_DATA) == null)
: (values.get(UserPacketsColumns.ATTRIBUTE_DATA) != null && values.get(UserPacketsColumns.USER_ID) == null)
- )) {
+ )) {
throw new AssertionError("Incorrect type for user packet! This is a bug!");
}
- if (values.get(UserPacketsColumns.RANK) == 0 && values.get(UserPacketsColumns.USER_ID) == null) {
+ if (((Number) values.get(UserPacketsColumns.RANK)).intValue() == 0 && values.get(UserPacketsColumns.USER_ID) == null) {
throw new AssertionError("Rank 0 user packet must be a user id!");
}
db.insertOrThrow(Tables.USER_PACKETS, null, values);
keyId = values.getAsLong(UserPackets.MASTER_KEY_ID);
break;
-
- case KEY_RING_CERTS:
+ }
+ case KEY_RING_CERTS: {
// we replace here, keeping only the latest signature
// TODO this would be better handled in savePublicKeyRing directly!
db.replaceOrThrow(Tables.CERTS, null, values);
keyId = values.getAsLong(Certs.MASTER_KEY_ID);
break;
-
- case API_APPS:
+ }
+ case UPDATED_KEYS: {
+ long updatedKeyId = db.replace(Tables.UPDATED_KEYS, null, values);
+ rowUri = UpdatedKeys.CONTENT_URI.buildUpon().appendPath("" + updatedKeyId)
+ .build();
+ break;
+ }
+ case API_APPS: {
db.insertOrThrow(Tables.API_APPS, null, values);
break;
-
+ }
case API_ACCOUNTS: {
// set foreign key automatically based on given uri
// e.g., api_apps/com.example.app/accounts/
@@ -767,8 +809,9 @@ public class KeychainProvider extends ContentProvider {
db.insertOrThrow(Tables.API_ALLOWED_KEYS, null, values);
break;
}
- default:
+ default: {
throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
}
if (keyId != null) {
@@ -826,20 +869,24 @@ public class KeychainProvider extends ContentProvider {
break;
}
- case API_APPS_BY_PACKAGE_NAME:
+ case API_APPS_BY_PACKAGE_NAME: {
count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, additionalSelection),
selectionArgs);
break;
- case API_ACCOUNTS_BY_ACCOUNT_NAME:
+ }
+ case API_ACCOUNTS_BY_ACCOUNT_NAME: {
count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, additionalSelection),
selectionArgs);
break;
- case API_ALLOWED_KEYS:
+ }
+ case API_ALLOWED_KEYS: {
count = db.delete(Tables.API_ALLOWED_KEYS, buildDefaultApiAllowedKeysSelection(uri, additionalSelection),
selectionArgs);
break;
- default:
+ }
+ default: {
throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
}
// notify of changes in db
@@ -875,16 +922,19 @@ public class KeychainProvider extends ContentProvider {
count = db.update(Tables.KEYS, values, actualSelection, selectionArgs);
break;
}
- case API_APPS_BY_PACKAGE_NAME:
+ case API_APPS_BY_PACKAGE_NAME: {
count = db.update(Tables.API_APPS, values,
buildDefaultApiAppsSelection(uri, selection), selectionArgs);
break;
- case API_ACCOUNTS_BY_ACCOUNT_NAME:
+ }
+ case API_ACCOUNTS_BY_ACCOUNT_NAME: {
count = db.update(Tables.API_ACCOUNTS, values,
buildDefaultApiAccountsSelection(uri, selection), selectionArgs);
break;
- default:
+ }
+ default: {
throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
}
// notify of changes in db
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index bf56417e9..d9ef4f3c8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -26,6 +26,7 @@ import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
+import android.support.annotation.NonNull;
import android.support.v4.util.LongSparseArray;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
@@ -38,7 +39,7 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.operations.ImportExportOperation;
+import org.sufficientlysecure.keychain.operations.ImportOperation;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
@@ -49,7 +50,7 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.pgp.PgpConstants;
+import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
@@ -61,6 +62,8 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys;
import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.util.IterableIterator;
@@ -81,6 +84,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
/**
* This class contains high level methods for database access. Despite its
@@ -648,10 +652,6 @@ public class ProviderHelper {
UserPacketItem item = uids.get(userIdRank);
operations.add(buildUserIdOperations(masterKeyId, item, userIdRank));
- if (item.selfCert == null) {
- throw new AssertionError("User ids MUST be self-certified at this point!!");
- }
-
if (item.selfRevocation != null) {
operations.add(buildCertOperations(masterKeyId, userIdRank, item.selfRevocation,
Certs.VERIFIED_SELF));
@@ -659,6 +659,10 @@ public class ProviderHelper {
continue;
}
+ if (item.selfCert == null) {
+ throw new AssertionError("User ids MUST be self-certified at this point!!");
+ }
+
operations.add(buildCertOperations(masterKeyId, userIdRank, item.selfCert,
selfCertsAreTrusted ? Certs.VERIFIED_SECRET : Certs.VERIFIED_SELF));
@@ -684,6 +688,36 @@ public class ProviderHelper {
mIndent -= 1;
}
+ // before deleting key, retrieve it's last updated time
+ final int INDEX_MASTER_KEY_ID = 0;
+ final int INDEX_LAST_UPDATED = 1;
+ Cursor lastUpdatedCursor = mContentResolver.query(
+ UpdatedKeys.CONTENT_URI,
+ new String[]{
+ UpdatedKeys.MASTER_KEY_ID,
+ UpdatedKeys.LAST_UPDATED
+ },
+ UpdatedKeys.MASTER_KEY_ID + " = ?",
+ new String[]{"" + masterKeyId},
+ null
+ );
+ if (lastUpdatedCursor.moveToNext()) {
+ // there was an entry to re-insert
+ // this operation must happen after the new key is inserted
+ ContentValues lastUpdatedEntry = new ContentValues(2);
+ lastUpdatedEntry.put(UpdatedKeys.MASTER_KEY_ID,
+ lastUpdatedCursor.getLong(INDEX_MASTER_KEY_ID));
+ lastUpdatedEntry.put(UpdatedKeys.LAST_UPDATED,
+ lastUpdatedCursor.getLong(INDEX_LAST_UPDATED));
+ operations.add(
+ ContentProviderOperation
+ .newInsert(UpdatedKeys.CONTENT_URI)
+ .withValues(lastUpdatedEntry)
+ .build()
+ );
+ }
+ lastUpdatedCursor.close();
+
try {
// delete old version of this keyRing, which also deletes all keys and userIds on cascade
int deleted = mContentResolver.delete(
@@ -725,7 +759,7 @@ public class ProviderHelper {
LongSparseArray<WrappedSignature> trustedCerts = new LongSparseArray<>();
@Override
- public int compareTo(UserPacketItem o) {
+ public int compareTo(@NonNull UserPacketItem o) {
// revoked keys always come last!
//noinspection DoubleNegation
if ((selfRevocation != null) != (o.selfRevocation != null)) {
@@ -782,7 +816,7 @@ public class ProviderHelper {
// first, mark all keys as not available
ContentValues values = new ContentValues();
- values.put(Keys.HAS_SECRET, SecretKeyType.UNAVAILABLE.getNum());
+ values.put(Keys.HAS_SECRET, SecretKeyType.GNU_DUMMY.getNum());
mContentResolver.update(uri, values, null, null);
// then, mark exactly the keys we have available
@@ -831,7 +865,7 @@ public class ProviderHelper {
mIndent -= 1;
// this implicitly leaves all keys which were not in the secret key ring
- // with has_secret = 0
+ // with has_secret = 1
}
log(LogType.MSG_IS_SUCCESS);
@@ -906,7 +940,8 @@ public class ProviderHelper {
// If there is a secret key, merge new data (if any) and save the key for later
CanonicalizedSecretKeyRing canSecretRing;
try {
- UncachedKeyRing secretRing = getCanonicalizedSecretKeyRing(publicRing.getMasterKeyId()).getUncachedKeyRing();
+ UncachedKeyRing secretRing = getCanonicalizedSecretKeyRing(publicRing.getMasterKeyId())
+ .getUncachedKeyRing();
// Merge data from new public ring into secret one
log(LogType.MSG_IP_MERGE_SECRET);
@@ -1031,7 +1066,8 @@ public class ProviderHelper {
publicRing = secretRing.extractPublicKeyRing();
}
- CanonicalizedPublicKeyRing canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent);
+ CanonicalizedPublicKeyRing canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog,
+ mIndent);
if (canPublicRing == null) {
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
}
@@ -1057,6 +1093,7 @@ public class ProviderHelper {
}
+ @NonNull
public ConsolidateResult consolidateDatabaseStep1(Progressable progress) {
OperationLog log = new OperationLog();
@@ -1082,7 +1119,7 @@ public class ProviderHelper {
indent += 1;
final Cursor cursor = mContentResolver.query(KeyRingData.buildSecretKeyRingUri(),
- new String[]{ KeyRingData.KEY_RING_DATA }, null, null, null);
+ new String[]{KeyRingData.KEY_RING_DATA}, null, null, null);
if (cursor == null) {
log.add(LogType.MSG_CON_ERROR_DB, indent);
@@ -1124,6 +1161,7 @@ public class ProviderHelper {
}
});
+ cursor.close();
} catch (IOException e) {
Log.e(Constants.TAG, "error saving secret", e);
@@ -1143,7 +1181,7 @@ public class ProviderHelper {
final Cursor cursor = mContentResolver.query(
KeyRingData.buildPublicKeyRingUri(),
- new String[]{ KeyRingData.KEY_RING_DATA }, null, null, null);
+ new String[]{KeyRingData.KEY_RING_DATA}, null, null, null);
if (cursor == null) {
log.add(LogType.MSG_CON_ERROR_DB, indent);
@@ -1185,6 +1223,7 @@ public class ProviderHelper {
}
});
+ cursor.close();
} catch (IOException e) {
Log.e(Constants.TAG, "error saving public", e);
@@ -1200,12 +1239,14 @@ public class ProviderHelper {
return consolidateDatabaseStep2(log, indent, progress, false);
}
+ @NonNull
public ConsolidateResult consolidateDatabaseStep2(Progressable progress) {
return consolidateDatabaseStep2(new OperationLog(), 0, progress, true);
}
private static boolean mConsolidateCritical = false;
+ @NonNull
private ConsolidateResult consolidateDatabaseStep2(
OperationLog log, int indent, Progressable progress, boolean recovery) {
@@ -1231,6 +1272,28 @@ public class ProviderHelper {
}
// 2. wipe database (IT'S DANGEROUS)
+
+ // first, backup our list of updated key times
+ ArrayList<ContentValues> updatedKeysValues = new ArrayList<>();
+ final int INDEX_MASTER_KEY_ID = 0;
+ final int INDEX_LAST_UPDATED = 1;
+ Cursor lastUpdatedCursor = mContentResolver.query(
+ UpdatedKeys.CONTENT_URI,
+ new String[]{
+ UpdatedKeys.MASTER_KEY_ID,
+ UpdatedKeys.LAST_UPDATED
+ },
+ null, null, null);
+ while (lastUpdatedCursor.moveToNext()) {
+ ContentValues values = new ContentValues();
+ values.put(UpdatedKeys.MASTER_KEY_ID,
+ lastUpdatedCursor.getLong(INDEX_MASTER_KEY_ID));
+ values.put(UpdatedKeys.LAST_UPDATED,
+ lastUpdatedCursor.getLong(INDEX_LAST_UPDATED));
+ updatedKeysValues.add(values);
+ }
+ lastUpdatedCursor.close();
+
log.add(LogType.MSG_CON_DB_CLEAR, indent);
mContentResolver.delete(KeyRings.buildUnifiedKeyRingsUri(), null, null);
@@ -1248,9 +1311,9 @@ public class ProviderHelper {
// 3. Re-Import secret keyrings from cache
if (numSecrets > 0) {
- ImportKeyResult result = new ImportExportOperation(mContext, this,
+ ImportKeyResult result = new ImportOperation(mContext, this,
new ProgressFixedScaler(progress, 10, 25, 100, R.string.progress_con_reimport))
- .importKeyRings(itSecrets, numSecrets, null);
+ .serialKeyRingImport(itSecrets, numSecrets, null, null);
log.add(result, indent);
} else {
log.add(LogType.MSG_CON_REIMPORT_SECRET_SKIP, indent);
@@ -1276,10 +1339,14 @@ public class ProviderHelper {
// 4. Re-Import public keyrings from cache
if (numPublics > 0) {
- ImportKeyResult result = new ImportExportOperation(mContext, this,
+ ImportKeyResult result = new ImportOperation(mContext, this,
new ProgressFixedScaler(progress, 25, 99, 100, R.string.progress_con_reimport))
- .importKeyRings(itPublics, numPublics, null);
+ .serialKeyRingImport(itPublics, numPublics, null, null);
log.add(result, indent);
+ // re-insert our backed up list of updated key times
+ // TODO: can this cause issues in case a public key re-import failed?
+ mContentResolver.bulkInsert(UpdatedKeys.CONTENT_URI,
+ updatedKeysValues.toArray(new ContentValues[updatedKeysValues.size()]));
} else {
log.add(LogType.MSG_CON_REIMPORT_PUBLIC_SKIP, indent);
}
@@ -1389,6 +1456,14 @@ public class ProviderHelper {
return getKeyRingAsArmoredString(data);
}
+ public Uri renewKeyLastUpdatedTime(long masterKeyId, long time, TimeUnit timeUnit) {
+ ContentValues values = new ContentValues();
+ values.put(UpdatedKeys.MASTER_KEY_ID, masterKeyId);
+ values.put(UpdatedKeys.LAST_UPDATED, timeUnit.toSeconds(time));
+
+ return mContentResolver.insert(UpdatedKeys.CONTENT_URI, values);
+ }
+
public ArrayList<String> getRegisteredApiApps() {
Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null);
@@ -1414,7 +1489,7 @@ public class ProviderHelper {
private ContentValues contentValueForApiApps(AppSettings appSettings) {
ContentValues values = new ContentValues();
values.put(ApiApps.PACKAGE_NAME, appSettings.getPackageName());
- values.put(ApiApps.PACKAGE_CERTIFICATE, appSettings.getPackageSignature());
+ values.put(ApiApps.PACKAGE_CERTIFICATE, appSettings.getPackageCertificate());
return values;
}
@@ -1426,9 +1501,9 @@ public class ProviderHelper {
// DEPRECATED and thus hardcoded
values.put(KeychainContract.ApiAccounts.COMPRESSION, CompressionAlgorithmTags.ZLIB);
values.put(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM,
- PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
+ PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT);
values.put(KeychainContract.ApiAccounts.HASH_ALORITHM,
- PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED);
+ PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT);
return values;
}
@@ -1460,7 +1535,7 @@ public class ProviderHelper {
settings = new AppSettings();
settings.setPackageName(cursor.getString(
cursor.getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
- settings.setPackageSignature(cursor.getBlob(
+ settings.setPackageCertificate(cursor.getBlob(
cursor.getColumnIndex(KeychainContract.ApiApps.PACKAGE_CERTIFICATE)));
}
} finally {
@@ -1514,8 +1589,8 @@ public class ProviderHelper {
return keyIds;
}
- public Set<Long> getAllowedKeyIdsForApp(Uri uri) {
- Set<Long> keyIds = new HashSet<>();
+ public HashSet<Long> getAllowedKeyIdsForApp(Uri uri) {
+ HashSet<Long> keyIds = new HashSet<>();
Cursor cursor = mContentResolver.query(uri, null, null, null, null);
try {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java
index 45f806960..7e9b24989 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java
@@ -18,6 +18,8 @@
package org.sufficientlysecure.keychain.provider;
+
+import android.content.ClipDescription;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
@@ -38,6 +40,25 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.UUID;
+/**
+ * TemporaryStorageProvider stores decrypted files inside the app's cache directory previously to
+ * sharing them with other applications.
+ *
+ * Security:
+ * - It is writable by OpenKeychain only (see Manifest), but exported for reading files
+ * - It uses UUIDs as identifiers which makes predicting files from outside impossible
+ * - Querying a number of files is not allowed, only querying single files
+ * -> You can only open a file if you know the Uri containing the precise UUID, this Uri is only
+ * revealed when the user shares a decrypted file with another app.
+ *
+ * Why is support lib's FileProvider not used?
+ * Because granting Uri permissions temporarily does not work correctly. See
+ * - https://code.google.com/p/android/issues/detail?id=76683
+ * - https://github.com/nmr8acme/FileProvider-permission-bug
+ * - http://stackoverflow.com/q/24467696
+ * - http://stackoverflow.com/q/18249007
+ * - Comments at http://www.blogc.at/2014/03/23/share-private-files-with-other-apps-fileprovider/
+ */
public class TemporaryStorageProvider extends ContentProvider {
private static final String DB_NAME = "tempstorage.db";
@@ -45,17 +66,37 @@ public class TemporaryStorageProvider extends ContentProvider {
private static final String COLUMN_ID = "id";
private static final String COLUMN_NAME = "name";
private static final String COLUMN_TIME = "time";
- private static final Uri BASE_URI = Uri.parse("content://org.sufficientlysecure.keychain.tempstorage/");
- private static final int DB_VERSION = 2;
+ private static final String COLUMN_TYPE = "mimetype";
+ public static final String CONTENT_AUTHORITY = Constants.TEMPSTORAGE_AUTHORITY;
+ private static final Uri BASE_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
+ private static final int DB_VERSION = 3;
private static File cacheDir;
+ public static Uri createFile(Context context, String targetName, String mimeType) {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(COLUMN_NAME, targetName);
+ contentValues.put(COLUMN_TYPE, mimeType);
+ return context.getContentResolver().insert(BASE_URI, contentValues);
+ }
+
public static Uri createFile(Context context, String targetName) {
ContentValues contentValues = new ContentValues();
contentValues.put(COLUMN_NAME, targetName);
return context.getContentResolver().insert(BASE_URI, contentValues);
}
+ public static Uri createFile(Context context) {
+ ContentValues contentValues = new ContentValues();
+ return context.getContentResolver().insert(BASE_URI, contentValues);
+ }
+
+ public static int setMimeType(Context context, Uri uri, String mimetype) {
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_TYPE, mimetype);
+ return context.getContentResolver().update(uri, values, null, null);
+ }
+
public static int cleanUp(Context context) {
return context.getContentResolver().delete(BASE_URI, COLUMN_TIME + "< ?",
new String[]{Long.toString(System.currentTimeMillis() - Constants.TEMPFILE_TTL)});
@@ -72,6 +113,7 @@ public class TemporaryStorageProvider extends ContentProvider {
db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_FILES + " (" +
COLUMN_ID + " TEXT PRIMARY KEY, " +
COLUMN_NAME + " TEXT, " +
+ COLUMN_TYPE + " TEXT, " +
COLUMN_TIME + " INTEGER" +
");");
}
@@ -88,6 +130,8 @@ public class TemporaryStorageProvider extends ContentProvider {
COLUMN_NAME + " TEXT, " +
COLUMN_TIME + " INTEGER" +
");");
+ case 2:
+ db.execSQL("ALTER TABLE files ADD COLUMN " + COLUMN_TYPE + " TEXT");
}
}
}
@@ -115,6 +159,10 @@ public class TemporaryStorageProvider extends ContentProvider {
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ if (uri.getLastPathSegment() == null) {
+ throw new SecurityException("Listing temporary files is not allowed, only querying single files.");
+ }
+
File file;
try {
file = getFile(uri);
@@ -125,9 +173,15 @@ public class TemporaryStorageProvider extends ContentProvider {
new String[]{uri.getLastPathSegment()}, null, null, null);
if (fileName != null) {
if (fileName.moveToNext()) {
- MatrixCursor cursor =
- new MatrixCursor(new String[]{OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE, "_data"});
- cursor.newRow().add(fileName.getString(0)).add(file.length()).add(file.getAbsolutePath());
+ MatrixCursor cursor = new MatrixCursor(new String[]{
+ OpenableColumns.DISPLAY_NAME,
+ OpenableColumns.SIZE,
+ "_data"
+ });
+ cursor.newRow()
+ .add(fileName.getString(0))
+ .add(file.length())
+ .add(file.getAbsolutePath());
fileName.close();
return cursor;
}
@@ -138,9 +192,30 @@ public class TemporaryStorageProvider extends ContentProvider {
@Override
public String getType(Uri uri) {
- // Note: If we can find a files mime type, we can decrypt it to temp storage and open it after
- // encryption. The mime type is needed, else UI really sucks and some apps break.
- return "*/*";
+ Cursor cursor = db.getReadableDatabase().query(TABLE_FILES,
+ new String[]{COLUMN_TYPE}, COLUMN_ID + "=?",
+ new String[]{uri.getLastPathSegment()}, null, null, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToNext()) {
+ if (!cursor.isNull(0)) {
+ return cursor.getString(0);
+ }
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ return "application/octet-stream";
+ }
+
+ @Override
+ public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
+ String type = getType(uri);
+ if (ClipDescription.compareMimeTypes(type, mimeTypeFilter)) {
+ return new String[]{type};
+ }
+ return null;
}
@Override
@@ -151,9 +226,14 @@ public class TemporaryStorageProvider extends ContentProvider {
String uuid = UUID.randomUUID().toString();
values.put(COLUMN_ID, uuid);
int insert = (int) db.getWritableDatabase().insert(TABLE_FILES, null, values);
+ if (insert == -1) {
+ Log.e(Constants.TAG, "Insert failed!");
+ return null;
+ }
try {
getFile(uuid).createNewFile();
} catch (IOException e) {
+ Log.e(Constants.TAG, "File creation failed!");
return null;
}
return Uri.withAppendedPath(BASE_URI, uuid);
@@ -161,10 +241,13 @@ public class TemporaryStorageProvider extends ContentProvider {
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
- if (uri.getLastPathSegment() != null) {
- selection = DatabaseUtil.concatenateWhere(selection, COLUMN_ID + "=?");
- selectionArgs = DatabaseUtil.appendSelectionArgs(selectionArgs, new String[]{uri.getLastPathSegment()});
+ if (uri == null || uri.getLastPathSegment() == null) {
+ return 0;
}
+
+ selection = DatabaseUtil.concatenateWhere(selection, COLUMN_ID + "=?");
+ selectionArgs = DatabaseUtil.appendSelectionArgs(selectionArgs, new String[]{uri.getLastPathSegment()});
+
Cursor files = db.getReadableDatabase().query(TABLE_FILES, new String[]{COLUMN_ID}, selection,
selectionArgs, null, null, null);
if (files != null) {
@@ -179,11 +262,19 @@ public class TemporaryStorageProvider extends ContentProvider {
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- throw new UnsupportedOperationException("Update not supported");
+ if (values.size() != 1 || !values.containsKey(COLUMN_TYPE)) {
+ throw new UnsupportedOperationException("Update supported only for type field!");
+ }
+ if (selection != null || selectionArgs != null) {
+ throw new UnsupportedOperationException("Update supported only for plain uri!");
+ }
+ return db.getWritableDatabase().update(TABLE_FILES, values,
+ COLUMN_ID + " = ?", new String[]{uri.getLastPathSegment()});
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
return openFileHelper(uri, mode);
}
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java
index a3f9f84c9..498601769 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java
@@ -19,7 +19,7 @@ package org.sufficientlysecure.keychain.remote;
public class AppSettings {
private String mPackageName;
- private byte[] mPackageSignature;
+ private byte[] mPackageCertificate;
public AppSettings() {
@@ -28,7 +28,7 @@ public class AppSettings {
public AppSettings(String packageName, byte[] packageSignature) {
super();
this.mPackageName = packageName;
- this.mPackageSignature = packageSignature;
+ this.mPackageCertificate = packageSignature;
}
public String getPackageName() {
@@ -39,12 +39,12 @@ public class AppSettings {
this.mPackageName = packageName;
}
- public byte[] getPackageSignature() {
- return mPackageSignature;
+ public byte[] getPackageCertificate() {
+ return mPackageCertificate;
}
- public void setPackageSignature(byte[] packageSignature) {
- this.mPackageSignature = packageSignature;
+ public void setPackageCertificate(byte[] packageCertificate) {
+ this.mPackageCertificate = packageCertificate;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index 4a8bf9332..49079f585 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2013-2015 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
@@ -24,21 +24,22 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
import android.text.TextUtils;
import org.openintents.openpgp.IOpenPgpService;
+import org.openintents.openpgp.OpenPgpDecryptionResult;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi;
-import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel;
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
-import org.sufficientlysecure.keychain.pgp.PgpConstants;
-import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
+import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
@@ -64,7 +65,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
-import java.util.Set;
+import java.util.Date;
+import java.util.HashSet;
public class OpenPgpService extends RemoteService {
@@ -88,8 +90,8 @@ public class OpenPgpService extends RemoteService {
boolean duplicateUserIdsCheck = false;
ArrayList<Long> keyIds = new ArrayList<>();
- ArrayList<String> missingUserIds = new ArrayList<>();
- ArrayList<String> duplicateUserIds = new ArrayList<>();
+ ArrayList<String> missingEmails = new ArrayList<>();
+ ArrayList<String> duplicateEmails = new ArrayList<>();
if (!noUserIdsCheck) {
for (String email : encryptionUserIds) {
// try to find the key for this specific email
@@ -102,13 +104,13 @@ public class OpenPgpService extends RemoteService {
keyIds.add(id);
} else {
missingUserIdsCheck = true;
- missingUserIds.add(email);
+ missingEmails.add(email);
Log.d(Constants.TAG, "user id missing");
}
- // another entry for this email -> too keys with the same email inside user id
+ // another entry for this email -> two keys with the same email inside user id
if (cursor != null && cursor.moveToNext()) {
duplicateUserIdsCheck = true;
- duplicateUserIds.add(email);
+ duplicateEmails.add(email);
// also pre-select
long id = cursor.getLong(cursor.getColumnIndex(KeyRings.MASTER_KEY_ID));
@@ -136,8 +138,8 @@ public class OpenPgpService extends RemoteService {
intent.setAction(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS);
intent.putExtra(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
intent.putExtra(RemoteServiceActivity.EXTRA_NO_USER_IDS_CHECK, noUserIdsCheck);
- intent.putExtra(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
- intent.putExtra(RemoteServiceActivity.EXTRA_DUPLICATE_USER_IDS, duplicateUserIds);
+ intent.putExtra(RemoteServiceActivity.EXTRA_MISSING_EMAILS, missingEmails);
+ intent.putExtra(RemoteServiceActivity.EXTRA_DUPLICATE_EMAILS, duplicateEmails);
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
@@ -164,9 +166,12 @@ public class OpenPgpService extends RemoteService {
}
private static PendingIntent getRequiredInputPendingIntent(Context context,
- Intent data, RequiredInputParcel requiredInput) {
+ Intent data,
+ RequiredInputParcel requiredInput,
+ CryptoInputParcel cryptoInput) {
switch (requiredInput.mType) {
+ case NFC_MOVE_KEY_TO_CARD:
case NFC_DECRYPT:
case NFC_SIGN: {
// build PendingIntent for YubiKey NFC operations
@@ -174,6 +179,7 @@ public class OpenPgpService extends RemoteService {
// pass params through to activity that it can be returned again later to repeat pgp operation
intent.putExtra(NfcOperationActivity.EXTRA_SERVICE_INTENT, data);
intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
+ intent.putExtra(NfcOperationActivity.EXTRA_CRYPTO_INPUT, cryptoInput);
return PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
}
@@ -184,6 +190,7 @@ public class OpenPgpService extends RemoteService {
// pass params through to activity that it can be returned again later to repeat pgp operation
intent.putExtra(PassphraseDialogActivity.EXTRA_SERVICE_INTENT, data);
intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, requiredInput);
+ intent.putExtra(PassphraseDialogActivity.EXTRA_CRYPTO_INPUT, cryptoInput);
return PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
}
@@ -241,7 +248,7 @@ public class OpenPgpService extends RemoteService {
.setCleartextSignature(cleartextSign)
.setDetachedSignature(!cleartextSign)
.setVersionHeader(null)
- .setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED);
+ .setSignatureHashAlgorithm(PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT);
Intent signKeyIdIntent = getSignKeyMasterId(data);
// NOTE: Fallback to return account settings (Old API)
@@ -278,12 +285,12 @@ public class OpenPgpService extends RemoteService {
CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
if (inputParcel == null) {
- inputParcel = new CryptoInputParcel();
+ inputParcel = new CryptoInputParcel(new Date());
}
// override passphrase in input parcel if given by API call
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
- inputParcel = new CryptoInputParcel(inputParcel.getSignatureTime(),
- new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)));
+ inputParcel.mPassphrase =
+ new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
}
// execute PGP operation!
@@ -293,7 +300,8 @@ public class OpenPgpService extends RemoteService {
if (pgpResult.isPending()) {
RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel();
- PendingIntent pIntent = getRequiredInputPendingIntent(getBaseContext(), data, requiredInput);
+ PendingIntent pIntent = getRequiredInputPendingIntent(getBaseContext(), data,
+ requiredInput, pgpResult.mCryptoInputParcel);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -351,9 +359,9 @@ public class OpenPgpService extends RemoteService {
boolean enableCompression = data.getBooleanExtra(OpenPgpApi.EXTRA_ENABLE_COMPRESSION, true);
int compressionId;
if (enableCompression) {
- compressionId = CompressionAlgorithmTags.ZLIB;
+ compressionId = PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.USE_DEFAULT;
} else {
- compressionId = CompressionAlgorithmTags.UNCOMPRESSED;
+ compressionId = PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.UNCOMPRESSED;
}
// first try to get key ids from non-ambiguous key id extra
@@ -383,8 +391,8 @@ public class OpenPgpService extends RemoteService {
PgpSignEncryptInputParcel pseInput = new PgpSignEncryptInputParcel();
pseInput.setEnableAsciiArmorOutput(asciiArmor)
.setVersionHeader(null)
- .setCompressionId(compressionId)
- .setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED)
+ .setCompressionAlgorithm(compressionId)
+ .setSymmetricEncryptionAlgorithm(PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT)
.setEncryptionMasterKeyIds(keyIds)
.setFailOnMissingEncryptionKeyIds(true);
@@ -413,7 +421,7 @@ public class OpenPgpService extends RemoteService {
}
// sign and encrypt
- pseInput.setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)
+ pseInput.setSignatureHashAlgorithm(PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT)
.setAdditionalEncryptId(signKeyId); // add sign key for encryption
}
@@ -433,12 +441,12 @@ public class OpenPgpService extends RemoteService {
CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
if (inputParcel == null) {
- inputParcel = new CryptoInputParcel();
+ inputParcel = new CryptoInputParcel(new Date());
}
// override passphrase in input parcel if given by API call
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
- inputParcel = new CryptoInputParcel(inputParcel.getSignatureTime(),
- new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)));
+ inputParcel.mPassphrase =
+ new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
}
PgpSignEncryptOperation op = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null);
@@ -448,7 +456,8 @@ public class OpenPgpService extends RemoteService {
if (pgpResult.isPending()) {
RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel();
- PendingIntent pIntent = getRequiredInputPendingIntent(getBaseContext(), data, requiredInput);
+ PendingIntent pIntent = getRequiredInputPendingIntent(getBaseContext(), data,
+ requiredInput, pgpResult.mCryptoInputParcel);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -488,23 +497,23 @@ public class OpenPgpService extends RemoteService {
}
}
- private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor input,
+ private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor inputDescriptor,
ParcelFileDescriptor output, boolean decryptMetadataOnly) {
- InputStream is = null;
- OutputStream os = null;
+ InputStream inputStream = null;
+ OutputStream outputStream = null;
try {
// Get Input- and OutputStream from ParcelFileDescriptor
- is = new ParcelFileDescriptor.AutoCloseInputStream(input);
+ inputStream = new ParcelFileDescriptor.AutoCloseInputStream(inputDescriptor);
// output is optional, e.g., for verifying detached signatures
if (decryptMetadataOnly || output == null) {
- os = null;
+ outputStream = null;
} else {
- os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
+ outputStream = new ParcelFileDescriptor.AutoCloseOutputStream(output);
}
String currentPkg = getCurrentCallingPackage();
- Set<Long> allowedKeyIds = mProviderHelper.getAllowedKeyIdsForApp(
+ HashSet<Long> allowedKeyIds = mProviderHelper.getAllowedKeyIdsForApp(
KeychainContract.ApiAllowedKeys.buildBaseUri(currentPkg));
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 7) {
@@ -512,38 +521,38 @@ public class OpenPgpService extends RemoteService {
ApiAccounts.buildBaseUri(currentPkg)));
}
- long inputLength = is.available();
- InputData inputData = new InputData(is, inputLength);
-
- PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
- this, new ProviderHelper(getContext()), null, inputData, os
- );
-
- CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
- if (inputParcel == null) {
- inputParcel = new CryptoInputParcel();
+ CryptoInputParcel cryptoInput = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
+ if (cryptoInput == null) {
+ cryptoInput = new CryptoInputParcel();
}
// override passphrase in input parcel if given by API call
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
- inputParcel = new CryptoInputParcel(inputParcel.getSignatureTime(),
- new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)));
+ cryptoInput.mPassphrase =
+ new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
}
byte[] detachedSignature = data.getByteArrayExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE);
+ PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(this, mProviderHelper, null);
+
+ long inputLength = inputStream.available();
+ InputData inputData = new InputData(inputStream, inputLength);
+
// allow only private keys associated with accounts of this app
// no support for symmetric encryption
- builder.setAllowSymmetricDecryption(false)
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel()
+ .setAllowSymmetricDecryption(false)
.setAllowedKeyIds(allowedKeyIds)
.setDecryptMetadataOnly(decryptMetadataOnly)
.setDetachedSignature(detachedSignature);
- DecryptVerifyResult pgpResult = builder.build().execute(inputParcel);
+ DecryptVerifyResult pgpResult = op.execute(input, cryptoInput, inputData, outputStream);
if (pgpResult.isPending()) {
// prepare and return PendingIntent to be executed by client
RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel();
- PendingIntent pIntent = getRequiredInputPendingIntent(getBaseContext(), data, requiredInput);
+ PendingIntent pIntent = getRequiredInputPendingIntent(getBaseContext(), data,
+ requiredInput, pgpResult.mCryptoInputParcel);
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_INTENT, pIntent);
@@ -554,40 +563,71 @@ public class OpenPgpService extends RemoteService {
Intent result = new Intent();
OpenPgpSignatureResult signatureResult = pgpResult.getSignatureResult();
- // TODO: currently RESULT_TYPE_UNENCRYPTED_UNSIGNED is never returned
- // instead an error is returned when no pgp data has been found
- int resultType = OpenPgpApi.RESULT_TYPE_UNENCRYPTED_UNSIGNED;
- if (signatureResult != null) {
- resultType |= OpenPgpApi.RESULT_TYPE_SIGNED;
- if (!signatureResult.isSignatureOnly()) {
- resultType |= OpenPgpApi.RESULT_TYPE_ENCRYPTED;
+
+ result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult);
+
+ if (signatureResult.getResult() == OpenPgpSignatureResult.RESULT_KEY_MISSING) {
+ // If signature is unknown we return an _additional_ PendingIntent
+ // to retrieve the missing key
+ result.putExtra(OpenPgpApi.RESULT_INTENT, getKeyserverPendingIntent(data, signatureResult.getKeyId()));
+ } else {
+ // If signature key is known, return PendingIntent to show key
+ result.putExtra(OpenPgpApi.RESULT_INTENT, getShowKeyPendingIntent(signatureResult.getKeyId()));
+ }
+
+ if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 5) {
+ // RESULT_INVALID_KEY_REVOKED and RESULT_INVALID_KEY_EXPIRED have been added in version 5
+ if (signatureResult.getResult() == OpenPgpSignatureResult.RESULT_INVALID_KEY_REVOKED
+ || signatureResult.getResult() == OpenPgpSignatureResult.RESULT_INVALID_KEY_EXPIRED) {
+ signatureResult.setResult(OpenPgpSignatureResult.RESULT_INVALID_SIGNATURE);
+ }
+ }
+
+ if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 8) {
+ // RESULT_INVALID_INSECURE has been added in version 8, fallback to RESULT_INVALID_SIGNATURE
+ if (signatureResult.getResult() == OpenPgpSignatureResult.RESULT_INVALID_INSECURE) {
+ signatureResult.setResult(OpenPgpSignatureResult.RESULT_INVALID_SIGNATURE);
+ }
+
+ // RESULT_NO_SIGNATURE has been added in version 8, before the signatureResult was null
+ if (signatureResult.getResult() == OpenPgpSignatureResult.RESULT_NO_SIGNATURE) {
+ result.putExtra(OpenPgpApi.RESULT_SIGNATURE, (Parcelable[]) null);
}
- result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult);
+ // OpenPgpDecryptionResult does not exist in API < 8
+ {
+ OpenPgpDecryptionResult decryptionResult = pgpResult.getDecryptionResult();
- if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 5) {
- // SIGNATURE_KEY_REVOKED and SIGNATURE_KEY_EXPIRED have been added in version 5
- if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED
- || signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED) {
- signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR);
+ // case RESULT_NOT_ENCRYPTED, but a signature, fallback to deprecated signatureOnly variable
+ if (decryptionResult.getResult() == OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED
+ && signatureResult.getResult() != OpenPgpSignatureResult.RESULT_NO_SIGNATURE) {
+ signatureResult.setSignatureOnly(true);
}
+
+ // case RESULT_INSECURE, fallback to an error
+ if (decryptionResult.getResult() == OpenPgpDecryptionResult.RESULT_INSECURE) {
+ Intent resultError = new Intent();
+ resultError.putExtra(OpenPgpApi.RESULT_ERROR, new OpenPgpError(OpenPgpError.GENERIC_ERROR,
+ "Insecure encryption: An outdated algorithm has been used!"));
+ resultError.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
+ return resultError;
+ }
+
+ // case RESULT_ENCRYPTED
+ // nothing to do!
}
+ }
- if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_MISSING) {
- // If signature is unknown we return an _additional_ PendingIntent
- // to retrieve the missing key
- result.putExtra(OpenPgpApi.RESULT_INTENT, getKeyserverPendingIntent(data, signatureResult.getKeyId()));
- } else {
- // If signature key is known, return PendingIntent to show key
- result.putExtra(OpenPgpApi.RESULT_INTENT, getShowKeyPendingIntent(signatureResult.getKeyId()));
+ if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 8) {
+ OpenPgpDecryptionResult decryptionResult = pgpResult.getDecryptionResult();
+ if (decryptionResult != null) {
+ result.putExtra(OpenPgpApi.RESULT_DECRYPTION, decryptionResult);
}
- } else {
- resultType |= OpenPgpApi.RESULT_TYPE_ENCRYPTED;
}
- result.putExtra(OpenPgpApi.RESULT_TYPE, resultType);
+
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 4) {
- OpenPgpMetadata metadata = pgpResult.getDecryptMetadata();
+ OpenPgpMetadata metadata = pgpResult.getDecryptionMetadata();
if (metadata != null) {
result.putExtra(OpenPgpApi.RESULT_METADATA, metadata);
}
@@ -601,9 +641,8 @@ public class OpenPgpService extends RemoteService {
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
return result;
} else {
- LogEntryParcel errorMsg = pgpResult.getLog().getLast();
-
- if (errorMsg.mType == OperationResult.LogType.MSG_DC_ERROR_NO_KEY) {
+ //
+ if (pgpResult.isKeysDisallowed()) {
// allow user to select allowed keys
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_INTENT, getSelectAllowedKeysIntent(data));
@@ -611,32 +650,36 @@ public class OpenPgpService extends RemoteService {
return result;
}
- throw new Exception(getString(errorMsg.mType.getMsgId()));
+ String errorMsg = getString(pgpResult.getLog().getLast().mType.getMsgId());
+ Intent result = new Intent();
+ result.putExtra(OpenPgpApi.RESULT_ERROR, new OpenPgpError(OpenPgpError.GENERIC_ERROR, errorMsg));
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
+ return result;
}
- } catch (Exception e) {
- Log.d(Constants.TAG, "decryptAndVerifyImpl", e);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "decryptAndVerifyImpl", e);
Intent result = new Intent();
- result.putExtra(OpenPgpApi.RESULT_ERROR,
- new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
+ result.putExtra(OpenPgpApi.RESULT_ERROR, new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
return result;
} finally {
- if (is != null) {
+ if (inputStream != null) {
try {
- is.close();
+ inputStream.close();
} catch (IOException e) {
Log.e(Constants.TAG, "IOException when closing InputStream", e);
}
}
- if (os != null) {
+ if (outputStream != null) {
try {
- os.close();
+ outputStream.close();
} catch (IOException e) {
Log.e(Constants.TAG, "IOException when closing OutputStream", e);
}
}
}
+
}
private Intent getKeyImpl(Intent data) {
@@ -672,28 +715,40 @@ public class OpenPgpService extends RemoteService {
}
private Intent getSignKeyIdImpl(Intent data) {
- String preferredUserId = data.getStringExtra(OpenPgpApi.EXTRA_USER_ID);
+ // if data already contains EXTRA_SIGN_KEY_ID, it has been executed again
+ // after user interaction. Then, we just need to return the long again!
+ if (data.hasExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
+ long signKeyId = data.getLongExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID,
+ Constants.key.none);
- Intent intent = new Intent(getBaseContext(), SelectSignKeyIdActivity.class);
- String currentPkg = getCurrentCallingPackage();
- intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(currentPkg));
- intent.putExtra(SelectSignKeyIdActivity.EXTRA_USER_ID, preferredUserId);
- intent.putExtra(SelectSignKeyIdActivity.EXTRA_DATA, data);
+ Intent result = new Intent();
+ result.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, signKeyId);
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
+ return result;
+ } else {
+ String preferredUserId = data.getStringExtra(OpenPgpApi.EXTRA_USER_ID);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
- intent,
- PendingIntent.FLAG_CANCEL_CURRENT);
+ Intent intent = new Intent(getBaseContext(), SelectSignKeyIdActivity.class);
+ String currentPkg = getCurrentCallingPackage();
+ intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(currentPkg));
+ intent.putExtra(SelectSignKeyIdActivity.EXTRA_USER_ID, preferredUserId);
+ intent.putExtra(SelectSignKeyIdActivity.EXTRA_DATA, data);
+
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
- // return PendingIntent to be executed by client
- Intent result = new Intent();
- result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
- result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
+ // return PendingIntent to be executed by client
+ Intent result = new Intent();
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
+ result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
- return result;
+ return result;
+ }
}
private Intent getKeyIdsImpl(Intent data) {
- // if data already contains key ids extra GET_KEY_IDS has been executed again
+ // if data already contains EXTRA_KEY_IDS, it has been executed again
// after user interaction. Then, we just need to return the array again!
if (data.hasExtra(OpenPgpApi.EXTRA_KEY_IDS)) {
long[] keyIdsArray = data.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS);
@@ -763,12 +818,13 @@ public class OpenPgpService extends RemoteService {
&& data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 4
&& data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 5
&& data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 6
- && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 7) {
+ && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 7
+ && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 8) {
Intent result = new Intent();
OpenPgpError error = new OpenPgpError
(OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!\n"
+ "used API version: " + data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) + "\n"
- + "supported API versions: 3-7");
+ + "supported API versions: 3-8");
result.putExtra(OpenPgpApi.RESULT_ERROR, error);
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
return result;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java
index e4d4ac49a..792a4d253 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java
@@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.remote;
+import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
@@ -65,12 +66,11 @@ public abstract class RemoteService extends Service {
/**
* Checks if caller is allowed to access the API
*
- * @param data
* @return null if caller is allowed, or a Bundle with a PendingIntent
*/
protected Intent isAllowed(Intent data) {
try {
- if (isCallerAllowed(false)) {
+ if (isCallerAllowed()) {
return null;
} else {
String packageName = getCurrentCallingPackage();
@@ -130,8 +130,8 @@ public abstract class RemoteService extends Service {
}
private byte[] getPackageCertificate(String packageName) throws NameNotFoundException {
- PackageInfo pkgInfo = getPackageManager().getPackageInfo(packageName,
- PackageManager.GET_SIGNATURES);
+ @SuppressLint("PackageManagerGetSignatures") // we do check the byte array of *all* signatures
+ PackageInfo pkgInfo = getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
// NOTE: Silly Android API naming: Signatures are actually certificates
Signature[] certificates = pkgInfo.signatures;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
@@ -211,22 +211,15 @@ public abstract class RemoteService extends Service {
* 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
* @return true if process is allowed to use this service
* @throws WrongPackageCertificateException
*/
- private boolean isCallerAllowed(boolean allowOnlySelf) throws WrongPackageCertificateException {
- return isUidAllowed(Binder.getCallingUid(), allowOnlySelf);
+ private boolean isCallerAllowed() throws WrongPackageCertificateException {
+ return isUidAllowed(Binder.getCallingUid());
}
- private boolean isUidAllowed(int uid, boolean allowOnlySelf)
+ private boolean isUidAllowed(int uid)
throws WrongPackageCertificateException {
- if (android.os.Process.myUid() == uid) {
- return true;
- }
- if (allowOnlySelf) { // barrier
- return false;
- }
String[] callingPackages = getPackageManager().getPackagesForUid(uid);
@@ -237,7 +230,7 @@ public abstract class RemoteService extends Service {
}
}
- Log.d(Constants.TAG, "Uid is NOT allowed!");
+ Log.e(Constants.TAG, "Uid is NOT allowed!");
return false;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
index 81181d61d..18afd2f23 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
@@ -58,7 +58,7 @@ public class AccountSettingsFragment extends Fragment {
this.mAccSettings = accountSettings;
mAccNameView.setText(accountSettings.getAccountName());
- mSelectKeySpinner.setSelectedKeyId(accountSettings.getKeyId());
+ mSelectKeySpinner.setPreSelectedKeyId(accountSettings.getKeyId());
}
/**
@@ -107,7 +107,7 @@ public class AccountSettingsFragment extends Fragment {
if (resultCode == Activity.RESULT_OK) {
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
EditKeyResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
- mSelectKeySpinner.setSelectedKeyId(result.mMasterKeyId);
+ mSelectKeySpinner.setPreSelectedKeyId(result.mMasterKeyId);
} else {
Log.e(Constants.TAG, "missing result!");
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
index 2b71d6dc1..4eee73e01 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
@@ -75,7 +75,7 @@ public class AppSettingsActivity extends BaseActivity {
mAppNameView = (TextView) findViewById(R.id.api_app_settings_app_name);
mAppIconView = (ImageView) findViewById(R.id.api_app_settings_app_icon);
mPackageName = (TextView) findViewById(R.id.api_app_settings_package_name);
- mPackageSignature = (TextView) findViewById(R.id.api_app_settings_package_signature);
+ mPackageSignature = (TextView) findViewById(R.id.api_app_settings_package_certificate);
mStartFab = (FloatingActionButton) findViewById(R.id.fab);
mStartFab.setOnClickListener(new View.OnClickListener() {
@@ -148,19 +148,19 @@ public class AppSettingsActivity extends BaseActivity {
}
private void showAdvancedInfo() {
- String signature = null;
- // advanced info: package signature SHA-256
+ String certificate = null;
+ // advanced info: package certificate SHA-256
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
- md.update(mAppSettings.getPackageSignature());
+ md.update(mAppSettings.getPackageCertificate());
byte[] digest = md.digest();
- signature = new String(Hex.encode(digest));
+ certificate = new String(Hex.encode(digest));
} catch (NoSuchAlgorithmException e) {
Log.e(Constants.TAG, "Should not happen!", e);
}
AdvancedAppSettingsDialogFragment dialogFragment =
- AdvancedAppSettingsDialogFragment.newInstance(mAppSettings.getPackageName(), signature);
+ AdvancedAppSettingsDialogFragment.newInstance(mAppSettings.getPackageName(), certificate);
dialogFragment.show(getSupportFragmentManager(), "advancedDialog");
}
@@ -217,13 +217,15 @@ public class AppSettingsActivity extends BaseActivity {
// show accounts only if available (deprecated API)
Cursor cursor = getContentResolver().query(accountsUri, null, null, null, null);
- if (cursor.moveToFirst()) {
+ if (cursor != null && cursor.moveToFirst()) try {
mAccountsLabel.setVisibility(View.VISIBLE);
mAccountsListFragment = AccountsListFragment.newInstance(accountsUri);
// Create an instance of the fragments
getSupportFragmentManager().beginTransaction()
.replace(R.id.api_accounts_list_fragment, mAccountsListFragment)
.commitAllowingStateLoss();
+ } finally {
+ cursor.close();
}
// Create an instance of the fragments
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java
index b880525ca..caa173f03 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsAllowedKeysListFragment.java
@@ -17,10 +17,11 @@
package org.sufficientlysecure.keychain.remote.ui;
-import android.content.Context;
+
+import java.util.Set;
+
import android.content.OperationApplicationException;
import android.database.Cursor;
-import android.database.DatabaseUtils;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
@@ -35,23 +36,17 @@ import android.widget.ListView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter;
+import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
+import org.sufficientlysecure.keychain.ui.adapter.KeySelectableAdapter;
import org.sufficientlysecure.keychain.ui.widget.FixedListView;
import org.sufficientlysecure.keychain.util.Log;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.Vector;
-
public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround implements LoaderManager.LoaderCallbacks<Cursor> {
private static final String ARG_DATA_URI = "uri";
- private SelectKeyCursorAdapter mAdapter;
- private Set<Long> mSelectedMasterKeyIds;
+ private KeySelectableAdapter mAdapter;
private ProviderHelper mProviderHelper;
private Uri mDataUri;
@@ -80,8 +75,7 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- View layout = super.onCreateView(inflater, container,
- savedInstanceState);
+ View layout = super.onCreateView(inflater, container, savedInstanceState);
ListView lv = (ListView) layout.findViewById(android.R.id.list);
ViewGroup parent = (ViewGroup) lv.getParent();
@@ -109,67 +103,29 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i
mDataUri = getArguments().getParcelable(ARG_DATA_URI);
- getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
-
// Give some text to display if there is no data. In a real
// application this would come from a resource.
setEmptyText(getString(R.string.list_empty));
- mAdapter = new SecretKeyCursorAdapter(getActivity(), null, 0, getListView());
-
+ Set<Long> checked = mProviderHelper.getAllKeyIdsForApp(mDataUri);
+ mAdapter = new KeySelectableAdapter(getActivity(), null, 0, checked);
setListAdapter(mAdapter);
+ getListView().setOnItemClickListener(mAdapter);
// Start out with a progress indicator.
setListShown(false);
- mSelectedMasterKeyIds = mProviderHelper.getAllKeyIdsForApp(mDataUri);
- Log.d(Constants.TAG, "allowed: " + mSelectedMasterKeyIds.toString());
-
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
- }
- /**
- * Selects items based on master key ids in list view
- *
- * @param masterKeyIds
- */
- private void preselectMasterKeyIds(Set<Long> masterKeyIds) {
- for (int i = 0; i < getListView().getCount(); ++i) {
- long listKeyId = mAdapter.getMasterKeyId(i);
- for (long keyId : masterKeyIds) {
- if (listKeyId == keyId) {
- getListView().setItemChecked(i, true);
- break;
- }
- }
- }
}
-
- /**
- * Returns all selected master key ids
- *
- * @return
- */
+ /** Returns all selected master key ids. */
public Set<Long> getSelectedMasterKeyIds() {
- // mListView.getCheckedItemIds() would give the row ids of the KeyRings not the master key
- // ids!
- Set<Long> keyIds = new HashSet<>();
- for (int i = 0; i < getListView().getCount(); ++i) {
- if (getListView().isItemChecked(i)) {
- keyIds.add(mAdapter.getMasterKeyId(i));
- }
- }
-
- return keyIds;
+ return mAdapter.getSelectedMasterKeyIds();
}
- /**
- * Returns all selected user ids
- *
- * @return
- */
+ /** Returns all selected user ids.
public String[] getSelectedUserIds() {
Vector<String> userIds = new Vector<>();
for (int i = 0; i < getListView().getCount(); ++i) {
@@ -181,7 +137,7 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i
// make empty array to not return null
String userIdArray[] = new String[0];
return userIds.toArray(userIdArray);
- }
+ } */
public void saveAllowedKeys() {
try {
@@ -192,46 +148,11 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i
}
@Override
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- Uri baseUri = KeyRings.buildUnifiedKeyRingsUri();
-
- // These are the rows that we will retrieve.
- String[] projection = new String[]{
- KeyRings._ID,
- KeyRings.MASTER_KEY_ID,
- KeyRings.USER_ID,
- KeyRings.IS_EXPIRED,
- KeyRings.IS_REVOKED,
- KeyRings.HAS_ENCRYPT,
- KeyRings.VERIFIED,
- KeyRings.HAS_ANY_SECRET,
- KeyRings.HAS_DUPLICATE_USER_ID,
- KeyRings.CREATION,
- };
-
- String inMasterKeyList = null;
- if (mSelectedMasterKeyIds != null && mSelectedMasterKeyIds.size() > 0) {
- inMasterKeyList = Tables.KEYS + "." + KeyRings.MASTER_KEY_ID + " IN (";
- Iterator iter = mSelectedMasterKeyIds.iterator();
- while (iter.hasNext()) {
- inMasterKeyList += DatabaseUtils.sqlEscapeString("" + iter.next());
- if (iter.hasNext()) {
- inMasterKeyList += ", ";
- }
- }
- inMasterKeyList += ")";
- }
-
- String selection = KeyRings.HAS_ANY_SECRET + " != 0";
+ public Loader<Cursor> onCreateLoader(int loaderId, Bundle data) {
+ Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri();
+ String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1";
- String orderBy = KeyRings.USER_ID + " ASC";
- if (inMasterKeyList != null) {
- // sort by selected master keys
- orderBy = inMasterKeyList + " DESC, " + orderBy;
- }
- // Now create and return a CursorLoader that will take care of
- // creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, projection, selection, null, orderBy);
+ return new CursorLoader(getActivity(), baseUri, KeyAdapter.PROJECTION, where, null, null);
}
@Override
@@ -246,9 +167,6 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i
} else {
setListShownNoAnimation(true);
}
-
- // preselect given master keys
- preselectMasterKeyIds(mSelectedMasterKeyIds);
}
@Override
@@ -259,36 +177,4 @@ public class AppSettingsAllowedKeysListFragment extends ListFragmentWorkaround i
mAdapter.swapCursor(null);
}
- private class SecretKeyCursorAdapter extends SelectKeyCursorAdapter {
-
- public SecretKeyCursorAdapter(Context context, Cursor c, int flags, ListView listView) {
- super(context, c, flags, listView);
- }
-
- @Override
- protected void initIndex(Cursor cursor) {
- super.initIndex(cursor);
- }
-
- @Override
- public void bindView(View view, Context context, Cursor cursor) {
- super.bindView(view, context, cursor);
- ViewHolderItem h = (ViewHolderItem) view.getTag();
-
- // We care about the checkbox
- h.selected.setVisibility(View.VISIBLE);
- // the getListView works because this is not a static subclass!
- h.selected.setChecked(getListView().isItemChecked(cursor.getPosition()));
-
- boolean enabled = false;
- if ((Boolean) h.statusIcon.getTag()) {
- h.statusIcon.setVisibility(View.GONE);
- enabled = true;
- }
-
- h.setEnabled(enabled);
- }
-
- }
-
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsHeaderFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsHeaderFragment.java
index 7beac8973..9160987ab 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsHeaderFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsHeaderFragment.java
@@ -47,7 +47,7 @@ public class AppSettingsHeaderFragment extends Fragment {
private TextView mAppNameView;
private ImageView mAppIconView;
private TextView mPackageName;
- private TextView mPackageSignature;
+ private TextView mPackageCertificate;
public AppSettings getAppSettings() {
return mAppSettings;
@@ -67,7 +67,7 @@ public class AppSettingsHeaderFragment extends Fragment {
mAppNameView = (TextView) view.findViewById(R.id.api_app_settings_app_name);
mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon);
mPackageName = (TextView) view.findViewById(R.id.api_app_settings_package_name);
- mPackageSignature = (TextView) view.findViewById(R.id.api_app_settings_package_signature);
+ mPackageCertificate = (TextView) view.findViewById(R.id.api_app_settings_package_certificate);
return view;
}
@@ -94,11 +94,11 @@ public class AppSettingsHeaderFragment extends Fragment {
// advanced info: package signature SHA-256
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
- md.update(appSettings.getPackageSignature());
+ md.update(appSettings.getPackageCertificate());
byte[] digest = md.digest();
String signature = new String(Hex.encode(digest));
- mPackageSignature.setText(signature);
+ mPackageCertificate.setText(signature);
} catch (NoSuchAlgorithmException e) {
Log.e(Constants.TAG, "Should not happen!", e);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
index 5facde64f..a2e781e8a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
@@ -69,8 +69,8 @@ public class RemoteServiceActivity extends BaseActivity {
public static final String EXTRA_ACC_NAME = "acc_name";
// select pub keys action
public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "master_key_ids";
- public static final String EXTRA_MISSING_USER_IDS = "missing_user_ids";
- public static final String EXTRA_DUPLICATE_USER_IDS = "dublicate_user_ids";
+ public static final String EXTRA_MISSING_EMAILS = "missing_emails";
+ public static final String EXTRA_DUPLICATE_EMAILS = "dublicate_emails";
public static final String EXTRA_NO_USER_IDS_CHECK = "no_user_ids";
// error message
public static final String EXTRA_ERROR_MESSAGE = "error_message";
@@ -222,10 +222,10 @@ public class RemoteServiceActivity extends BaseActivity {
case ACTION_SELECT_PUB_KEYS: {
long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
boolean noUserIdsCheck = intent.getBooleanExtra(EXTRA_NO_USER_IDS_CHECK, true);
- ArrayList<String> missingUserIds = intent
- .getStringArrayListExtra(EXTRA_MISSING_USER_IDS);
- ArrayList<String> dublicateUserIds = intent
- .getStringArrayListExtra(EXTRA_DUPLICATE_USER_IDS);
+ ArrayList<String> missingEmails = intent
+ .getStringArrayListExtra(EXTRA_MISSING_EMAILS);
+ ArrayList<String> duplicateEmails = intent
+ .getStringArrayListExtra(EXTRA_DUPLICATE_EMAILS);
SpannableStringBuilder ssb = new SpannableStringBuilder();
final SpannableString textIntro = new SpannableString(
@@ -235,23 +235,23 @@ public class RemoteServiceActivity extends BaseActivity {
textIntro.setSpan(new StyleSpan(Typeface.BOLD), 0, textIntro.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append(textIntro);
- if (missingUserIds != null && missingUserIds.size() > 0) {
+ if (missingEmails != null && missingEmails.size() > 0) {
ssb.append("\n\n");
ssb.append(getString(R.string.api_select_pub_keys_missing_text));
ssb.append("\n");
- for (String userId : missingUserIds) {
- SpannableString ss = new SpannableString(userId + "\n");
+ for (String emails : missingEmails) {
+ SpannableString ss = new SpannableString(emails + "\n");
ss.setSpan(new BulletSpan(15, Color.BLACK), 0, ss.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append(ss);
}
}
- if (dublicateUserIds != null && dublicateUserIds.size() > 0) {
+ if (duplicateEmails != null && duplicateEmails.size() > 0) {
ssb.append("\n\n");
ssb.append(getString(R.string.api_select_pub_keys_dublicates_text));
ssb.append("\n");
- for (String userId : dublicateUserIds) {
- SpannableString ss = new SpannableString(userId + "\n");
+ for (String email : duplicateEmails) {
+ SpannableString ss = new SpannableString(email + "\n");
ss.setSpan(new BulletSpan(15, Color.BLACK), 0, ss.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append(ss);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java
index cb9f46f7f..bed49a6f6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java
@@ -40,14 +40,9 @@ public class SelectSignKeyIdActivity extends BaseActivity {
private static final int REQUEST_CODE_CREATE_KEY = 0x00008884;
- private Uri mAppUri;
private String mPreferredUserId;
private Intent mData;
- private SelectSignKeyIdListFragment mListFragment;
- private TextView mActionCreateKey;
- private TextView mNone;
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -62,15 +57,15 @@ public class SelectSignKeyIdActivity extends BaseActivity {
}
});
- mActionCreateKey = (TextView) findViewById(R.id.api_select_sign_key_create_key);
- mActionCreateKey.setOnClickListener(new View.OnClickListener() {
+ TextView createKeyButton = (TextView) findViewById(R.id.api_select_sign_key_create_key);
+ createKeyButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
createKey(mPreferredUserId);
}
});
- mNone = (TextView) findViewById(R.id.api_select_sign_key_none);
- mNone.setOnClickListener(new View.OnClickListener() {
+ TextView noneButton = (TextView) findViewById(R.id.api_select_sign_key_none);
+ noneButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 0 is "none"
@@ -82,16 +77,16 @@ public class SelectSignKeyIdActivity extends BaseActivity {
});
Intent intent = getIntent();
- mAppUri = intent.getData();
+ Uri appUri = intent.getData();
mPreferredUserId = intent.getStringExtra(EXTRA_USER_ID);
mData = intent.getParcelableExtra(EXTRA_DATA);
- if (mAppUri == null) {
+ if (appUri == null) {
Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
finish();
return;
} else {
- Log.d(Constants.TAG, "uri: " + mAppUri);
- startListFragments(savedInstanceState, mAppUri, mData);
+ Log.d(Constants.TAG, "uri: " + appUri);
+ startListFragments(savedInstanceState, appUri, mData);
}
}
@@ -113,11 +108,11 @@ public class SelectSignKeyIdActivity extends BaseActivity {
}
// Create an instance of the fragments
- mListFragment = SelectSignKeyIdListFragment.newInstance(dataUri, data);
+ SelectSignKeyIdListFragment listFragment = SelectSignKeyIdListFragment.newInstance(dataUri, data);
// Add the fragment to the 'fragment_container' FrameLayout
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
getSupportFragmentManager().beginTransaction()
- .replace(R.id.api_select_sign_key_list_fragment, mListFragment)
+ .replace(R.id.api_select_sign_key_list_fragment, listFragment)
.commitAllowingStateLoss();
// do it immediately!
getSupportFragmentManager().executePendingTransactions();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
index a7571a7ac..3cdbca633 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
@@ -22,14 +22,13 @@ import android.os.Parcel;
import android.os.Parcelable;
import java.io.Serializable;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Date;
import java.util.Map;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.util.ParcelableProxy;
/**
@@ -44,6 +43,8 @@ public class CertifyActionsParcel implements Parcelable {
public ArrayList<CertifyAction> mCertifyActions = new ArrayList<>();
+ public String keyServerUri;
+
public CertifyActionsParcel(long masterKeyId) {
mMasterKeyId = masterKeyId;
mLevel = CertifyLevel.DEFAULT;
@@ -53,6 +54,7 @@ public class CertifyActionsParcel implements Parcelable {
mMasterKeyId = source.readLong();
// just like parcelables, this is meant for ad-hoc IPC only and is NOT portable!
mLevel = CertifyLevel.values()[source.readInt()];
+ keyServerUri = source.readString();
mCertifyActions = (ArrayList<CertifyAction>) source.readSerializable();
}
@@ -65,6 +67,7 @@ public class CertifyActionsParcel implements Parcelable {
public void writeToParcel(Parcel destination, int flags) {
destination.writeLong(mMasterKeyId);
destination.writeInt(mLevel.ordinal());
+ destination.writeString(keyServerUri);
destination.writeSerializable(mCertifyActions);
}
@@ -86,8 +89,7 @@ public class CertifyActionsParcel implements Parcelable {
final public ArrayList<String> mUserIds;
final public ArrayList<WrappedUserAttribute> mUserAttributes;
- public CertifyAction(long masterKeyId, List<String> userIds,
- List<WrappedUserAttribute> attributes) {
+ public CertifyAction(long masterKeyId, List<String> userIds, List<WrappedUserAttribute> attributes) {
mMasterKeyId = masterKeyId;
mUserIds = userIds == null ? null : new ArrayList<>(userIds);
mUserAttributes = attributes == null ? null : new ArrayList<>(attributes);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CloudImportService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CloudImportService.java
deleted file mode 100644
index 249586f6d..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CloudImportService.java
+++ /dev/null
@@ -1,384 +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.service;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.operations.ImportExportOperation;
-import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.ParcelableFileCache;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * When this service is started it will initiate a multi-threaded key import and when done it will
- * shut itself down.
- */
-public class CloudImportService extends Service implements Progressable {
-
- // required as extras from intent
- public static final String EXTRA_MESSENGER = "messenger";
- public static final String EXTRA_DATA = "data";
-
- // required by data bundle
- public static final String IMPORT_KEY_LIST = "import_key_list";
- public static final String IMPORT_KEY_SERVER = "import_key_server";
-
- // indicates a request to cancel the import
- public static final String ACTION_CANCEL = Constants.INTENT_PREFIX + "CANCEL";
-
- // tells the spawned threads whether the user has requested a cancel
- private static AtomicBoolean mActionCancelled = new AtomicBoolean(false);
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- /**
- * Used to accumulate the results of individual key imports
- */
- private class KeyImportAccumulator {
- private OperationResult.OperationLog mImportLog = new OperationResult.OperationLog();
- private int mTotalKeys;
- private int mImportedKeys = 0;
- private Progressable mImportProgressable;
- ArrayList<Long> mImportedMasterKeyIds = new ArrayList<Long>();
- private int mBadKeys = 0;
- private int mNewKeys = 0;
- private int mUpdatedKeys = 0;
- private int mSecret = 0;
- private int mResultType = 0;
-
- public KeyImportAccumulator(int totalKeys) {
- mTotalKeys = totalKeys;
- // ignore updates from ImportExportOperation for now
- mImportProgressable = new Progressable() {
- @Override
- public void setProgress(String message, int current, int total) {
-
- }
-
- @Override
- public void setProgress(int resourceId, int current, int total) {
-
- }
-
- @Override
- public void setProgress(int current, int total) {
-
- }
-
- @Override
- public void setPreventCancel() {
-
- }
- };
- }
-
- public Progressable getImportProgressable() {
- return mImportProgressable;
- }
-
- public int getTotalKeys() {
- return mTotalKeys;
- }
-
- public int getImportedKeys() {
- return mImportedKeys;
- }
-
- public synchronized void accumulateKeyImport(ImportKeyResult result) {
- mImportedKeys++;
- mImportLog.addAll(result.getLog().toList());//accumulates log
- mBadKeys += result.mBadKeys;
- mNewKeys += result.mNewKeys;
- mUpdatedKeys += result.mUpdatedKeys;
- mSecret += result.mSecret;
-
- long[] masterKeyIds = result.getImportedMasterKeyIds();
- for (long masterKeyId : masterKeyIds) {
- mImportedMasterKeyIds.add(masterKeyId);
- }
-
- // if any key import has been cancelled, set result type to cancelled
- // resultType is added to in getConsolidatedKayImport to account for remaining factors
- mResultType |= result.getResult() & ImportKeyResult.RESULT_CANCELLED;
- }
-
- /**
- * returns accumulated result of all imports so far
- */
- public ImportKeyResult getConsolidatedImportKeyResult() {
-
- // adding required information to mResultType
- // special case,no keys requested for import
- if (mBadKeys == 0 && mNewKeys == 0 && mUpdatedKeys == 0) {
- mResultType = ImportKeyResult.RESULT_FAIL_NOTHING;
- } else {
- if (mNewKeys > 0) {
- mResultType |= ImportKeyResult.RESULT_OK_NEWKEYS;
- }
- if (mUpdatedKeys > 0) {
- mResultType |= ImportKeyResult.RESULT_OK_UPDATED;
- }
- if (mBadKeys > 0) {
- mResultType |= ImportKeyResult.RESULT_WITH_ERRORS;
- if (mNewKeys == 0 && mUpdatedKeys == 0) {
- mResultType |= ImportKeyResult.RESULT_ERROR;
- }
- }
- if (mImportLog.containsWarnings()) {
- mResultType |= ImportKeyResult.RESULT_WARNINGS;
- }
- }
-
- long masterKeyIds[] = new long[mImportedMasterKeyIds.size()];
- for (int i = 0; i < masterKeyIds.length; i++) {
- masterKeyIds[i] = mImportedMasterKeyIds.get(i);
- }
-
- return new ImportKeyResult(mResultType, mImportLog, mNewKeys, mUpdatedKeys, mBadKeys,
- mSecret, masterKeyIds);
- }
-
- public boolean isImportFinished() {
- return mTotalKeys == mImportedKeys;
- }
- }
-
- private KeyImportAccumulator mKeyImportAccumulator;
-
- Messenger mMessenger;
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
-
- if (ACTION_CANCEL.equals(intent.getAction())) {
- mActionCancelled.set(true);
- return Service.START_NOT_STICKY;
- }
-
- mActionCancelled.set(false);//we haven't been cancelled, yet
-
- Bundle extras = intent.getExtras();
-
- mMessenger = (Messenger) extras.get(EXTRA_MESSENGER);
-
- Bundle data = extras.getBundle(EXTRA_DATA);
-
- final String keyServer = data.getString(IMPORT_KEY_SERVER);
- // keyList being null (in case key list to be reaad from cache) is checked by importKeys
- final ArrayList<ParcelableKeyRing> keyList = data.getParcelableArrayList(IMPORT_KEY_LIST);
-
- // Adding keys to the ThreadPoolExecutor takes time, we don't want to block the main thread
- Thread baseImportThread = new Thread(new Runnable() {
-
- @Override
- public void run() {
- importKeys(keyList, keyServer);
- }
- });
- baseImportThread.start();
- return Service.START_NOT_STICKY;
- }
-
- public void importKeys(ArrayList<ParcelableKeyRing> keyList, final String keyServer) {
- ParcelableFileCache<ParcelableKeyRing> cache =
- new ParcelableFileCache<>(this, "key_import.pcl");
- int totKeys = 0;
- Iterator<ParcelableKeyRing> keyListIterator = null;
- // either keyList or cache must be null, no guarantees otherwise
- if (keyList == null) {//export from cache, copied from ImportExportOperation.importKeyRings
-
- try {
- ParcelableFileCache.IteratorWithSize<ParcelableKeyRing> it = cache.readCache();
- keyListIterator = it;
- totKeys = it.getSize();
- } catch (IOException e) {
-
- // Special treatment here, we need a lot
- OperationResult.OperationLog log = new OperationResult.OperationLog();
- log.add(OperationResult.LogType.MSG_IMPORT, 0, 0);
- log.add(OperationResult.LogType.MSG_IMPORT_ERROR_IO, 0, 0);
-
- keyImportFailed(new ImportKeyResult(ImportKeyResult.RESULT_ERROR, log));
- }
- } else {
- keyListIterator = keyList.iterator();
- totKeys = keyList.size();
- }
-
-
- if (keyListIterator != null) {
- mKeyImportAccumulator = new KeyImportAccumulator(totKeys);
- setProgress(0, totKeys);
-
- final int maxThreads = 200;
- ExecutorService importExecutor = new ThreadPoolExecutor(0, maxThreads,
- 30L, TimeUnit.SECONDS,
- new SynchronousQueue<Runnable>());
-
- while (keyListIterator.hasNext()) {
-
- final ParcelableKeyRing pkRing = keyListIterator.next();
-
- Runnable importOperationRunnable = new Runnable() {
-
- @Override
- public void run() {
- ImportKeyResult result = null;
- try {
- ImportExportOperation importExportOperation = new ImportExportOperation(
- CloudImportService.this,
- new ProviderHelper(CloudImportService.this),
- mKeyImportAccumulator.getImportProgressable(),
- mActionCancelled);
-
- ArrayList<ParcelableKeyRing> list = new ArrayList<>();
- list.add(pkRing);
- result = importExportOperation.importKeyRings(list,
- keyServer);
- } finally {
- // in the off-chance that importKeyRings does something to crash the
- // thread before it can call singleKeyRingImportCompleted, our imported
- // key count will go wrong. This will cause the service to never die,
- // and the progress dialog to stay displayed. The finally block was
- // originally meant to ensure singleKeyRingImportCompleted was called,
- // and checks for null were to be introduced, but in such a scenario,
- // knowing an uncaught error exists in importKeyRings is more important.
-
- // if a null gets passed, something wrong is happening. We want a crash.
-
- singleKeyRingImportCompleted(result);
- }
- }
- };
-
- importExecutor.execute(importOperationRunnable);
- }
- }
- }
-
- private synchronized void singleKeyRingImportCompleted(ImportKeyResult result) {
- // increase imported key count and accumulate log and bad, new etc. key counts from result
- mKeyImportAccumulator.accumulateKeyImport(result);
-
- setProgress(mKeyImportAccumulator.getImportedKeys(), mKeyImportAccumulator.getTotalKeys());
-
- if (mKeyImportAccumulator.isImportFinished()) {
- ContactSyncAdapterService.requestSync();
-
- sendMessageToHandler(ServiceProgressHandler.MessageStatus.OKAY,
- mKeyImportAccumulator.getConsolidatedImportKeyResult());
-
- stopSelf();//we're done here
- }
- }
-
- private void keyImportFailed(ImportKeyResult result) {
- sendMessageToHandler(ServiceProgressHandler.MessageStatus.OKAY, result);
- }
-
- private void sendMessageToHandler(ServiceProgressHandler.MessageStatus status, Integer arg2, Bundle data) {
-
- Message msg = Message.obtain();
- assert msg != null;
- msg.arg1 = status.ordinal();
- if (arg2 != null) {
- msg.arg2 = arg2;
- }
- if (data != null) {
- msg.setData(data);
- }
-
- try {
- mMessenger.send(msg);
- } catch (RemoteException e) {
- Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
- } catch (NullPointerException e) {
- Log.w(Constants.TAG, "Messenger is null!", e);
- }
- }
-
- private void sendMessageToHandler(ServiceProgressHandler.MessageStatus status, OperationResult data) {
- Bundle bundle = new Bundle();
- bundle.putParcelable(OperationResult.EXTRA_RESULT, data);
- sendMessageToHandler(status, null, bundle);
- }
-
- private void sendMessageToHandler(ServiceProgressHandler.MessageStatus status, Bundle data) {
- sendMessageToHandler(status, null, data);
- }
-
- private void sendMessageToHandler(ServiceProgressHandler.MessageStatus status) {
- sendMessageToHandler(status, null, null);
- }
-
- /**
- * Set progress of ProgressDialog by sending message to handler on UI thread
- */
- @Override
- public synchronized void setProgress(String message, int progress, int max) {
- Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max="
- + max);
-
- Bundle data = new Bundle();
- if (message != null) {
- data.putString(ServiceProgressHandler.DATA_MESSAGE, message);
- }
- data.putInt(ServiceProgressHandler.DATA_PROGRESS, progress);
- data.putInt(ServiceProgressHandler.DATA_PROGRESS_MAX, max);
-
- sendMessageToHandler(ServiceProgressHandler.MessageStatus.UPDATE_PROGRESS, null, data);
- }
-
- @Override
- public synchronized void setProgress(int resourceId, int progress, int max) {
- setProgress(getString(resourceId), progress, max);
- }
-
- @Override
- public synchronized void setProgress(int progress, int max) {
- setProgress(null, progress, max);
- }
-
- @Override
- public synchronized void setPreventCancel() {
- sendMessageToHandler(ServiceProgressHandler.MessageStatus.PREVENT_CANCEL);
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ConsolidateInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ConsolidateInputParcel.java
new file mode 100644
index 000000000..15d109814
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ConsolidateInputParcel.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class ConsolidateInputParcel implements Parcelable {
+
+ public boolean mConsolidateRecovery;
+
+ public ConsolidateInputParcel(boolean consolidateRecovery) {
+ mConsolidateRecovery = consolidateRecovery;
+ }
+
+ protected ConsolidateInputParcel(Parcel in) {
+ mConsolidateRecovery = in.readByte() != 0x00;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte((byte) (mConsolidateRecovery ? 0x01 : 0x00));
+ }
+
+ public static final Parcelable.Creator<ConsolidateInputParcel> CREATOR = new Parcelable.Creator<ConsolidateInputParcel>() {
+ @Override
+ public ConsolidateInputParcel createFromParcel(Parcel in) {
+ return new ConsolidateInputParcel(in);
+ }
+
+ @Override
+ public ConsolidateInputParcel[] newArray(int size) {
+ return new ConsolidateInputParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
index 7688b9252..b36d23775 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
@@ -45,7 +45,7 @@ public class ContactSyncAdapterService extends Service {
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider,
final SyncResult syncResult) {
- Log.d(Constants.TAG, "Performing a sync!");
+ Log.d(Constants.TAG, "Performing a contact sync!");
// TODO: Import is currently disabled for 2.8, until we implement proper origin management
// importDone.set(false);
// KeychainApplication.setupAccountAsNeeded(ContactSyncAdapterService.this);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DeleteKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DeleteKeyringParcel.java
new file mode 100644
index 000000000..b412a6e2b
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DeleteKeyringParcel.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class DeleteKeyringParcel implements Parcelable {
+
+ public long[] mMasterKeyIds;
+ public boolean mIsSecret;
+
+ public DeleteKeyringParcel(long[] masterKeyIds, boolean isSecret) {
+ mMasterKeyIds = masterKeyIds;
+ mIsSecret = isSecret;
+ }
+
+ protected DeleteKeyringParcel(Parcel in) {
+ mIsSecret = in.readByte() != 0x00;
+ mMasterKeyIds = in.createLongArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte((byte) (mIsSecret ? 0x01 : 0x00));
+ dest.writeLongArray(mMasterKeyIds);
+ }
+
+ public static final Parcelable.Creator<DeleteKeyringParcel> CREATOR = new Parcelable.Creator<DeleteKeyringParcel>() {
+ @Override
+ public DeleteKeyringParcel createFromParcel(Parcel in) {
+ return new DeleteKeyringParcel(in);
+ }
+
+ @Override
+ public DeleteKeyringParcel[] newArray(int size) {
+ return new DeleteKeyringParcel[size];
+ }
+ };
+}
+
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java
index 41ff6d02b..18bc3cc9d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java
@@ -83,7 +83,7 @@ public class DummyAccountService extends Service {
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType,
String[] requiredFeatures, Bundle options) throws NetworkErrorException {
response.onResult(new Bundle());
- toaster.toast(R.string.info_no_manual_account_creation);
+ toaster.toast(R.string.account_no_manual_account_creation);
Log.d(Constants.TAG, "DummyAccountService.addAccount");
return null;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java
new file mode 100644
index 000000000..24c002bbd
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.service;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+
+public class ExportKeyringParcel implements Parcelable {
+ public String mKeyserver;
+ public Uri mCanonicalizedPublicKeyringUri;
+ public UncachedKeyRing mUncachedKeyRing;
+
+ public boolean mExportSecret;
+ public long mMasterKeyIds[];
+ public String mOutputFile;
+ public Uri mOutputUri;
+ public ExportType mExportType;
+
+ public enum ExportType {
+ UPLOAD_KEYSERVER,
+ EXPORT_FILE,
+ EXPORT_URI
+ }
+
+ public ExportKeyringParcel(String keyserver, Uri keyringUri) {
+ mExportType = ExportType.UPLOAD_KEYSERVER;
+ mKeyserver = keyserver;
+ mCanonicalizedPublicKeyringUri = keyringUri;
+ }
+
+ public ExportKeyringParcel(String keyserver, UncachedKeyRing uncachedKeyRing) {
+ mExportType = ExportType.UPLOAD_KEYSERVER;
+ mKeyserver = keyserver;
+ mUncachedKeyRing = uncachedKeyRing;
+ }
+
+ public ExportKeyringParcel(long[] masterKeyIds, boolean exportSecret, String outputFile) {
+ mExportType = ExportType.EXPORT_FILE;
+ mMasterKeyIds = masterKeyIds;
+ mExportSecret = exportSecret;
+ mOutputFile = outputFile;
+ }
+
+ @SuppressWarnings("unused") // TODO: is it used?
+ public ExportKeyringParcel(long[] masterKeyIds, boolean exportSecret, Uri outputUri) {
+ mExportType = ExportType.EXPORT_URI;
+ mMasterKeyIds = masterKeyIds;
+ mExportSecret = exportSecret;
+ mOutputUri = outputUri;
+ }
+
+ protected ExportKeyringParcel(Parcel in) {
+ mKeyserver = in.readString();
+ mCanonicalizedPublicKeyringUri = (Uri) in.readValue(Uri.class.getClassLoader());
+ mUncachedKeyRing = (UncachedKeyRing) in.readValue(UncachedKeyRing.class.getClassLoader());
+ mExportSecret = in.readByte() != 0x00;
+ mOutputFile = in.readString();
+ mOutputUri = (Uri) in.readValue(Uri.class.getClassLoader());
+ mExportType = (ExportType) in.readValue(ExportType.class.getClassLoader());
+ mMasterKeyIds = in.createLongArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mKeyserver);
+ dest.writeValue(mCanonicalizedPublicKeyringUri);
+ dest.writeValue(mUncachedKeyRing);
+ dest.writeByte((byte) (mExportSecret ? 0x01 : 0x00));
+ dest.writeString(mOutputFile);
+ dest.writeValue(mOutputUri);
+ dest.writeValue(mExportType);
+ dest.writeLongArray(mMasterKeyIds);
+ }
+
+ public static final Parcelable.Creator<ExportKeyringParcel> CREATOR = new Parcelable.Creator<ExportKeyringParcel>() {
+ @Override
+ public ExportKeyringParcel createFromParcel(Parcel in) {
+ return new ExportKeyringParcel(in);
+ }
+
+ @Override
+ public ExportKeyringParcel[] newArray(int size) {
+ return new ExportKeyringParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java
new file mode 100644
index 000000000..a41dd71cb
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+
+import java.util.ArrayList;
+
+public class ImportKeyringParcel implements Parcelable {
+ // if null, keys are expected to be read from a cache file in ImportExportOperations
+ public ArrayList<ParcelableKeyRing> mKeyList;
+ public String mKeyserver; // must be set if keys are to be imported from a keyserver
+
+ public ImportKeyringParcel (ArrayList<ParcelableKeyRing> keyList, String keyserver) {
+ mKeyList = keyList;
+ mKeyserver = keyserver;
+ }
+
+ protected ImportKeyringParcel(Parcel in) {
+ if (in.readByte() == 0x01) {
+ mKeyList = new ArrayList<>();
+ in.readList(mKeyList, ParcelableKeyRing.class.getClassLoader());
+ } else {
+ mKeyList = null;
+ }
+ mKeyserver = in.readString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mKeyList == null) {
+ dest.writeByte((byte) (0x00));
+ } else {
+ dest.writeByte((byte) (0x01));
+ dest.writeList(mKeyList);
+ }
+ dest.writeString(mKeyserver);
+ }
+
+ public static final Parcelable.Creator<ImportKeyringParcel> CREATOR = new Parcelable.Creator<ImportKeyringParcel>() {
+ @Override
+ public ImportKeyringParcel createFromParcel(Parcel in) {
+ return new ImportKeyringParcel(in);
+ }
+
+ @Override
+ public ImportKeyringParcel[] newArray(int size) {
+ return new ImportKeyringParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeybaseVerificationParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeybaseVerificationParcel.java
new file mode 100644
index 000000000..1872191af
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeybaseVerificationParcel.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class KeybaseVerificationParcel implements Parcelable {
+
+ public String mKeybaseProof;
+ public String mRequiredFingerprint;
+
+ public KeybaseVerificationParcel(String keybaseProof, String requiredFingerprint) {
+ mKeybaseProof = keybaseProof;
+ mRequiredFingerprint = requiredFingerprint;
+ }
+
+ protected KeybaseVerificationParcel(Parcel in) {
+ mKeybaseProof = in.readString();
+ mRequiredFingerprint = in.readString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mKeybaseProof);
+ dest.writeString(mRequiredFingerprint);
+ }
+
+ public static final Parcelable.Creator<KeybaseVerificationParcel> CREATOR = new Parcelable.Creator<KeybaseVerificationParcel>() {
+ @Override
+ public KeybaseVerificationParcel createFromParcel(Parcel in) {
+ return new KeybaseVerificationParcel(in);
+ }
+
+ @Override
+ public KeybaseVerificationParcel[] newArray(int size) {
+ return new KeybaseVerificationParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
deleted file mode 100644
index 63ea6285c..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ /dev/null
@@ -1,752 +0,0 @@
-/*
- * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.service;
-
-import android.app.IntentService;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-
-import com.textuality.keybase.lib.Proof;
-import com.textuality.keybase.lib.prover.Prover;
-
-import org.json.JSONObject;
-import org.spongycastle.openpgp.PGPUtil;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
-import org.sufficientlysecure.keychain.keyimport.Keyserver;
-import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.operations.CertifyOperation;
-import org.sufficientlysecure.keychain.operations.DeleteOperation;
-import org.sufficientlysecure.keychain.operations.EditKeyOperation;
-import org.sufficientlysecure.keychain.operations.ImportExportOperation;
-import org.sufficientlysecure.keychain.operations.PromoteKeyOperation;
-import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
-import org.sufficientlysecure.keychain.operations.results.CertifyResult;
-import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
-import org.sufficientlysecure.keychain.operations.results.DeleteResult;
-import org.sufficientlysecure.keychain.operations.results.ExportResult;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
-import org.sufficientlysecure.keychain.operations.results.CertifyResult;
-import org.sufficientlysecure.keychain.util.FileHelper;
-import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
-import org.sufficientlysecure.keychain.util.Preferences;
-import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
-import org.sufficientlysecure.keychain.keyimport.Keyserver;
-import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
-import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
-import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
-import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
-import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
-import org.sufficientlysecure.keychain.util.FileHelper;
-import org.sufficientlysecure.keychain.util.InputData;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.ParcelableFileCache;
-import org.sufficientlysecure.keychain.util.Passphrase;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import de.measite.minidns.Client;
-import de.measite.minidns.DNSMessage;
-import de.measite.minidns.Question;
-import de.measite.minidns.Record;
-import de.measite.minidns.record.Data;
-import de.measite.minidns.record.TXT;
-
-/**
- * This Service contains all important long lasting operations for OpenKeychain. It receives Intents with
- * data from the activities or other apps, queues these intents, executes them, and stops itself
- * after doing them.
- */
-public class KeychainIntentService extends IntentService implements Progressable {
-
- /* extras that can be given by intent */
- public static final String EXTRA_MESSENGER = "messenger";
- public static final String EXTRA_DATA = "data";
-
- /* possible actions */
- public static final String ACTION_SIGN_ENCRYPT = Constants.INTENT_PREFIX + "SIGN_ENCRYPT";
-
- public static final String ACTION_DECRYPT_VERIFY = Constants.INTENT_PREFIX + "DECRYPT_VERIFY";
-
- public static final String ACTION_VERIFY_KEYBASE_PROOF = Constants.INTENT_PREFIX + "VERIFY_KEYBASE_PROOF";
-
- public static final String ACTION_DECRYPT_METADATA = Constants.INTENT_PREFIX + "DECRYPT_METADATA";
-
- public static final String ACTION_EDIT_KEYRING = Constants.INTENT_PREFIX + "EDIT_KEYRING";
-
- public static final String ACTION_PROMOTE_KEYRING = Constants.INTENT_PREFIX + "PROMOTE_KEYRING";
-
- public static final String ACTION_IMPORT_KEYRING = Constants.INTENT_PREFIX + "IMPORT_KEYRING";
- public static final String ACTION_EXPORT_KEYRING = Constants.INTENT_PREFIX + "EXPORT_KEYRING";
-
- public static final String ACTION_UPLOAD_KEYRING = Constants.INTENT_PREFIX + "UPLOAD_KEYRING";
-
- public static final String ACTION_CERTIFY_KEYRING = Constants.INTENT_PREFIX + "SIGN_KEYRING";
-
- public static final String ACTION_DELETE = Constants.INTENT_PREFIX + "DELETE";
-
- public static final String ACTION_CONSOLIDATE = Constants.INTENT_PREFIX + "CONSOLIDATE";
-
- public static final String ACTION_CANCEL = Constants.INTENT_PREFIX + "CANCEL";
-
- /* keys for data bundle */
-
- // encrypt, decrypt, import export
- public static final String TARGET = "target";
- public static final String SOURCE = "source";
-
- // possible targets:
- public static enum IOType {
- UNKNOWN,
- BYTES,
- URI;
-
- private static final IOType[] values = values();
-
- public static IOType fromInt(int n) {
- if (n < 0 || n >= values.length) {
- return UNKNOWN;
- } else {
- return values[n];
- }
- }
- }
-
- // encrypt
- public static final String ENCRYPT_DECRYPT_INPUT_URI = "input_uri";
- public static final String ENCRYPT_DECRYPT_OUTPUT_URI = "output_uri";
- public static final String SIGN_ENCRYPT_PARCEL = "sign_encrypt_parcel";
-
- // decrypt/verify
- public static final String DECRYPT_CIPHERTEXT_BYTES = "ciphertext_bytes";
-
- // keybase proof
- public static final String KEYBASE_REQUIRED_FINGERPRINT = "keybase_required_fingerprint";
- public static final String KEYBASE_PROOF = "keybase_proof";
-
- // save keyring
- public static final String EDIT_KEYRING_PARCEL = "save_parcel";
- public static final String EDIT_KEYRING_PASSPHRASE = "passphrase";
- public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
-
- // delete keyring(s)
- public static final String DELETE_KEY_LIST = "delete_list";
- public static final String DELETE_IS_SECRET = "delete_is_secret";
-
- // import key
- public static final String IMPORT_KEY_LIST = "import_key_list";
- public static final String IMPORT_KEY_SERVER = "import_key_server";
-
- // export key
- public static final String EXPORT_FILENAME = "export_filename";
- public static final String EXPORT_URI = "export_uri";
- public static final String EXPORT_SECRET = "export_secret";
- public static final String EXPORT_ALL = "export_all";
- public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "export_key_ring_id";
-
- // upload key
- public static final String UPLOAD_KEY_SERVER = "upload_key_server";
-
- // certify key
- public static final String CERTIFY_PARCEL = "certify_parcel";
-
- // promote key
- public static final String PROMOTE_MASTER_KEY_ID = "promote_master_key_id";
- public static final String PROMOTE_CARD_AID = "promote_card_aid";
-
- // consolidate
- public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery";
-
-
- /*
- * possible data keys as result send over messenger
- */
-
- // decrypt/verify
- public static final String RESULT_DECRYPTED_BYTES = "decrypted_data";
-
- Messenger mMessenger;
-
- // this attribute can possibly merged with the one above? not sure...
- private AtomicBoolean mActionCanceled = new AtomicBoolean(false);
-
- public KeychainIntentService() {
- super("KeychainIntentService");
- }
-
- /**
- * The IntentService calls this method from the default worker thread with the intent that
- * started the service. When this method returns, IntentService stops the service, as
- * appropriate.
- */
- @Override
- protected void onHandleIntent(Intent intent) {
-
- // We have not been cancelled! (yet)
- mActionCanceled.set(false);
-
- Bundle extras = intent.getExtras();
- if (extras == null) {
- Log.e(Constants.TAG, "Extras bundle is null!");
- return;
- }
-
- if (!(extras.containsKey(EXTRA_MESSENGER) || extras.containsKey(EXTRA_DATA) || (intent
- .getAction() == null))) {
- Log.e(Constants.TAG,
- "Extra bundle must contain a messenger, a data bundle, and an action!");
- return;
- }
-
- Uri dataUri = intent.getData();
-
- mMessenger = (Messenger) extras.get(EXTRA_MESSENGER);
- Bundle data = extras.getBundle(EXTRA_DATA);
- if (data == null) {
- Log.e(Constants.TAG, "data extra is null!");
- return;
- }
-
- Log.logDebugBundle(data, "EXTRA_DATA");
-
- ProviderHelper providerHelper = new ProviderHelper(this);
-
- String action = intent.getAction();
-
- // executeServiceMethod action from extra bundle
- switch (action) {
- case ACTION_CERTIFY_KEYRING: {
-
- // Input
- CertifyActionsParcel parcel = data.getParcelable(CERTIFY_PARCEL);
- CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
- String keyServerUri = data.getString(UPLOAD_KEY_SERVER);
-
- // Operation
- CertifyOperation op = new CertifyOperation(this, providerHelper, this, mActionCanceled);
- CertifyResult result = op.certify(parcel, cryptoInput, keyServerUri);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_CONSOLIDATE: {
-
- // Operation
- ConsolidateResult result;
- if (data.containsKey(CONSOLIDATE_RECOVERY) && data.getBoolean(CONSOLIDATE_RECOVERY)) {
- result = new ProviderHelper(this).consolidateDatabaseStep2(this);
- } else {
- result = new ProviderHelper(this).consolidateDatabaseStep1(this);
- }
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_DECRYPT_METADATA: {
-
- try {
- /* Input */
- CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
-
- InputData inputData = createDecryptInputData(data);
-
- // verifyText and decrypt returning additional resultData values for the
- // verification of signatures
- PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
- this, new ProviderHelper(this), this, inputData, null
- );
- builder.setAllowSymmetricDecryption(true)
- .setDecryptMetadataOnly(true);
-
- DecryptVerifyResult decryptVerifyResult = builder.build().execute(cryptoInput);
-
- sendMessageToHandler(MessageStatus.OKAY, decryptVerifyResult);
- } catch (Exception e) {
- sendErrorToHandler(e);
- }
-
- break;
- }
- case ACTION_VERIFY_KEYBASE_PROOF: {
-
- try {
- Proof proof = new Proof(new JSONObject(data.getString(KEYBASE_PROOF)));
- setProgress(R.string.keybase_message_fetching_data, 0, 100);
-
- Prover prover = Prover.findProverFor(proof);
-
- if (prover == null) {
- sendProofError(getString(R.string.keybase_no_prover_found) + ": " + proof.getPrettyName());
- return;
- }
-
- if (!prover.fetchProofData()) {
- sendProofError(prover.getLog(), getString(R.string.keybase_problem_fetching_evidence));
- return;
- }
- String requiredFingerprint = data.getString(KEYBASE_REQUIRED_FINGERPRINT);
- if (!prover.checkFingerprint(requiredFingerprint)) {
- sendProofError(getString(R.string.keybase_key_mismatch));
- return;
- }
-
- String domain = prover.dnsTxtCheckRequired();
- if (domain != null) {
- DNSMessage dnsQuery = new Client().query(new Question(domain, Record.TYPE.TXT));
- if (dnsQuery == null) {
- sendProofError(prover.getLog(), getString(R.string.keybase_dns_query_failure));
- return;
- }
- Record[] records = dnsQuery.getAnswers();
- List<List<byte[]>> extents = new ArrayList<List<byte[]>>();
- for (Record r : records) {
- Data d = r.getPayload();
- if (d instanceof TXT) {
- extents.add(((TXT) d).getExtents());
- }
- }
- if (!prover.checkDnsTxt(extents)) {
- sendProofError(prover.getLog(), null);
- return;
- }
- }
-
- byte[] messageBytes = prover.getPgpMessage().getBytes();
- if (prover.rawMessageCheckRequired()) {
- InputStream messageByteStream = PGPUtil.getDecoderStream(new ByteArrayInputStream(messageBytes));
- if (!prover.checkRawMessageBytes(messageByteStream)) {
- sendProofError(prover.getLog(), null);
- return;
- }
- }
-
- // kind of awkward, but this whole class wants to pull bytes out of “data”
- data.putInt(KeychainIntentService.TARGET, IOType.BYTES.ordinal());
- data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, messageBytes);
-
- InputData inputData = createDecryptInputData(data);
- OutputStream outStream = createCryptOutputStream(data);
-
- PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
- this, new ProviderHelper(this), this,
- inputData, outStream
- );
- builder.setSignedLiteralData(true).setRequiredSignerFingerprint(requiredFingerprint);
-
- DecryptVerifyResult decryptVerifyResult = builder.build().execute(
- new CryptoInputParcel());
- outStream.close();
-
- if (!decryptVerifyResult.success()) {
- OperationLog log = decryptVerifyResult.getLog();
- OperationResult.LogEntryParcel lastEntry = null;
- for (OperationResult.LogEntryParcel entry : log) {
- lastEntry = entry;
- }
- sendProofError(getString(lastEntry.mType.getMsgId()));
- return;
- }
-
- if (!prover.validate(outStream.toString())) {
- sendProofError(getString(R.string.keybase_message_payload_mismatch));
- return;
- }
-
- Bundle resultData = new Bundle();
- resultData.putString(ServiceProgressHandler.DATA_MESSAGE, "OK");
-
- // these help the handler construct a useful human-readable message
- resultData.putString(ServiceProgressHandler.KEYBASE_PROOF_URL, prover.getProofUrl());
- resultData.putString(ServiceProgressHandler.KEYBASE_PRESENCE_URL, prover.getPresenceUrl());
- resultData.putString(ServiceProgressHandler.KEYBASE_PRESENCE_LABEL, prover.getPresenceLabel());
- sendMessageToHandler(MessageStatus.OKAY, resultData);
- } catch (Exception e) {
- sendErrorToHandler(e);
- }
-
- break;
- }
- case ACTION_DECRYPT_VERIFY: {
-
- try {
- /* Input */
- CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
-
- InputData inputData = createDecryptInputData(data);
- OutputStream outStream = createCryptOutputStream(data);
-
- /* Operation */
- Bundle resultData = new Bundle();
-
- // verifyText and decrypt returning additional resultData values for the
- // verification of signatures
- PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
- this, new ProviderHelper(this), this,
- inputData, outStream
- );
- builder.setAllowSymmetricDecryption(true);
-
- DecryptVerifyResult decryptVerifyResult = builder.build().execute(cryptoInput);
-
- outStream.close();
-
- resultData.putParcelable(DecryptVerifyResult.EXTRA_RESULT, decryptVerifyResult);
-
- /* Output */
- finalizeDecryptOutputStream(data, resultData, outStream);
- Log.logDebugBundle(resultData, "resultData");
-
- sendMessageToHandler(MessageStatus.OKAY, resultData);
-
- } catch (IOException | PgpGeneralException e) {
- // TODO get rid of this!
- sendErrorToHandler(e);
- }
-
- break;
- }
- case ACTION_DELETE: {
-
- // Input
- long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST);
- boolean isSecret = data.getBoolean(DELETE_IS_SECRET);
-
- // Operation
- DeleteOperation op = new DeleteOperation(this, new ProviderHelper(this), this);
- DeleteResult result = op.execute(masterKeyIds, isSecret);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_EDIT_KEYRING: {
-
- // Input
- SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL);
- CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
-
- // Operation
- EditKeyOperation op = new EditKeyOperation(this, providerHelper, this, mActionCanceled);
- OperationResult result = op.execute(saveParcel, cryptoInput);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_PROMOTE_KEYRING: {
-
- // Input
- long keyRingId = data.getLong(PROMOTE_MASTER_KEY_ID);
- byte[] cardAid = data.getByteArray(PROMOTE_CARD_AID);
-
- // Operation
- PromoteKeyOperation op = new PromoteKeyOperation(this, providerHelper, this, mActionCanceled);
- PromoteKeyResult result = op.execute(keyRingId, cardAid);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_EXPORT_KEYRING: {
-
- // Input
- boolean exportSecret = data.getBoolean(EXPORT_SECRET, false);
- String outputFile = data.getString(EXPORT_FILENAME);
- Uri outputUri = data.getParcelable(EXPORT_URI);
-
- boolean exportAll = data.getBoolean(EXPORT_ALL);
- long[] masterKeyIds = exportAll ? null : data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
-
- // Operation
- ImportExportOperation importExportOperation = new ImportExportOperation(this, new ProviderHelper(this), this);
- ExportResult result;
- if (outputFile != null) {
- result = importExportOperation.exportToFile(masterKeyIds, exportSecret, outputFile);
- } else {
- result = importExportOperation.exportToUri(masterKeyIds, exportSecret, outputUri);
- }
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_IMPORT_KEYRING: {
-
- // Input
- String keyServer = data.getString(IMPORT_KEY_SERVER);
- ArrayList<ParcelableKeyRing> list = data.getParcelableArrayList(IMPORT_KEY_LIST);
- ParcelableFileCache<ParcelableKeyRing> cache =
- new ParcelableFileCache<>(this, "key_import.pcl");
-
- // Operation
- ImportExportOperation importExportOperation = new ImportExportOperation(
- this, providerHelper, this, mActionCanceled);
- // Either list or cache must be null, no guarantees otherwise.
- ImportKeyResult result = list != null
- ? importExportOperation.importKeyRings(list, keyServer)
- : importExportOperation.importKeyRings(cache, keyServer);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
-
- }
- case ACTION_SIGN_ENCRYPT: {
-
- // Input
- SignEncryptParcel inputParcel = data.getParcelable(SIGN_ENCRYPT_PARCEL);
- CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
-
- // Operation
- SignEncryptOperation op = new SignEncryptOperation(
- this, new ProviderHelper(this), this, mActionCanceled);
- SignEncryptResult result = op.execute(inputParcel, cryptoInput);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_UPLOAD_KEYRING: {
- try {
-
- /* Input */
- String keyServer = data.getString(UPLOAD_KEY_SERVER);
- // and dataUri!
-
- /* Operation */
- HkpKeyserver server = new HkpKeyserver(keyServer);
-
- CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri);
- ImportExportOperation importExportOperation = new ImportExportOperation(this, new ProviderHelper(this), this);
-
- try {
- importExportOperation.uploadKeyRingToServer(server, keyring);
- } catch (Keyserver.AddKeyException e) {
- throw new PgpGeneralException("Unable to export key to selected server");
- }
-
- sendMessageToHandler(MessageStatus.OKAY);
- } catch (Exception e) {
- sendErrorToHandler(e);
- }
- break;
- }
- }
- }
-
- private void sendProofError(List<String> log, String label) {
- String msg = null;
- label = (label == null) ? "" : label + ": ";
- for (String m : log) {
- Log.e(Constants.TAG, label + m);
- msg = m;
- }
- sendProofError(label + msg);
- }
-
- private void sendProofError(String msg) {
- Bundle bundle = new Bundle();
- bundle.putString(ServiceProgressHandler.DATA_ERROR, msg);
- sendMessageToHandler(MessageStatus.OKAY, bundle);
- }
-
- private void sendErrorToHandler(Exception e) {
- // TODO: Implement a better exception handling here
- // contextualize the exception, if necessary
- String message;
- if (e instanceof PgpGeneralMsgIdException) {
- e = ((PgpGeneralMsgIdException) e).getContextualized(this);
- message = e.getMessage();
- } else {
- message = e.getMessage();
- }
- Log.d(Constants.TAG, "KeychainIntentService Exception: ", e);
-
- Bundle data = new Bundle();
- data.putString(ServiceProgressHandler.DATA_ERROR, message);
- sendMessageToHandler(MessageStatus.EXCEPTION, null, data);
- }
-
- private void sendMessageToHandler(MessageStatus status, Integer arg2, Bundle data) {
-
- Message msg = Message.obtain();
- assert msg != null;
- msg.arg1 = status.ordinal();
- if (arg2 != null) {
- msg.arg2 = arg2;
- }
- if (data != null) {
- msg.setData(data);
- }
-
- try {
- mMessenger.send(msg);
- } catch (RemoteException e) {
- Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
- } catch (NullPointerException e) {
- Log.w(Constants.TAG, "Messenger is null!", e);
- }
- }
-
- private void sendMessageToHandler(MessageStatus status, OperationResult data) {
- Bundle bundle = new Bundle();
- bundle.putParcelable(OperationResult.EXTRA_RESULT, data);
- sendMessageToHandler(status, null, bundle);
- }
-
- private void sendMessageToHandler(MessageStatus status, Bundle data) {
- sendMessageToHandler(status, null, data);
- }
-
- private void sendMessageToHandler(MessageStatus status) {
- sendMessageToHandler(status, null, null);
- }
-
- /**
- * Set progress of ProgressDialog by sending message to handler on UI thread
- */
- public void setProgress(String message, int progress, int max) {
- Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max="
- + max);
-
- Bundle data = new Bundle();
- if (message != null) {
- data.putString(ServiceProgressHandler.DATA_MESSAGE, message);
- }
- data.putInt(ServiceProgressHandler.DATA_PROGRESS, progress);
- data.putInt(ServiceProgressHandler.DATA_PROGRESS_MAX, max);
-
- sendMessageToHandler(MessageStatus.UPDATE_PROGRESS, null, data);
- }
-
- public void setProgress(int resourceId, int progress, int max) {
- setProgress(getString(resourceId), progress, max);
- }
-
- public void setProgress(int progress, int max) {
- setProgress(null, progress, max);
- }
-
- @Override
- public void setPreventCancel() {
- sendMessageToHandler(MessageStatus.PREVENT_CANCEL);
- }
-
- private InputData createDecryptInputData(Bundle data) throws IOException, PgpGeneralException {
- return createCryptInputData(data, DECRYPT_CIPHERTEXT_BYTES);
- }
-
- private InputData createCryptInputData(Bundle data, String bytesName) throws PgpGeneralException, IOException {
- int source = data.get(SOURCE) != null ? data.getInt(SOURCE) : data.getInt(TARGET);
- IOType type = IOType.fromInt(source);
- switch (type) {
- case BYTES: /* encrypting bytes directly */
- byte[] bytes = data.getByteArray(bytesName);
- return new InputData(new ByteArrayInputStream(bytes), bytes.length);
-
- case URI: /* encrypting content uri */
- Uri providerUri = data.getParcelable(ENCRYPT_DECRYPT_INPUT_URI);
-
- // InputStream
- return new InputData(getContentResolver().openInputStream(providerUri), FileHelper.getFileSize(this, providerUri, 0));
-
- default:
- throw new PgpGeneralException("No target chosen!");
- }
- }
-
- private OutputStream createCryptOutputStream(Bundle data) throws PgpGeneralException, FileNotFoundException {
- int target = data.getInt(TARGET);
- IOType type = IOType.fromInt(target);
- switch (type) {
- case BYTES:
- return new ByteArrayOutputStream();
-
- case URI:
- Uri providerUri = data.getParcelable(ENCRYPT_DECRYPT_OUTPUT_URI);
-
- return getContentResolver().openOutputStream(providerUri);
-
- default:
- throw new PgpGeneralException("No target chosen!");
- }
- }
-
- private void finalizeDecryptOutputStream(Bundle data, Bundle resultData, OutputStream outStream) {
- finalizeCryptOutputStream(data, resultData, outStream, RESULT_DECRYPTED_BYTES);
- }
-
- private void finalizeCryptOutputStream(Bundle data, Bundle resultData, OutputStream outStream, String bytesName) {
- int target = data.getInt(TARGET);
- IOType type = IOType.fromInt(target);
- switch (type) {
- case BYTES:
- byte output[] = ((ByteArrayOutputStream) outStream).toByteArray();
- resultData.putByteArray(bytesName, output);
- break;
- case URI:
- // nothing, output was written, just send okay and verification bundle
-
- break;
- }
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (ACTION_CANCEL.equals(intent.getAction())) {
- mActionCanceled.set(true);
- return START_NOT_STICKY;
- }
- return super.onStartCommand(intent, flags, startId);
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java
new file mode 100644
index 000000000..eff27f112
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.service;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.operations.BaseOperation;
+import org.sufficientlysecure.keychain.operations.CertifyOperation;
+import org.sufficientlysecure.keychain.operations.ConsolidateOperation;
+import org.sufficientlysecure.keychain.operations.DeleteOperation;
+import org.sufficientlysecure.keychain.operations.EditKeyOperation;
+import org.sufficientlysecure.keychain.operations.ExportOperation;
+import org.sufficientlysecure.keychain.operations.ImportOperation;
+import org.sufficientlysecure.keychain.operations.KeybaseVerificationOperation;
+import org.sufficientlysecure.keychain.operations.PromoteKeyOperation;
+import org.sufficientlysecure.keychain.operations.RevokeOperation;
+import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
+import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This Service contains all important long lasting operations for OpenKeychain. It receives Intents with
+ * data from the activities or other apps, executes them, and stops itself after doing them.
+ */
+public class KeychainService extends Service implements Progressable {
+
+ // messenger for communication (hack)
+ public static final String EXTRA_MESSENGER = "messenger";
+
+ // extras for operation
+ public static final String EXTRA_OPERATION_INPUT = "op_input";
+ public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
+
+ public static final String ACTION_CANCEL = "action_cancel";
+
+ // this attribute can possibly merged with the one above? not sure...
+ private AtomicBoolean mActionCanceled = new AtomicBoolean(false);
+
+ ThreadLocal<Messenger> mMessenger = new ThreadLocal<>();
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ /**
+ * This is run on the main thread, we need to spawn a runnable which runs on another thread for the actual operation
+ */
+ @Override
+ public int onStartCommand(final Intent intent, int flags, int startId) {
+
+ if (intent.getAction() != null && intent.getAction().equals(ACTION_CANCEL)) {
+ mActionCanceled.set(true);
+ return START_NOT_STICKY;
+ }
+
+ Runnable actionRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // We have not been cancelled! (yet)
+ mActionCanceled.set(false);
+
+ Bundle extras = intent.getExtras();
+
+ // Set messenger for communication (for this particular thread)
+ mMessenger.set(extras.<Messenger>getParcelable(EXTRA_MESSENGER));
+
+ // Input
+ Parcelable inputParcel = extras.getParcelable(EXTRA_OPERATION_INPUT);
+ CryptoInputParcel cryptoInput = extras.getParcelable(EXTRA_CRYPTO_INPUT);
+
+ // Operation
+ BaseOperation op;
+
+ // just for brevity
+ KeychainService outerThis = KeychainService.this;
+ if (inputParcel instanceof SignEncryptParcel) {
+ op = new SignEncryptOperation(outerThis, new ProviderHelper(outerThis),
+ outerThis, mActionCanceled);
+ } else if (inputParcel instanceof PgpDecryptVerifyInputParcel) {
+ op = new PgpDecryptVerifyOperation(outerThis, new ProviderHelper(outerThis), outerThis);
+ } else if (inputParcel instanceof SaveKeyringParcel) {
+ op = new EditKeyOperation(outerThis, new ProviderHelper(outerThis), outerThis,
+ mActionCanceled);
+ } else if (inputParcel instanceof RevokeKeyringParcel) {
+ op = new RevokeOperation(outerThis, new ProviderHelper(outerThis), outerThis);
+ } else if (inputParcel instanceof CertifyActionsParcel) {
+ op = new CertifyOperation(outerThis, new ProviderHelper(outerThis), outerThis,
+ mActionCanceled);
+ } else if (inputParcel instanceof DeleteKeyringParcel) {
+ op = new DeleteOperation(outerThis, new ProviderHelper(outerThis), outerThis);
+ } else if (inputParcel instanceof PromoteKeyringParcel) {
+ op = new PromoteKeyOperation(outerThis, new ProviderHelper(outerThis),
+ outerThis, mActionCanceled);
+ } else if (inputParcel instanceof ImportKeyringParcel) {
+ op = new ImportOperation(outerThis, new ProviderHelper(outerThis), outerThis,
+ mActionCanceled);
+ } else if (inputParcel instanceof ExportKeyringParcel) {
+ op = new ExportOperation(outerThis, new ProviderHelper(outerThis), outerThis,
+ mActionCanceled);
+ } else if (inputParcel instanceof ConsolidateInputParcel) {
+ op = new ConsolidateOperation(outerThis, new ProviderHelper(outerThis),
+ outerThis);
+ } else if (inputParcel instanceof KeybaseVerificationParcel) {
+ op = new KeybaseVerificationOperation(outerThis, new ProviderHelper(outerThis),
+ outerThis);
+ } else {
+ throw new AssertionError("Unrecognized input parcel in KeychainService!");
+ }
+
+ @SuppressWarnings("unchecked") // this is unchecked, we make sure it's the correct op above!
+ OperationResult result = op.execute(inputParcel, cryptoInput);
+ sendMessageToHandler(MessageStatus.OKAY, result);
+
+ }
+ };
+
+ Thread actionThread = new Thread(actionRunnable);
+ actionThread.start();
+
+ return START_NOT_STICKY;
+ }
+
+ private void sendMessageToHandler(MessageStatus status, Integer arg2, Bundle data) {
+
+ Message msg = Message.obtain();
+ assert msg != null;
+ msg.arg1 = status.ordinal();
+ if (arg2 != null) {
+ msg.arg2 = arg2;
+ }
+ if (data != null) {
+ msg.setData(data);
+ }
+
+ try {
+ mMessenger.get().send(msg);
+ } catch (RemoteException e) {
+ Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
+ } catch (NullPointerException e) {
+ Log.w(Constants.TAG, "Messenger is null!", e);
+ }
+ }
+
+ private void sendMessageToHandler(MessageStatus status, OperationResult data) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(OperationResult.EXTRA_RESULT, data);
+ sendMessageToHandler(status, null, bundle);
+ }
+
+ private void sendMessageToHandler(MessageStatus status) {
+ sendMessageToHandler(status, null, null);
+ }
+
+ /**
+ * Set progress of ProgressDialog by sending message to handler on UI thread
+ */
+ @Override
+ public void setProgress(String message, int progress, int max) {
+ Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max="
+ + max);
+
+ Bundle data = new Bundle();
+ if (message != null) {
+ data.putString(ServiceProgressHandler.DATA_MESSAGE, message);
+ }
+ data.putInt(ServiceProgressHandler.DATA_PROGRESS, progress);
+ data.putInt(ServiceProgressHandler.DATA_PROGRESS_MAX, max);
+
+ sendMessageToHandler(MessageStatus.UPDATE_PROGRESS, null, data);
+ }
+
+ @Override
+ public void setProgress(int resourceId, int progress, int max) {
+ setProgress(getString(resourceId), progress, max);
+ }
+
+ @Override
+ public void setProgress(int progress, int max) {
+ setProgress(null, progress, max);
+ }
+
+ @Override
+ public void setPreventCancel() {
+ sendMessageToHandler(MessageStatus.PREVENT_CANCEL);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
new file mode 100644
index 000000000..3243df1a8
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
@@ -0,0 +1,516 @@
+package org.sufficientlysecure.keychain.service;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.AbstractThreadedSyncAdapter;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SyncResult;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.support.v4.app.NotificationCompat;
+import android.widget.Toast;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.operations.ImportOperation;
+import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.ui.OrbotRequiredDialogActivity;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ParcelableProxy;
+import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
+
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class KeyserverSyncAdapterService extends Service {
+
+ // how often a sync should be initiated, in s
+ public static final long SYNC_INTERVAL =
+ Constants.DEBUG_KEYSERVER_SYNC
+ ? TimeUnit.MINUTES.toSeconds(2) : TimeUnit.DAYS.toSeconds(3);
+ // time since last update after which a key should be updated again, in s
+ public static final long KEY_UPDATE_LIMIT =
+ Constants.DEBUG_KEYSERVER_SYNC ? 1 : TimeUnit.DAYS.toSeconds(7);
+ // time by which a sync is postponed in case of a
+ public static final long SYNC_POSTPONE_TIME =
+ Constants.DEBUG_KEYSERVER_SYNC ? 30 * 1000 : TimeUnit.MINUTES.toMillis(5);
+ // Time taken by Orbot before a new circuit is created
+ public static final int ORBOT_CIRCUIT_TIMEOUT = (int) TimeUnit.MINUTES.toMillis(10);
+
+
+ private static final String ACTION_IGNORE_TOR = "ignore_tor";
+ private static final String ACTION_UPDATE_ALL = "update_all";
+ private static final String ACTION_SYNC_NOW = "sync_now";
+ private static final String ACTION_DISMISS_NOTIFICATION = "cancel_sync";
+ private static final String ACTION_START_ORBOT = "start_orbot";
+ private static final String ACTION_CANCEL = "cancel";
+
+ private AtomicBoolean mCancelled = new AtomicBoolean(false);
+
+ @Override
+ public int onStartCommand(final Intent intent, int flags, final int startId) {
+ switch (intent.getAction()) {
+ case ACTION_CANCEL: {
+ mCancelled.set(true);
+ break;
+ }
+ // the reason for the separation betweyeen SYNC_NOW and UPDATE_ALL is so that starting
+ // the sync directly from the notification is possible while the screen is on with
+ // UPDATE_ALL, but a postponed sync is only started if screen is off
+ case ACTION_SYNC_NOW: {
+ // this checks for screen on/off before sync, and postpones the sync if on
+ ContentResolver.requestSync(
+ new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE),
+ Constants.PROVIDER_AUTHORITY,
+ new Bundle()
+ );
+ break;
+ }
+ case ACTION_UPDATE_ALL: {
+ // does not check for screen on/off
+ asyncKeyUpdate(this, new CryptoInputParcel());
+ break;
+ }
+ case ACTION_IGNORE_TOR: {
+ NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT);
+ asyncKeyUpdate(this, new CryptoInputParcel(ParcelableProxy.getForNoProxy()));
+ break;
+ }
+ case ACTION_START_ORBOT: {
+ NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT);
+ Intent startOrbot = new Intent(this, OrbotRequiredDialogActivity.class);
+ startOrbot.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startOrbot.putExtra(OrbotRequiredDialogActivity.EXTRA_START_ORBOT, true);
+ Messenger messenger = new Messenger(
+ new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case OrbotRequiredDialogActivity.MESSAGE_ORBOT_STARTED: {
+ asyncKeyUpdate(KeyserverSyncAdapterService.this,
+ new CryptoInputParcel());
+ break;
+ }
+ case OrbotRequiredDialogActivity.MESSAGE_ORBOT_IGNORE: {
+ asyncKeyUpdate(KeyserverSyncAdapterService.this,
+ new CryptoInputParcel(
+ ParcelableProxy.getForNoProxy()));
+ break;
+ }
+ case OrbotRequiredDialogActivity.MESSAGE_DIALOG_CANCEL: {
+ // just stop service
+ stopSelf();
+ break;
+ }
+ }
+ }
+ }
+ );
+ startOrbot.putExtra(OrbotRequiredDialogActivity.EXTRA_MESSENGER, messenger);
+ startActivity(startOrbot);
+ break;
+ }
+ case ACTION_DISMISS_NOTIFICATION: {
+ NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT);
+ stopSelf(startId);
+ break;
+ }
+ }
+ return START_NOT_STICKY;
+ }
+
+ private class KeyserverSyncAdapter extends AbstractThreadedSyncAdapter {
+
+ public KeyserverSyncAdapter() {
+ super(KeyserverSyncAdapterService.this, true);
+ }
+
+ @Override
+ public void onPerformSync(Account account, Bundle extras, String authority,
+ ContentProviderClient provider, SyncResult syncResult) {
+ Log.d(Constants.TAG, "Performing a keyserver sync!");
+
+ PowerManager pm = (PowerManager) KeyserverSyncAdapterService.this
+ .getSystemService(Context.POWER_SERVICE);
+ @SuppressWarnings("deprecation") // our min is API 15, deprecated only in 20
+ boolean isScreenOn = pm.isScreenOn();
+
+ if (!isScreenOn) {
+ Intent serviceIntent = new Intent(KeyserverSyncAdapterService.this,
+ KeyserverSyncAdapterService.class);
+ serviceIntent.setAction(ACTION_UPDATE_ALL);
+ startService(serviceIntent);
+ } else {
+ postponeSync();
+ }
+ }
+
+ @Override
+ public void onSyncCanceled() {
+ super.onSyncCanceled();
+ cancelUpdates(KeyserverSyncAdapterService.this);
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new KeyserverSyncAdapter().getSyncAdapterBinder();
+ }
+
+ private void handleUpdateResult(ImportKeyResult result) {
+ if (result.isPending()) {
+ // result is pending due to Orbot not being started
+ // try to start it silently, if disabled show notifications
+ new OrbotHelper.SilentStartManager() {
+ @Override
+ protected void onOrbotStarted() {
+ // retry the update
+ asyncKeyUpdate(KeyserverSyncAdapterService.this,
+ new CryptoInputParcel());
+ }
+
+ @Override
+ protected void onSilentStartDisabled() {
+ // show notification
+ NotificationManager manager =
+ (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ manager.notify(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT,
+ getOrbotNoification(KeyserverSyncAdapterService.this));
+ }
+ }.startOrbotAndListen(this, false);
+ } else if (isUpdateCancelled()) {
+ Log.d(Constants.TAG, "Keyserver sync cancelled, postponing by" + SYNC_POSTPONE_TIME
+ + "ms");
+ postponeSync();
+ } else {
+ Log.d(Constants.TAG, "Keyserver sync completed: Updated: " + result.mUpdatedKeys
+ + " Failed: " + result.mBadKeys);
+ stopSelf();
+ }
+ }
+
+ private void postponeSync() {
+ AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+ Intent serviceIntent = new Intent(this, KeyserverSyncAdapterService.class);
+ serviceIntent.setAction(ACTION_SYNC_NOW);
+ PendingIntent pi = PendingIntent.getService(this, 0, serviceIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ alarmManager.set(
+ AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + SYNC_POSTPONE_TIME,
+ pi
+ );
+ }
+
+ private void asyncKeyUpdate(final Context context,
+ final CryptoInputParcel cryptoInputParcel) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ ImportKeyResult result = updateKeysFromKeyserver(context, cryptoInputParcel);
+ handleUpdateResult(result);
+ }
+ }).start();
+ }
+
+ private synchronized ImportKeyResult updateKeysFromKeyserver(final Context context,
+ final CryptoInputParcel cryptoInputParcel) {
+ mCancelled.set(false);
+
+ ArrayList<ParcelableKeyRing> keyList = getKeysToUpdate(context);
+
+ if (isUpdateCancelled()) { // if we've already been cancelled
+ return new ImportKeyResult(OperationResult.RESULT_CANCELLED,
+ new OperationResult.OperationLog());
+ }
+
+ if (cryptoInputParcel.getParcelableProxy() == null) {
+ // no explicit proxy, retrieve from preferences. Check if we should do a staggered sync
+ if (Preferences.getPreferences(context).getProxyPrefs().torEnabled) {
+ return staggeredUpdate(context, keyList, cryptoInputParcel);
+ } else {
+ return directUpdate(context, keyList, cryptoInputParcel);
+ }
+ } else {
+ return directUpdate(context, keyList, cryptoInputParcel);
+ }
+ }
+
+ private ImportKeyResult directUpdate(Context context, ArrayList<ParcelableKeyRing> keyList,
+ CryptoInputParcel cryptoInputParcel) {
+ Log.d(Constants.TAG, "Starting normal update");
+ ImportOperation importOp = new ImportOperation(context, new ProviderHelper(context), null);
+ return importOp.execute(
+ new ImportKeyringParcel(keyList,
+ Preferences.getPreferences(context).getPreferredKeyserver()),
+ cryptoInputParcel
+ );
+ }
+
+
+ /**
+ * will perform a staggered update of user's keys using delays to ensure new Tor circuits, as
+ * performed by parcimonie. Relevant issue and method at:
+ * https://github.com/open-keychain/open-keychain/issues/1337
+ *
+ * @return result of the sync
+ */
+ private ImportKeyResult staggeredUpdate(Context context, ArrayList<ParcelableKeyRing> keyList,
+ CryptoInputParcel cryptoInputParcel) {
+ Log.d(Constants.TAG, "Starting staggered update");
+ // final int WEEK_IN_SECONDS = (int) TimeUnit.DAYS.toSeconds(7);
+ final int WEEK_IN_SECONDS = 0;
+ ImportOperation.KeyImportAccumulator accumulator
+ = new ImportOperation.KeyImportAccumulator(keyList.size(), null);
+ for (ParcelableKeyRing keyRing : keyList) {
+ int waitTime;
+ int staggeredTime = new Random().nextInt(1 + 2 * (WEEK_IN_SECONDS / keyList.size()));
+ if (staggeredTime >= ORBOT_CIRCUIT_TIMEOUT) {
+ waitTime = staggeredTime;
+ } else {
+ waitTime = ORBOT_CIRCUIT_TIMEOUT + new Random().nextInt(ORBOT_CIRCUIT_TIMEOUT);
+ }
+ Log.d(Constants.TAG, "Updating key with fingerprint " + keyRing.mExpectedFingerprint +
+ " with a wait time of " + waitTime + "s");
+ try {
+ Thread.sleep(waitTime * 1000);
+ } catch (InterruptedException e) {
+ Log.e(Constants.TAG, "Exception during sleep between key updates", e);
+ // skip this one
+ continue;
+ }
+ ArrayList<ParcelableKeyRing> keyWrapper = new ArrayList<>();
+ keyWrapper.add(keyRing);
+ if (isUpdateCancelled()) {
+ return new ImportKeyResult(ImportKeyResult.RESULT_CANCELLED,
+ new OperationResult.OperationLog());
+ }
+ ImportKeyResult result =
+ new ImportOperation(context, new ProviderHelper(context), null, mCancelled)
+ .execute(
+ new ImportKeyringParcel(
+ keyWrapper,
+ Preferences.getPreferences(context)
+ .getPreferredKeyserver()
+ ),
+ cryptoInputParcel
+ );
+ if (result.isPending()) {
+ return result;
+ }
+ accumulator.accumulateKeyImport(result);
+ }
+ return accumulator.getConsolidatedResult();
+ }
+
+ /**
+ * 1. Get keys which have been updated recently and therefore do not need to
+ * be updated now
+ * 2. Get list of all keys and filter out ones that don't need to be updated
+ * 3. Return keys to be updated
+ *
+ * @return list of keys that require update
+ */
+ private ArrayList<ParcelableKeyRing> getKeysToUpdate(Context context) {
+
+ // 1. Get keys which have been updated recently and don't need to updated now
+ final int INDEX_UPDATED_KEYS_MASTER_KEY_ID = 0;
+ final int INDEX_LAST_UPDATED = 1;
+
+ // all time in seconds not milliseconds
+ final long CURRENT_TIME = GregorianCalendar.getInstance().getTimeInMillis() / 1000;
+ Cursor updatedKeysCursor = context.getContentResolver().query(
+ KeychainContract.UpdatedKeys.CONTENT_URI,
+ new String[]{
+ KeychainContract.UpdatedKeys.MASTER_KEY_ID,
+ KeychainContract.UpdatedKeys.LAST_UPDATED
+ },
+ "? - " + KeychainContract.UpdatedKeys.LAST_UPDATED + " < " + KEY_UPDATE_LIMIT,
+ new String[]{"" + CURRENT_TIME},
+ null
+ );
+
+ ArrayList<Long> ignoreMasterKeyIds = new ArrayList<>();
+ while (updatedKeysCursor.moveToNext()) {
+ long masterKeyId = updatedKeysCursor.getLong(INDEX_UPDATED_KEYS_MASTER_KEY_ID);
+ Log.d(Constants.TAG, "Keyserver sync: Ignoring {" + masterKeyId + "} last updated at {"
+ + updatedKeysCursor.getLong(INDEX_LAST_UPDATED) + "}s");
+ ignoreMasterKeyIds.add(masterKeyId);
+ }
+ updatedKeysCursor.close();
+
+ // 2. Make a list of public keys which should be updated
+ final int INDEX_MASTER_KEY_ID = 0;
+ final int INDEX_FINGERPRINT = 1;
+ Cursor keyCursor = context.getContentResolver().query(
+ KeychainContract.KeyRings.buildUnifiedKeyRingsUri(),
+ new String[]{
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.FINGERPRINT
+ },
+ null,
+ null,
+ null
+ );
+
+ if (keyCursor == null) {
+ return new ArrayList<>();
+ }
+
+ ArrayList<ParcelableKeyRing> keyList = new ArrayList<>();
+ while (keyCursor.moveToNext()) {
+ long keyId = keyCursor.getLong(INDEX_MASTER_KEY_ID);
+ if (ignoreMasterKeyIds.contains(keyId)) {
+ continue;
+ }
+ Log.d(Constants.TAG, "Keyserver sync: Updating {" + keyId + "}");
+ String fingerprint = KeyFormattingUtils
+ .convertFingerprintToHex(keyCursor.getBlob(INDEX_FINGERPRINT));
+ String hexKeyId = KeyFormattingUtils
+ .convertKeyIdToHex(keyId);
+ // we aren't updating from keybase as of now
+ keyList.add(new ParcelableKeyRing(fingerprint, hexKeyId, null));
+ }
+ keyCursor.close();
+
+ return keyList;
+ }
+
+ private boolean isUpdateCancelled() {
+ return mCancelled.get();
+ }
+
+ /**
+ * will cancel an update already in progress. We send an Intent to cancel it instead of simply
+ * modifying a static variable sync the service is running in a process that is different from
+ * the default application process where the UI code runs.
+ *
+ * @param context used to send an Intent to the service requesting cancellation.
+ */
+ public static void cancelUpdates(Context context) {
+ Intent intent = new Intent(context, KeyserverSyncAdapterService.class);
+ intent.setAction(ACTION_CANCEL);
+ context.startService(intent);
+ }
+
+ private Notification getOrbotNoification(Context context) {
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
+ builder.setSmallIcon(R.drawable.ic_stat_notify_24dp)
+ .setLargeIcon(getBitmap(R.drawable.ic_launcher, context))
+ .setContentTitle(context.getString(R.string.keyserver_sync_orbot_notif_title))
+ .setContentText(context.getString(R.string.keyserver_sync_orbot_notif_msg))
+ .setAutoCancel(true);
+
+ // In case the user decides to not use tor
+ Intent ignoreTorIntent = new Intent(context, KeyserverSyncAdapterService.class);
+ ignoreTorIntent.setAction(ACTION_IGNORE_TOR);
+ PendingIntent ignoreTorPi = PendingIntent.getService(
+ context,
+ 0, // security not issue since we're giving this pending intent to Notification Manager
+ ignoreTorIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT
+ );
+
+ builder.addAction(R.drawable.ic_stat_tor_off,
+ context.getString(R.string.keyserver_sync_orbot_notif_ignore),
+ ignoreTorPi);
+
+ Intent startOrbotIntent = new Intent(context, KeyserverSyncAdapterService.class);
+ startOrbotIntent.setAction(ACTION_START_ORBOT);
+ PendingIntent startOrbotPi = PendingIntent.getService(
+ context,
+ 0, // security not issue since we're giving this pending intent to Notification Manager
+ startOrbotIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT
+ );
+
+ builder.addAction(R.drawable.ic_stat_tor,
+ context.getString(R.string.keyserver_sync_orbot_notif_start),
+ startOrbotPi
+ );
+ builder.setContentIntent(startOrbotPi);
+
+ return builder.build();
+ }
+
+ public static void enableKeyserverSync(Context context) {
+ try {
+ AccountManager manager = AccountManager.get(context);
+ Account[] accounts = manager.getAccountsByType(Constants.ACCOUNT_TYPE);
+
+ Account account = new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE);
+ if (accounts.length == 0) {
+ if (!manager.addAccountExplicitly(account, null, null)) {
+ Log.e(Constants.TAG, "Adding account failed!");
+ }
+ }
+ // for keyserver sync
+ ContentResolver.setIsSyncable(account, Constants.PROVIDER_AUTHORITY, 1);
+ ContentResolver.setSyncAutomatically(account, Constants.PROVIDER_AUTHORITY,
+ true);
+ ContentResolver.addPeriodicSync(
+ account,
+ Constants.PROVIDER_AUTHORITY,
+ new Bundle(),
+ SYNC_INTERVAL
+ );
+ } catch (SecurityException e) {
+ Log.e(Constants.TAG, "SecurityException when adding the account", e);
+ Toast.makeText(context, R.string.reinstall_openkeychain, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ // from de.azapps.mirakel.helper.Helpers from https://github.com/MirakelX/mirakel-android
+ private Bitmap getBitmap(int resId, Context context) {
+ int mLargeIconWidth = (int) context.getResources().getDimension(
+ android.R.dimen.notification_large_icon_width);
+ int mLargeIconHeight = (int) context.getResources().getDimension(
+ android.R.dimen.notification_large_icon_height);
+ Drawable d;
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ // noinspection deprecation (can't help it at this api level)
+ d = context.getResources().getDrawable(resId);
+ } else {
+ d = context.getDrawable(resId);
+ }
+ if (d == null) {
+ return null;
+ }
+ Bitmap b = Bitmap.createBitmap(mLargeIconWidth, mLargeIconHeight, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(b);
+ d.setBounds(0, 0, mLargeIconWidth, mLargeIconHeight);
+ d.draw(c);
+ return b;
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
index 78137170d..be269c66d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -17,6 +17,9 @@
package org.sufficientlysecure.keychain.service;
+
+import java.util.Date;
+
import android.app.AlarmManager;
import android.app.Notification;
import android.app.PendingIntent;
@@ -25,8 +28,13 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
import android.os.Binder;
import android.os.Build;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -46,8 +54,6 @@ import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences;
-import java.util.Date;
-
/**
* This service runs in its own process, but is available to all other processes as the main
* passphrase cache. Use the static methods addCachedPassphrase and getCachedPassphrase for
@@ -95,8 +101,6 @@ public class PassphraseCacheService extends Service {
private static final long DEFAULT_TTL = 15;
- private static final int NOTIFICATION_ID = 1;
-
private static final int MSG_PASSPHRASE_CACHE_GET_OKAY = 1;
private static final int MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND = 2;
@@ -149,6 +153,14 @@ public class PassphraseCacheService extends Service {
context.startService(intent);
}
+ public static void clearCachedPassphrases(Context context) {
+ Log.d(Constants.TAG, "PassphraseCacheService.clearCachedPassphrase()");
+
+ Intent intent = new Intent(context, PassphraseCacheService.class);
+ intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR);
+
+ context.startService(intent);
+ }
/**
* Gets a cached passphrase from memory by sending an intent to the service. This method is
@@ -218,21 +230,21 @@ public class PassphraseCacheService extends Service {
* Internal implementation to get cached passphrase.
*/
private Passphrase getCachedPassphraseImpl(long masterKeyId, long subKeyId) throws ProviderHelper.NotFoundException {
+ // on "none" key, just do nothing
+ if (masterKeyId == Constants.key.none) {
+ return null;
+ }
+
// passphrase for symmetric encryption?
if (masterKeyId == Constants.key.symmetric) {
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for symmetric encryption");
- Passphrase cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric).getPassphrase();
+ CachedPassphrase cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric);
if (cachedPassphrase == null) {
return null;
}
addCachedPassphrase(this, Constants.key.symmetric, Constants.key.symmetric,
- cachedPassphrase, getString(R.string.passp_cache_notif_pwd));
- return cachedPassphrase;
- }
-
- // on "none" key, just do nothing
- if (masterKeyId == Constants.key.none) {
- return null;
+ cachedPassphrase.getPassphrase(), getString(R.string.passp_cache_notif_pwd));
+ return cachedPassphrase.getPassphrase();
}
// try to get master key id which is used as an identifier for cached passphrases
@@ -309,7 +321,7 @@ public class PassphraseCacheService extends Service {
if (action.equals(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE)) {
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
- timeout(context, keyId);
+ timeout(keyId);
}
}
};
@@ -411,9 +423,9 @@ public class PassphraseCacheService extends Service {
long referenceKeyId;
if (Preferences.getPreferences(mContext).getPassphraseCacheSubs()) {
- referenceKeyId = intent.getLongExtra(EXTRA_KEY_ID, 0L);
- } else {
referenceKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, 0L);
+ } else {
+ referenceKeyId = intent.getLongExtra(EXTRA_KEY_ID, 0L);
}
// Stop specific ttl alarm and
am.cancel(buildIntent(this, referenceKeyId));
@@ -444,12 +456,17 @@ public class PassphraseCacheService extends Service {
/**
* Called when one specific passphrase for keyId timed out
*/
- private void timeout(Context context, long keyId) {
+ private void timeout(long keyId) {
+
CachedPassphrase cPass = mPassphraseCache.get(keyId);
- // clean internal char[] from memory!
- cPass.getPassphrase().removeFromMemory();
- // remove passphrase object
- mPassphraseCache.remove(keyId);
+ if (cPass != null) {
+ if (cPass.getPassphrase() != null) {
+ // clean internal char[] from memory!
+ cPass.getPassphrase().removeFromMemory();
+ }
+ // remove passphrase object
+ mPassphraseCache.remove(keyId);
+ }
Log.d(Constants.TAG, "PassphraseCacheService Timeout of keyId " + keyId + ", removed from memory!");
@@ -458,7 +475,7 @@ public class PassphraseCacheService extends Service {
private void updateService() {
if (mPassphraseCache.size() > 0) {
- startForeground(NOTIFICATION_ID, getNotification());
+ startForeground(Constants.Notification.PASSPHRASE_CACHE, getNotification());
} else {
// stop whole service if no cached passphrases remaining
Log.d(Constants.TAG, "PassphraseCacheService: No passphrases remaining in memory, stopping service!");
@@ -466,59 +483,67 @@ public class PassphraseCacheService extends Service {
}
}
+ // from de.azapps.mirakel.helper.Helpers from https://github.com/MirakelX/mirakel-android
+ private static Bitmap getBitmap(int resId, Context context) {
+ int mLargeIconWidth = (int) context.getResources().getDimension(
+ android.R.dimen.notification_large_icon_width);
+ int mLargeIconHeight = (int) context.getResources().getDimension(
+ android.R.dimen.notification_large_icon_height);
+ Drawable d;
+ if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
+ // noinspection deprecation (can't help it at this api level)
+ d = context.getResources().getDrawable(resId);
+ } else {
+ d = context.getDrawable(resId);
+ }
+ if (d == null) {
+ return null;
+ }
+ Bitmap b = Bitmap.createBitmap(mLargeIconWidth, mLargeIconHeight, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(b);
+ d.setBounds(0, 0, mLargeIconWidth, mLargeIconHeight);
+ d.draw(c);
+ return b;
+ }
+
private Notification getNotification() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
+ builder.setSmallIcon(R.drawable.ic_stat_notify_24dp)
+ .setLargeIcon(getBitmap(R.drawable.ic_launcher, getBaseContext()))
+ .setContentTitle(getResources().getQuantityString(R.plurals.passp_cache_notif_n_keys,
+ mPassphraseCache.size(), mPassphraseCache.size()))
+ .setContentText(getString(R.string.passp_cache_notif_click_to_clear));
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- builder.setSmallIcon(R.drawable.ic_launcher)
- .setContentTitle(getString(R.string.app_name))
- .setContentText(String.format(getString(R.string.passp_cache_notif_n_keys),
- mPassphraseCache.size()));
+ NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
- NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
+ inboxStyle.setBigContentTitle(getString(R.string.passp_cache_notif_keys));
- inboxStyle.setBigContentTitle(getString(R.string.passp_cache_notif_keys));
+ // Moves events into the big view
+ for (int i = 0; i < mPassphraseCache.size(); i++) {
+ inboxStyle.addLine(mPassphraseCache.valueAt(i).getPrimaryUserID());
+ }
- // Moves events into the big view
- for (int i = 0; i < mPassphraseCache.size(); i++) {
- inboxStyle.addLine(mPassphraseCache.valueAt(i).getPrimaryUserID());
- }
+ // Moves the big view style object into the notification object.
+ builder.setStyle(inboxStyle);
- // Moves the big view style object into the notification object.
- builder.setStyle(inboxStyle);
-
- // Add purging action
- Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class);
- intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR);
- builder.addAction(
- R.drawable.abc_ic_clear_mtrl_alpha,
- getString(R.string.passp_cache_notif_clear),
- PendingIntent.getService(
- getApplicationContext(),
- 0,
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT
- )
- );
- } else {
- // Fallback, since expandable notifications weren't available back then
- builder.setSmallIcon(R.drawable.ic_launcher)
- .setContentTitle(String.format(getString(R.string.passp_cache_notif_n_keys),
- mPassphraseCache.size()))
- .setContentText(getString(R.string.passp_cache_notif_click_to_clear));
-
- Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class);
- intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR);
-
- builder.setContentIntent(
- PendingIntent.getService(
- getApplicationContext(),
- 0,
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT
- )
- );
- }
+ Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class);
+ intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR);
+ PendingIntent clearCachePi = PendingIntent.getService(
+ getApplicationContext(),
+ 0,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT
+ );
+
+ // Add cache clear PI to normal touch
+ builder.setContentIntent(clearCachePi);
+
+ // Add clear PI action below text
+ builder.addAction(
+ R.drawable.abc_ic_clear_mtrl_alpha,
+ getString(R.string.passp_cache_notif_clear),
+ clearCachePi
+ );
return builder.build();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java
new file mode 100644
index 000000000..d268c3694
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class PromoteKeyringParcel implements Parcelable {
+
+ public long mKeyRingId;
+ public byte[] mCardAid;
+ public long[] mSubKeyIds;
+
+ public PromoteKeyringParcel(long keyRingId, byte[] cardAid, long[] subKeyIds) {
+ mKeyRingId = keyRingId;
+ mCardAid = cardAid;
+ mSubKeyIds = subKeyIds;
+ }
+
+ protected PromoteKeyringParcel(Parcel in) {
+ mKeyRingId = in.readLong();
+ mCardAid = in.createByteArray();
+ mSubKeyIds = in.createLongArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mKeyRingId);
+ dest.writeByteArray(mCardAid);
+ dest.writeLongArray(mSubKeyIds);
+ }
+
+ public static final Parcelable.Creator<PromoteKeyringParcel> CREATOR = new Parcelable.Creator<PromoteKeyringParcel>() {
+ @Override
+ public PromoteKeyringParcel createFromParcel(Parcel in) {
+ return new PromoteKeyringParcel(in);
+ }
+
+ @Override
+ public PromoteKeyringParcel[] newArray(int size) {
+ return new PromoteKeyringParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/RevokeKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/RevokeKeyringParcel.java
new file mode 100644
index 000000000..02e8fda46
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/RevokeKeyringParcel.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class RevokeKeyringParcel implements Parcelable {
+
+ final public long mMasterKeyId;
+ final public boolean mUpload;
+ final public String mKeyserver;
+
+ public RevokeKeyringParcel(long masterKeyId, boolean upload, String keyserver) {
+ mMasterKeyId = masterKeyId;
+ mUpload = upload;
+ mKeyserver = keyserver;
+ }
+
+ protected RevokeKeyringParcel(Parcel in) {
+ mMasterKeyId = in.readLong();
+ mUpload = in.readByte() != 0x00;
+ mKeyserver = in.readString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mMasterKeyId);
+ dest.writeByte((byte) (mUpload ? 0x01 : 0x00));
+ dest.writeString(mKeyserver);
+ }
+
+ public static final Parcelable.Creator<RevokeKeyringParcel> CREATOR = new Parcelable.Creator<RevokeKeyringParcel>() {
+ @Override
+ public RevokeKeyringParcel createFromParcel(Parcel in) {
+ return new RevokeKeyringParcel(in);
+ }
+
+ @Override
+ public RevokeKeyringParcel[] newArray(int size) {
+ return new RevokeKeyringParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
index 2e0524141..6959fca56 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -61,6 +61,15 @@ public class SaveKeyringParcel implements Parcelable {
public ArrayList<String> mRevokeUserIds;
public ArrayList<Long> mRevokeSubKeys;
+ // if these are non-null, PINs will be changed on the card
+ public Passphrase mCardPin;
+ public Passphrase mCardAdminPin;
+
+ // private because they have to be set together with setUpdateOptions
+ private boolean mUpload;
+ private boolean mUploadAtomic;
+ private String mKeyserver;
+
public SaveKeyringParcel() {
reset();
}
@@ -80,6 +89,29 @@ public class SaveKeyringParcel implements Parcelable {
mChangeSubKeys = new ArrayList<>();
mRevokeUserIds = new ArrayList<>();
mRevokeSubKeys = new ArrayList<>();
+ mCardPin = null;
+ mCardAdminPin = null;
+ mUpload = false;
+ mUploadAtomic = false;
+ mKeyserver = null;
+ }
+
+ public void setUpdateOptions(boolean upload, boolean uploadAtomic, String keysever) {
+ mUpload = upload;
+ mUploadAtomic = uploadAtomic;
+ mKeyserver = keysever;
+ }
+
+ public boolean isUpload() {
+ return mUpload;
+ }
+
+ public boolean isUploadAtomic() {
+ return mUploadAtomic;
+ }
+
+ public String getUploadKeyserver() {
+ return mKeyserver;
}
public boolean isEmpty() {
@@ -95,7 +127,8 @@ public class SaveKeyringParcel implements Parcelable {
}
for (SubkeyChange change : mChangeSubKeys) {
- if (change.mRecertify || change.mFlags != null || change.mExpiry != null) {
+ if (change.mRecertify || change.mFlags != null || change.mExpiry != null
+ || change.mMoveKeyToCard) {
return false;
}
}
@@ -142,6 +175,8 @@ public class SaveKeyringParcel implements Parcelable {
public boolean mRecertify;
// if this flag is true, the subkey should be changed to a stripped key
public boolean mDummyStrip;
+ // if this flag is true, the subkey should be moved to a card
+ public boolean mMoveKeyToCard;
// if this is non-null, the subkey will be changed to a divert-to-card
// key for the given serial number
public byte[] mDummyDivert;
@@ -161,16 +196,16 @@ public class SaveKeyringParcel implements Parcelable {
mExpiry = expiry;
}
- public SubkeyChange(long keyId, boolean dummyStrip, byte[] dummyDivert) {
+ public SubkeyChange(long keyId, boolean dummyStrip, boolean moveKeyToCard) {
this(keyId, null, null);
// these flags are mutually exclusive!
- if (dummyStrip && dummyDivert != null) {
+ if (dummyStrip && moveKeyToCard) {
throw new AssertionError(
- "cannot set strip and divert flags at the same time - this is a bug!");
+ "cannot set strip and keytocard flags at the same time - this is a bug!");
}
mDummyStrip = dummyStrip;
- mDummyDivert = dummyDivert;
+ mMoveKeyToCard = moveKeyToCard;
}
@Override
@@ -179,6 +214,7 @@ public class SaveKeyringParcel implements Parcelable {
out += "mFlags: " + mFlags + ", ";
out += "mExpiry: " + mExpiry + ", ";
out += "mDummyStrip: " + mDummyStrip + ", ";
+ out += "mMoveKeyToCard: " + mMoveKeyToCard + ", ";
out += "mDummyDivert: [" + (mDummyDivert == null ? 0 : mDummyDivert.length) + " bytes]";
return out;
@@ -206,6 +242,7 @@ public class SaveKeyringParcel implements Parcelable {
}
}
+ @SuppressWarnings("unchecked") // we verify the reads against writes in writeToParcel
public SaveKeyringParcel(Parcel source) {
mMasterKeyId = source.readInt() != 0 ? source.readLong() : null;
mFingerprint = source.createByteArray();
@@ -221,6 +258,13 @@ public class SaveKeyringParcel implements Parcelable {
mRevokeUserIds = source.createStringArrayList();
mRevokeSubKeys = (ArrayList<Long>) source.readSerializable();
+
+ mCardPin = source.readParcelable(Passphrase.class.getClassLoader());
+ mCardAdminPin = source.readParcelable(Passphrase.class.getClassLoader());
+
+ mUpload = source.readByte() != 0;
+ mUploadAtomic = source.readByte() != 0;
+ mKeyserver = source.readString();
}
@Override
@@ -232,7 +276,7 @@ public class SaveKeyringParcel implements Parcelable {
destination.writeByteArray(mFingerprint);
// yes, null values are ok for parcelables
- destination.writeParcelable(mNewUnlock, 0);
+ destination.writeParcelable(mNewUnlock, flags);
destination.writeStringList(mAddUserIds);
destination.writeSerializable(mAddUserAttribute);
@@ -243,6 +287,13 @@ public class SaveKeyringParcel implements Parcelable {
destination.writeStringList(mRevokeUserIds);
destination.writeSerializable(mRevokeSubKeys);
+
+ destination.writeParcelable(mCardPin, flags);
+ destination.writeParcelable(mCardAdminPin, flags);
+
+ destination.writeByte((byte) (mUpload ? 1 : 0));
+ destination.writeByte((byte) (mUploadAtomic ? 1 : 0));
+ destination.writeString(mKeyserver);
}
public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() {
@@ -270,7 +321,9 @@ public class SaveKeyringParcel implements Parcelable {
out += "mChangeSubKeys: " + mChangeSubKeys + "\n";
out += "mChangePrimaryUserId: " + mChangePrimaryUserId + "\n";
out += "mRevokeUserIds: " + mRevokeUserIds + "\n";
- out += "mRevokeSubKeys: " + mRevokeSubKeys;
+ out += "mRevokeSubKeys: " + mRevokeSubKeys + "\n";
+ out += "mCardPin: " + mCardPin + "\n";
+ out += "mCardAdminPin: " + mCardAdminPin;
return out;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java
index 430d8a49b..d294e5057 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java
@@ -17,8 +17,8 @@
package org.sufficientlysecure.keychain.service;
-import android.app.Activity;
-import android.content.Intent;
+
+import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -27,7 +27,6 @@ import android.support.v4.app.FragmentManager;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.operations.results.CertifyResult;
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
@@ -35,7 +34,7 @@ import org.sufficientlysecure.keychain.util.Log;
public class ServiceProgressHandler extends Handler {
// possible messages sent from this service to handler on ui
- public static enum MessageStatus{
+ public enum MessageStatus {
UNKNOWN,
OKAY,
EXCEPTION,
@@ -44,9 +43,8 @@ public class ServiceProgressHandler extends Handler {
private static final MessageStatus[] values = values();
- public static MessageStatus fromInt(int n)
- {
- if(n < 0 || n >= values.length) {
+ public static MessageStatus fromInt(int n) {
+ if (n < 0 || n >= values.length) {
return UNKNOWN;
} else {
return values[n];
@@ -66,74 +64,51 @@ public class ServiceProgressHandler extends Handler {
public static final String KEYBASE_PRESENCE_URL = "keybase_presence_url";
public static final String KEYBASE_PRESENCE_LABEL = "keybase_presence_label";
- Activity mActivity;
- ProgressDialogFragment mProgressDialogFragment;
+ public static final String TAG_PROGRESS_DIALOG = "progressDialog";
- public ServiceProgressHandler(Activity activity) {
- this.mActivity = activity;
- }
+ FragmentActivity mActivity;
- public ServiceProgressHandler(Activity activity,
- ProgressDialogFragment progressDialogFragment) {
- this.mActivity = activity;
- this.mProgressDialogFragment = progressDialogFragment;
+ public ServiceProgressHandler(FragmentActivity activity) {
+ mActivity = activity;
}
- public ServiceProgressHandler(Activity activity,
- String progressDialogMessage,
- int progressDialogStyle,
- ProgressDialogFragment.ServiceType serviceType) {
- this(activity, progressDialogMessage, progressDialogStyle, false, serviceType);
+ public void showProgressDialog() {
+ showProgressDialog("", ProgressDialog.STYLE_SPINNER, false);
}
- public ServiceProgressHandler(Activity activity,
- String progressDialogMessage,
- int progressDialogStyle,
- boolean cancelable,
- ProgressDialogFragment.ServiceType serviceType) {
- this.mActivity = activity;
- this.mProgressDialogFragment = ProgressDialogFragment.newInstance(
+ public void showProgressDialog(
+ String progressDialogMessage, int progressDialogStyle, boolean cancelable) {
+
+ final ProgressDialogFragment frag = ProgressDialogFragment.newInstance(
progressDialogMessage,
progressDialogStyle,
- cancelable,
- serviceType);
- }
-
- public void showProgressDialog(FragmentActivity activity) {
- if (mProgressDialogFragment == null) {
- return;
- }
+ cancelable);
// TODO: This is a hack!, see
// http://stackoverflow.com/questions/10114324/show-dialogfragment-from-onactivityresult
- final FragmentManager manager = activity.getSupportFragmentManager();
+ final FragmentManager manager = mActivity.getSupportFragmentManager();
Handler handler = new Handler();
handler.post(new Runnable() {
public void run() {
- mProgressDialogFragment.show(manager, "progressDialog");
+ frag.show(manager, TAG_PROGRESS_DIALOG);
}
});
+
}
@Override
public void handleMessage(Message message) {
Bundle data = message.getData();
- if (mProgressDialogFragment == null) {
- // Log.e(Constants.TAG,
- // "Progress has not been updated because mProgressDialogFragment was null!");
- return;
- }
-
MessageStatus status = MessageStatus.fromInt(message.arg1);
switch (status) {
case OKAY:
- mProgressDialogFragment.dismissAllowingStateLoss();
+ dismissAllowingStateLoss();
break;
case EXCEPTION:
- mProgressDialogFragment.dismissAllowingStateLoss();
+ dismissAllowingStateLoss();
// show error from service
if (data.containsKey(DATA_ERROR)) {
@@ -147,23 +122,25 @@ public class ServiceProgressHandler extends Handler {
case UPDATE_PROGRESS:
if (data.containsKey(DATA_PROGRESS) && data.containsKey(DATA_PROGRESS_MAX)) {
+ String msg = null;
+ int progress = data.getInt(DATA_PROGRESS);
+ int max = data.getInt(DATA_PROGRESS_MAX);
+
// update progress from service
if (data.containsKey(DATA_MESSAGE)) {
- mProgressDialogFragment.setProgress(data.getString(DATA_MESSAGE),
- data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
+ msg = data.getString(DATA_MESSAGE);
} else if (data.containsKey(DATA_MESSAGE_ID)) {
- mProgressDialogFragment.setProgress(data.getInt(DATA_MESSAGE_ID),
- data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
- } else {
- mProgressDialogFragment.setProgress(data.getInt(DATA_PROGRESS),
- data.getInt(DATA_PROGRESS_MAX));
+ msg = mActivity.getString(data.getInt(DATA_MESSAGE_ID));
}
+
+ onSetProgress(msg, progress, max);
+
}
break;
case PREVENT_CANCEL:
- mProgressDialogFragment.setPreventCancel(true);
+ setPreventCancel(true);
break;
default:
@@ -171,4 +148,48 @@ public class ServiceProgressHandler extends Handler {
break;
}
}
+
+ private void setPreventCancel(boolean preventCancel) {
+ ProgressDialogFragment progressDialogFragment =
+ (ProgressDialogFragment) mActivity.getSupportFragmentManager()
+ .findFragmentByTag("progressDialog");
+
+ if (progressDialogFragment == null) {
+ return;
+ }
+
+ progressDialogFragment.setPreventCancel(preventCancel);
+ }
+
+ protected void dismissAllowingStateLoss() {
+ ProgressDialogFragment progressDialogFragment =
+ (ProgressDialogFragment) mActivity.getSupportFragmentManager()
+ .findFragmentByTag("progressDialog");
+
+ if (progressDialogFragment == null) {
+ return;
+ }
+
+ progressDialogFragment.dismissAllowingStateLoss();
+ }
+
+
+ protected void onSetProgress(String msg, int progress, int max) {
+
+ ProgressDialogFragment progressDialogFragment =
+ (ProgressDialogFragment) mActivity.getSupportFragmentManager()
+ .findFragmentByTag("progressDialog");
+
+ if (progressDialogFragment == null) {
+ return;
+ }
+
+ if (msg != null) {
+ progressDialogFragment.setProgress(msg, progress, max);
+ } else {
+ progressDialogFragment.setProgress(progress, max);
+ }
+
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java
index 3d1ccaca1..0d8569fe6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java
@@ -17,52 +17,87 @@
package org.sufficientlysecure.keychain.service.input;
-import java.nio.ByteBuffer;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
import android.os.Parcel;
import android.os.Parcelable;
+import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Passphrase;
+import java.nio.ByteBuffer;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* This is a base class for the input of crypto operations.
*/
public class CryptoInputParcel implements Parcelable {
- final Date mSignatureTime;
- final Passphrase mPassphrase;
+ private Date mSignatureTime;
+ private boolean mHasSignature;
+
+ public Passphrase mPassphrase;
+ // used to supply an explicit proxy to operations that require it
+ // this is not final so it can be added to an existing CryptoInputParcel
+ // (e.g) CertifyOperation with upload might require both passphrase and orbot to be enabled
+ private ParcelableProxy mParcelableProxy;
+
+ // specifies whether passphrases should be cached
+ public boolean mCachePassphrase = true;
// this map contains both decrypted session keys and signed hashes to be
// used in the crypto operation described by this parcel.
private HashMap<ByteBuffer, byte[]> mCryptoData = new HashMap<>();
public CryptoInputParcel() {
- mSignatureTime = new Date();
+ mSignatureTime = null;
mPassphrase = null;
+ mCachePassphrase = true;
}
public CryptoInputParcel(Date signatureTime, Passphrase passphrase) {
+ mHasSignature = true;
mSignatureTime = signatureTime == null ? new Date() : signatureTime;
mPassphrase = passphrase;
+ mCachePassphrase = true;
}
public CryptoInputParcel(Passphrase passphrase) {
- mSignatureTime = new Date();
mPassphrase = passphrase;
+ mCachePassphrase = true;
}
public CryptoInputParcel(Date signatureTime) {
+ mHasSignature = true;
mSignatureTime = signatureTime == null ? new Date() : signatureTime;
mPassphrase = null;
+ mCachePassphrase = true;
+ }
+
+ public CryptoInputParcel(ParcelableProxy parcelableProxy) {
+ this();
+ mParcelableProxy = parcelableProxy;
+ }
+
+ public CryptoInputParcel(Date signatureTime, boolean cachePassphrase) {
+ mHasSignature = true;
+ mSignatureTime = signatureTime == null ? new Date() : signatureTime;
+ mPassphrase = null;
+ mCachePassphrase = cachePassphrase;
+ }
+
+ public CryptoInputParcel(boolean cachePassphrase) {
+ mCachePassphrase = cachePassphrase;
}
protected CryptoInputParcel(Parcel source) {
- mSignatureTime = new Date(source.readLong());
+ mHasSignature = source.readByte() != 0;
+ if (mHasSignature) {
+ mSignatureTime = new Date(source.readLong());
+ }
mPassphrase = source.readParcelable(getClass().getClassLoader());
+ mParcelableProxy = source.readParcelable(getClass().getClassLoader());
+ mCachePassphrase = source.readByte() != 0;
{
int count = source.readInt();
@@ -83,8 +118,13 @@ public class CryptoInputParcel implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(mSignatureTime.getTime());
+ dest.writeByte((byte) (mHasSignature ? 1 : 0));
+ if (mHasSignature) {
+ dest.writeLong(mSignatureTime.getTime());
+ }
dest.writeParcelable(mPassphrase, 0);
+ dest.writeParcelable(mParcelableProxy, 0);
+ dest.writeByte((byte) (mCachePassphrase ? 1 : 0));
dest.writeInt(mCryptoData.size());
for (HashMap.Entry<ByteBuffer, byte[]> entry : mCryptoData.entrySet()) {
@@ -93,12 +133,28 @@ public class CryptoInputParcel implements Parcelable {
}
}
+ public void addParcelableProxy(ParcelableProxy parcelableProxy) {
+ mParcelableProxy = parcelableProxy;
+ }
+
+ public void addSignatureTime(Date signatureTime) {
+ mSignatureTime = signatureTime;
+ }
+
public void addCryptoData(byte[] hash, byte[] signedHash) {
mCryptoData.put(ByteBuffer.wrap(hash), signedHash);
}
+ public void addCryptoData(Map<ByteBuffer, byte[]> cachedSessionKeys) {
+ mCryptoData.putAll(cachedSessionKeys);
+ }
+
+ public ParcelableProxy getParcelableProxy() {
+ return mParcelableProxy;
+ }
+
public Map<ByteBuffer, byte[]> getCryptoData() {
- return Collections.unmodifiableMap(mCryptoData);
+ return mCryptoData;
}
public Date getSignatureTime() {
@@ -138,4 +194,5 @@ public class CryptoInputParcel implements Parcelable {
b.append("}");
return b.toString();
}
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java
index 535c1e735..e4dac3227 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java
@@ -1,35 +1,37 @@
package org.sufficientlysecure.keychain.service.input;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-
import android.os.Parcel;
import android.os.Parcelable;
-import org.sufficientlysecure.keychain.Constants.key;
+import org.sufficientlysecure.keychain.util.Passphrase;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
public class RequiredInputParcel implements Parcelable {
public enum RequiredInputType {
- PASSPHRASE, PASSPHRASE_SYMMETRIC, NFC_SIGN, NFC_DECRYPT
+ PASSPHRASE, PASSPHRASE_SYMMETRIC, NFC_SIGN, NFC_DECRYPT, NFC_MOVE_KEY_TO_CARD, ENABLE_ORBOT,
+ UPLOAD_FAIL_RETRY
}
public Date mSignatureTime;
public final RequiredInputType mType;
- public final byte[][] mInputHashes;
+ public final byte[][] mInputData;
public final int[] mSignAlgos;
private Long mMasterKeyId;
private Long mSubKeyId;
- private RequiredInputParcel(RequiredInputType type, byte[][] inputHashes,
+ private RequiredInputParcel(RequiredInputType type, byte[][] inputData,
int[] signAlgos, Date signatureTime, Long masterKeyId, Long subKeyId) {
mType = type;
- mInputHashes = inputHashes;
+ mInputData = inputData;
mSignAlgos = signAlgos;
mSignatureTime = signatureTime;
mMasterKeyId = masterKeyId;
@@ -39,25 +41,25 @@ public class RequiredInputParcel implements Parcelable {
public RequiredInputParcel(Parcel source) {
mType = RequiredInputType.values()[source.readInt()];
- // 0 = none, 1 = both, 2 = only hashes (decrypt)
- int hashTypes = source.readInt();
- if (hashTypes != 0) {
+ // 0 = none, 1 = signAlgos + inputData, 2 = only inputData (decrypt)
+ int inputDataType = source.readInt();
+ if (inputDataType != 0) {
int count = source.readInt();
- mInputHashes = new byte[count][];
- if (hashTypes == 1) {
+ mInputData = new byte[count][];
+ if (inputDataType == 1) {
mSignAlgos = new int[count];
for (int i = 0; i < count; i++) {
- mInputHashes[i] = source.createByteArray();
+ mInputData[i] = source.createByteArray();
mSignAlgos[i] = source.readInt();
}
} else {
mSignAlgos = null;
for (int i = 0; i < count; i++) {
- mInputHashes[i] = source.createByteArray();
+ mInputData[i] = source.createByteArray();
}
}
} else {
- mInputHashes = null;
+ mInputData = null;
mSignAlgos = null;
}
@@ -75,16 +77,27 @@ public class RequiredInputParcel implements Parcelable {
return mSubKeyId;
}
+ public static RequiredInputParcel createRetryUploadOperation() {
+ return new RequiredInputParcel(RequiredInputType.UPLOAD_FAIL_RETRY,
+ null, null, null, 0L, 0L);
+ }
+
+ public static RequiredInputParcel createOrbotRequiredOperation() {
+ return new RequiredInputParcel(RequiredInputType.ENABLE_ORBOT, null, null, null, 0L, 0L);
+ }
+
public static RequiredInputParcel createNfcSignOperation(
+ long masterKeyId, long subKeyId,
byte[] inputHash, int signAlgo, Date signatureTime) {
return new RequiredInputParcel(RequiredInputType.NFC_SIGN,
new byte[][] { inputHash }, new int[] { signAlgo },
- signatureTime, null, null);
+ signatureTime, masterKeyId, subKeyId);
}
- public static RequiredInputParcel createNfcDecryptOperation(byte[] inputHash, long subKeyId) {
+ public static RequiredInputParcel createNfcDecryptOperation(
+ long masterKeyId, long subKeyId, byte[] encryptedSessionKey) {
return new RequiredInputParcel(RequiredInputType.NFC_DECRYPT,
- new byte[][] { inputHash }, null, null, null, subKeyId);
+ new byte[][] { encryptedSessionKey }, null, null, masterKeyId, subKeyId);
}
public static RequiredInputParcel createRequiredSignPassphrase(
@@ -118,11 +131,11 @@ public class RequiredInputParcel implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mType.ordinal());
- if (mInputHashes != null) {
+ if (mInputData != null) {
dest.writeInt(mSignAlgos != null ? 1 : 2);
- dest.writeInt(mInputHashes.length);
- for (int i = 0; i < mInputHashes.length; i++) {
- dest.writeByteArray(mInputHashes[i]);
+ dest.writeInt(mInputData.length);
+ for (int i = 0; i < mInputData.length; i++) {
+ dest.writeByteArray(mInputData[i]);
if (mSignAlgos != null) {
dest.writeInt(mSignAlgos[i]);
}
@@ -165,10 +178,10 @@ public class RequiredInputParcel implements Parcelable {
Date mSignatureTime;
ArrayList<Integer> mSignAlgos = new ArrayList<>();
ArrayList<byte[]> mInputHashes = new ArrayList<>();
- Long mMasterKeyId;
- Long mSubKeyId;
+ long mMasterKeyId;
+ long mSubKeyId;
- public NfcSignOperationsBuilder(Date signatureTime, Long masterKeyId, Long subKeyId) {
+ public NfcSignOperationsBuilder(Date signatureTime, long masterKeyId, long subKeyId) {
mSignatureTime = signatureTime;
mMasterKeyId = masterKeyId;
mSubKeyId = subKeyId;
@@ -199,7 +212,7 @@ public class RequiredInputParcel implements Parcelable {
throw new AssertionError("operation types must match, this is a progrmming error!");
}
- Collections.addAll(mInputHashes, input.mInputHashes);
+ Collections.addAll(mInputHashes, input.mInputData);
for (int signAlgo : input.mSignAlgos) {
mSignAlgos.add(signAlgo);
}
@@ -211,4 +224,66 @@ public class RequiredInputParcel implements Parcelable {
}
+ public static class NfcKeyToCardOperationsBuilder {
+ ArrayList<byte[]> mSubkeysToExport = new ArrayList<>();
+ Long mMasterKeyId;
+ byte[] mPin;
+ byte[] mAdminPin;
+
+ public NfcKeyToCardOperationsBuilder(Long masterKeyId) {
+ mMasterKeyId = masterKeyId;
+ }
+
+ public RequiredInputParcel build() {
+ byte[][] inputData = new byte[mSubkeysToExport.size() + 2][];
+
+ // encode all subkeys into inputData
+ byte[][] subkeyData = new byte[mSubkeysToExport.size()][];
+ mSubkeysToExport.toArray(subkeyData);
+
+ // first two are PINs
+ inputData[0] = mPin;
+ inputData[1] = mAdminPin;
+ // then subkeys
+ System.arraycopy(subkeyData, 0, inputData, 2, subkeyData.length);
+
+ ByteBuffer buf = ByteBuffer.wrap(mSubkeysToExport.get(0));
+
+ // We need to pass in a subkey here...
+ return new RequiredInputParcel(RequiredInputType.NFC_MOVE_KEY_TO_CARD,
+ inputData, null, null, mMasterKeyId, buf.getLong());
+ }
+
+ public void addSubkey(long subkeyId) {
+ byte[] subKeyId = new byte[8];
+ ByteBuffer buf = ByteBuffer.wrap(subKeyId);
+ buf.putLong(subkeyId).rewind();
+ mSubkeysToExport.add(subKeyId);
+ }
+
+ public void setPin(Passphrase pin) {
+ mPin = pin.toStringUnsafe().getBytes();
+ }
+
+ public void setAdminPin(Passphrase adminPin) {
+ mAdminPin = adminPin.toStringUnsafe().getBytes();
+ }
+
+ public void addAll(RequiredInputParcel input) {
+ if (!mMasterKeyId.equals(input.mMasterKeyId)) {
+ throw new AssertionError("Master keys must match, this is a programming error!");
+ }
+ if (input.mType != RequiredInputType.NFC_MOVE_KEY_TO_CARD) {
+ throw new AssertionError("Operation types must match, this is a programming error!");
+ }
+
+ Collections.addAll(mSubkeysToExport, input.mInputData);
+ }
+
+ public boolean isEmpty() {
+ return mSubkeysToExport.isEmpty();
+ }
+
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupFragment.java
new file mode 100644
index 000000000..a3ea8ad9a
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupFragment.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Locale;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.util.ExportHelper;
+
+public class BackupFragment extends Fragment {
+
+ // This ids for multiple key export.
+ private ArrayList<Long> mIdsForRepeatAskPassphrase;
+ // This index for remembering the number of master key.
+ private int mIndex;
+
+ static final int REQUEST_REPEAT_PASSPHRASE = 1;
+ private ExportHelper mExportHelper;
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ // we won't get attached to a non-fragment activity, so the cast should be safe
+ mExportHelper = new ExportHelper((FragmentActivity) activity);
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ mExportHelper = null;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.backup_fragment, container, false);
+
+ View backupAll = view.findViewById(R.id.backup_all);
+ View backupPublicKeys = view.findViewById(R.id.backup_public_keys);
+
+ backupAll.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ exportToFile(true);
+ }
+ });
+
+ backupPublicKeys.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ exportToFile(false);
+ }
+ });
+
+ return view;
+ }
+
+ private void exportToFile(boolean includeSecretKeys) {
+ FragmentActivity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ if (!includeSecretKeys) {
+ startBackup(false);
+ return;
+ }
+
+ new AsyncTask<ContentResolver,Void,ArrayList<Long>>() {
+ @Override
+ protected ArrayList<Long> doInBackground(ContentResolver... resolver) {
+ ArrayList<Long> askPassphraseIds = new ArrayList<>();
+ Cursor cursor = resolver[0].query(
+ KeyRings.buildUnifiedKeyRingsUri(), new String[] {
+ KeyRings.MASTER_KEY_ID,
+ KeyRings.HAS_SECRET,
+ }, KeyRings.HAS_SECRET + " != 0", null, null);
+ try {
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ SecretKeyType secretKeyType = SecretKeyType.fromNum(cursor.getInt(1));
+ switch (secretKeyType) {
+ // all of these make no sense to ask
+ case PASSPHRASE_EMPTY:
+ case GNU_DUMMY:
+ case DIVERT_TO_CARD:
+ case UNAVAILABLE:
+ continue;
+ default: {
+ long keyId = cursor.getLong(0);
+ askPassphraseIds.add(keyId);
+ }
+ }
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ return askPassphraseIds;
+ }
+
+ @Override
+ protected void onPostExecute(ArrayList<Long> askPassphraseIds) {
+ super.onPostExecute(askPassphraseIds);
+ FragmentActivity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ mIdsForRepeatAskPassphrase = askPassphraseIds;
+ mIndex = 0;
+
+ if (mIdsForRepeatAskPassphrase.size() != 0) {
+ startPassphraseActivity();
+ return;
+ }
+
+ startBackup(true);
+ }
+
+ }.execute(activity.getContentResolver());
+
+ }
+
+ private void startPassphraseActivity() {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ Intent intent = new Intent(activity, PassphraseDialogActivity.class);
+ long masterKeyId = mIdsForRepeatAskPassphrase.get(mIndex++);
+ intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, masterKeyId);
+ startActivityForResult(intent, REQUEST_REPEAT_PASSPHRASE);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_REPEAT_PASSPHRASE) {
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
+ if (mIndex < mIdsForRepeatAskPassphrase.size()) {
+ startPassphraseActivity();
+ return;
+ }
+
+ startBackup(true);
+ }
+ }
+
+ private void startBackup(boolean exportSecret) {
+ File filename;
+ String date = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
+ if (exportSecret) {
+ filename = new File(Constants.Path.APP_DIR, "keys_" + date + ".asc");
+ } else {
+ filename = new File(Constants.Path.APP_DIR, "keys_" + date + ".pub.asc");
+ }
+ mExportHelper.showExportKeysDialog(null, filename, exportSecret);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java
index 016ab5f3c..c5528e40b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java
@@ -30,6 +30,8 @@ public class CertifyFingerprintActivity extends BaseActivity {
protected Uri mDataUri;
+ public static final String EXTRA_ENABLE_WORD_CONFIRM = "enable_word_confirm";
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -40,6 +42,7 @@ public class CertifyFingerprintActivity extends BaseActivity {
finish();
return;
}
+ boolean enableWordConfirm = getIntent().getBooleanExtra(EXTRA_ENABLE_WORD_CONFIRM, false);
setFullScreenDialogClose(new View.OnClickListener() {
@Override
@@ -50,7 +53,7 @@ public class CertifyFingerprintActivity extends BaseActivity {
Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
- startFragment(savedInstanceState, mDataUri);
+ startFragment(savedInstanceState, mDataUri, enableWordConfirm);
}
@Override
@@ -58,7 +61,7 @@ public class CertifyFingerprintActivity extends BaseActivity {
setContentView(R.layout.certify_fingerprint_activity);
}
- private void startFragment(Bundle savedInstanceState, Uri dataUri) {
+ private void startFragment(Bundle savedInstanceState, Uri dataUri, boolean enableWordConfirm) {
// 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.
@@ -67,7 +70,7 @@ public class CertifyFingerprintActivity extends BaseActivity {
}
// Create an instance of the fragment
- CertifyFingerprintFragment frag = CertifyFingerprintFragment.newInstance(dataUri);
+ CertifyFingerprintFragment frag = CertifyFingerprintFragment.newInstance(dataUri, enableWordConfirm);
// Add the fragment to the 'fragment_container' FrameLayout
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java
index a6b8a0e85..552fa34c0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
import android.database.Cursor;
+import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
@@ -34,6 +35,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.util.ExperimentalWordConfirm;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
@@ -44,23 +46,24 @@ public class CertifyFingerprintFragment extends LoaderFragment implements
static final int REQUEST_CERTIFY = 1;
public static final String ARG_DATA_URI = "uri";
+ public static final String ARG_ENABLE_WORD_CONFIRM = "enable_word_confirm";
private TextView mFingerprint;
+ private TextView mIntro;
private static final int LOADER_ID_UNIFIED = 0;
private Uri mDataUri;
-
- private View mActionNo;
- private View mActionYes;
+ private boolean mEnableWordConfirm;
/**
* Creates new instance of this fragment
*/
- public static CertifyFingerprintFragment newInstance(Uri dataUri) {
+ public static CertifyFingerprintFragment newInstance(Uri dataUri, boolean enableWordConfirm) {
CertifyFingerprintFragment frag = new CertifyFingerprintFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_DATA_URI, dataUri);
+ args.putBoolean(ARG_ENABLE_WORD_CONFIRM, enableWordConfirm);
frag.setArguments(args);
@@ -72,18 +75,19 @@ public class CertifyFingerprintFragment extends LoaderFragment implements
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
View view = inflater.inflate(R.layout.certify_fingerprint_fragment, getContainer());
- mActionNo = view.findViewById(R.id.certify_fingerprint_button_no);
- mActionYes = view.findViewById(R.id.certify_fingerprint_button_yes);
+ View actionNo = view.findViewById(R.id.certify_fingerprint_button_no);
+ View actionYes = view.findViewById(R.id.certify_fingerprint_button_yes);
mFingerprint = (TextView) view.findViewById(R.id.certify_fingerprint_fingerprint);
+ mIntro = (TextView) view.findViewById(R.id.certify_fingerprint_intro);
- mActionNo.setOnClickListener(new View.OnClickListener() {
+ actionNo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getActivity().finish();
}
});
- mActionYes.setOnClickListener(new View.OnClickListener() {
+ actionYes.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
certify(mDataUri);
@@ -103,6 +107,11 @@ public class CertifyFingerprintFragment extends LoaderFragment implements
getActivity().finish();
return;
}
+ mEnableWordConfirm = getArguments().getBoolean(ARG_ENABLE_WORD_CONFIRM);
+
+ if (mEnableWordConfirm) {
+ mIntro.setText(R.string.certify_fingerprint_text_words);
+ }
loadData(dataUri);
}
@@ -149,10 +158,13 @@ public class CertifyFingerprintFragment extends LoaderFragment implements
switch (loader.getId()) {
case LOADER_ID_UNIFIED: {
if (data.moveToFirst()) {
-
byte[] fingerprintBlob = data.getBlob(INDEX_UNIFIED_FINGERPRINT);
- String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fingerprintBlob);
- mFingerprint.setText(KeyFormattingUtils.colorizeFingerprint(fingerprint));
+
+ if (mEnableWordConfirm) {
+ displayWordConfirm(fingerprintBlob);
+ } else {
+ displayHexConfirm(fingerprintBlob);
+ }
break;
}
@@ -162,6 +174,19 @@ public class CertifyFingerprintFragment extends LoaderFragment implements
setContentShown(true);
}
+ private void displayHexConfirm(byte[] fingerprintBlob) {
+ String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fingerprintBlob);
+ mFingerprint.setText(KeyFormattingUtils.colorizeFingerprint(fingerprint));
+ }
+
+ private void displayWordConfirm(byte[] fingerprintBlob) {
+ String fingerprint = ExperimentalWordConfirm.getWords(getActivity(), fingerprintBlob);
+
+ mFingerprint.setTextSize(24);
+ mFingerprint.setTypeface(Typeface.DEFAULT, Typeface.BOLD);
+ mFingerprint.setText(fingerprint);
+ }
+
/**
* 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.
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
index 51b6e824d..357b445f0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
@@ -19,15 +19,12 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.os.Parcel;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
@@ -52,24 +49,24 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.adapter.MultiUserIdsAdapter;
-import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
-import org.sufficientlysecure.keychain.ui.widget.KeySpinner;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList;
+import java.util.Date;
-
-public class CertifyKeyFragment extends CryptoOperationFragment
+public class CertifyKeyFragment
+ extends CachingCryptoOperationFragment<CertifyActionsParcel, CertifyResult>
implements LoaderManager.LoaderCallbacks<Cursor> {
+ public static final String ARG_CHECK_STATES = "check_states";
+
private CheckBox mUploadKeyCheckbox;
ListView mUserIds;
@@ -77,8 +74,6 @@ public class CertifyKeyFragment extends CryptoOperationFragment
private long[] mPubMasterKeyIds;
- private long mSignMasterKeyId = Constants.key.none;
-
public static final String[] USER_IDS_PROJECTION = new String[]{
UserPackets._ID,
UserPackets.MASTER_KEY_ID,
@@ -94,7 +89,6 @@ public class CertifyKeyFragment extends CryptoOperationFragment
private static final int INDEX_IS_REVOKED = 4;
private MultiUserIdsAdapter mUserIdsAdapter;
- private Messenger mPassthroughMessenger;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
@@ -107,24 +101,30 @@ public class CertifyKeyFragment extends CryptoOperationFragment
return;
}
- mPassthroughMessenger = getActivity().getIntent().getParcelableExtra(
- KeychainIntentService.EXTRA_MESSENGER);
- mPassthroughMessenger = null; // TODO remove, development hack
-
- // preselect certify key id if given
- long certifyKeyId = getActivity().getIntent().getLongExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, Constants.key.none);
- if (certifyKeyId != Constants.key.none) {
- try {
- CachedPublicKeyRing key = (new ProviderHelper(getActivity())).getCachedPublicKeyRing(certifyKeyId);
- if (key.canCertify()) {
- mCertifyKeySpinner.setSelectedKeyId(certifyKeyId);
+ ArrayList<Boolean> checkedStates;
+ if (savedInstanceState != null) {
+ checkedStates = (ArrayList<Boolean>) savedInstanceState.getSerializable(ARG_CHECK_STATES);
+ // key spinner and the checkbox keep their own state
+ } else {
+ checkedStates = null;
+
+ // preselect certify key id if given
+ long certifyKeyId = getActivity().getIntent()
+ .getLongExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, Constants.key.none);
+ if (certifyKeyId != Constants.key.none) {
+ try {
+ CachedPublicKeyRing key = (new ProviderHelper(getActivity())).getCachedPublicKeyRing(certifyKeyId);
+ if (key.canCertify()) {
+ mCertifyKeySpinner.setPreSelectedKeyId(certifyKeyId);
+ }
+ } catch (PgpKeyNotFoundException e) {
+ Log.e(Constants.TAG, "certify certify check failed", e);
}
- } catch (PgpKeyNotFoundException e) {
- Log.e(Constants.TAG, "certify certify check failed", e);
}
+
}
- mUserIdsAdapter = new MultiUserIdsAdapter(getActivity(), null, 0);
+ mUserIdsAdapter = new MultiUserIdsAdapter(getActivity(), null, 0, checkedStates);
mUserIds.setAdapter(mUserIdsAdapter);
mUserIds.setDividerHeight(0);
@@ -138,6 +138,15 @@ public class CertifyKeyFragment extends CryptoOperationFragment
}
@Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ ArrayList<Boolean> states = mUserIdsAdapter.getCheckStates();
+ // no proper parceling method available :(
+ outState.putSerializable(ARG_CHECK_STATES, states);
+ }
+
+ @Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.certify_key_fragment, null);
@@ -148,26 +157,20 @@ public class CertifyKeyFragment extends CryptoOperationFragment
// make certify image gray, like action icons
ImageView vActionCertifyImage =
(ImageView) view.findViewById(R.id.certify_key_action_certify_image);
- vActionCertifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
+ vActionCertifyImage.setColorFilter(FormattingUtils.getColorFromAttr(getActivity(), R.attr.colorTertiaryText),
PorterDuff.Mode.SRC_IN);
- mCertifyKeySpinner.setOnKeyChangedListener(new KeySpinner.OnKeyChangedListener() {
- @Override
- public void onKeyChanged(long masterKeyId) {
- mSignMasterKeyId = masterKeyId;
- }
- });
-
View vCertifyButton = view.findViewById(R.id.certify_key_certify_button);
vCertifyButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- if (mSignMasterKeyId == Constants.key.none) {
+ long selectedKeyId = mCertifyKeySpinner.getSelectedKeyId();
+ if (selectedKeyId == Constants.key.none) {
Notify.create(getActivity(), getString(R.string.select_key_to_certify),
Notify.Style.ERROR).show();
} else {
- cryptoOperation(new CryptoInputParcel());
+ cryptoOperation(new CryptoInputParcel(new Date()));
}
}
});
@@ -298,82 +301,42 @@ public class CertifyKeyFragment extends CryptoOperationFragment
}
@Override
- protected void cryptoOperation(CryptoInputParcel cryptoInput) {
+ public CertifyActionsParcel createOperationInput() {
+
// Bail out if there is not at least one user id selected
ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions();
if (certifyActions.isEmpty()) {
Notify.create(getActivity(), "No identities selected!",
Notify.Style.ERROR).show();
- return;
- }
-
- Bundle data = new Bundle();
- {
- // fill values for this action
- CertifyActionsParcel parcel = new CertifyActionsParcel(mSignMasterKeyId);
- parcel.mCertifyActions.addAll(certifyActions);
-
- data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput);
- data.putParcelable(KeychainIntentService.CERTIFY_PARCEL, parcel);
- if (mUploadKeyCheckbox.isChecked()) {
- String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
- data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, keyserver);
- }
+ return null;
}
- // Send all information needed to service to sign key in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_CERTIFY_KEYRING);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+ long selectedKeyId = mCertifyKeySpinner.getSelectedKeyId();
- if (mPassthroughMessenger != null) {
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, mPassthroughMessenger);
- } else {
+ // fill values for this action
+ CertifyActionsParcel actionsParcel = new CertifyActionsParcel(selectedKeyId);
+ actionsParcel.mCertifyActions.addAll(certifyActions);
- // Message is received after signing is done in KeychainIntentService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_certifying),
- ProgressDialog.STYLE_SPINNER,
- true,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- // handle messages by KeychainIntentCryptoServiceHandler first
- super.handleMessage(message);
-
- // handle pending messages
- if (handlePendingMessage(message)) {
- return;
- }
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- Bundle data = message.getData();
-
- CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);
-
- Intent intent = new Intent();
- intent.putExtra(CertifyResult.EXTRA_RESULT, result);
- getActivity().setResult(Activity.RESULT_OK, intent);
- getActivity().finish();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(getActivity());
+ if (mUploadKeyCheckbox.isChecked()) {
+ actionsParcel.keyServerUri = Preferences.getPreferences(getActivity())
+ .getPreferredKeyserver();
}
- // start service with intent
- getActivity().startService(intent);
+ // cached for next cryptoOperation loop
+ cacheActionsParcel(actionsParcel);
- if (mPassthroughMessenger != null) {
- getActivity().setResult(Activity.RESULT_OK);
- getActivity().finish();
- }
+ return actionsParcel;
+ }
+
+ @Override
+ public void onQueuedOperationSuccess(CertifyResult result) {
+ // protected by Queueing*Fragment
+ Activity activity = getActivity();
+
+ Intent intent = new Intent();
+ intent.putExtra(CertifyResult.EXTRA_RESULT, result);
+ activity.setResult(Activity.RESULT_OK, intent);
+ activity.finish();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java
index 11ba3e287..ff5fb7cca 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java
@@ -17,26 +17,27 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.support.v4.app.FragmentActivity;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
+import org.sufficientlysecure.keychain.service.ConsolidateInputParcel;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
/**
* We can not directly create a dialog on the application context.
* This activity encapsulates a DialogFragment to emulate a dialog.
*/
-public class ConsolidateDialogActivity extends FragmentActivity {
+public class ConsolidateDialogActivity extends FragmentActivity
+ implements CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult> {
public static final String EXTRA_CONSOLIDATE_RECOVERY = "consolidate_recovery";
+ private CryptoOperationHelper<ConsolidateInputParcel, ConsolidateResult> mConsolidateOpHelper;
+ private boolean mRecovery;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -49,55 +50,45 @@ public class ConsolidateDialogActivity extends FragmentActivity {
}
private void consolidateRecovery(boolean recovery) {
- // Message is received after importing is done in KeychainIntentService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- this,
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- /* don't care about the results (for now?)
-
- // get returned data bundle
- Bundle returnData = message.getInputData();
- if (returnData == null) {
- return;
- }
- final ConsolidateResult result =
- returnData.getParcelable(KeychainIntentService.RESULT_CONSOLIDATE);
- if (result == null) {
- return;
- }
- result.createNotify(ConsolidateDialogActivity.this).show();
- */
-
- ConsolidateDialogActivity.this.finish();
- }
- }
- };
-
- // Send all information needed to service to import key in other thread
- Intent intent = new Intent(this, KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_CONSOLIDATE);
-
- // fill values for this action
- Bundle data = new Bundle();
- data.putBoolean(KeychainIntentService.CONSOLIDATE_RECOVERY, recovery);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(this);
-
- // start service with intent
- startService(intent);
+
+ mRecovery = recovery;
+
+ mConsolidateOpHelper = new CryptoOperationHelper<>(1, this, this, R.string.progress_importing);
+ mConsolidateOpHelper.cryptoOperation();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (mConsolidateOpHelper != null) {
+ mConsolidateOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
}
+ @Override
+ public ConsolidateInputParcel createOperationInput() {
+ return new ConsolidateInputParcel(mRecovery);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ConsolidateResult result) {
+ // don't care about result (for now?)
+ ConsolidateDialogActivity.this.finish();
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(ConsolidateResult result) {
+ // don't care about result (for now?)
+ ConsolidateDialogActivity.this.finish();
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
index e0b728bd4..579a001cb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
+import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
@@ -29,7 +30,9 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Passphrase;
+import org.sufficientlysecure.keychain.util.Preferences;
import java.io.IOException;
import java.util.ArrayList;
@@ -41,6 +44,9 @@ public class CreateKeyActivity extends BaseNfcActivity {
public static final String EXTRA_FIRST_TIME = "first_time";
public static final String EXTRA_ADDITIONAL_EMAILS = "additional_emails";
public static final String EXTRA_PASSPHRASE = "passphrase";
+ public static final String EXTRA_CREATE_YUBI_KEY = "create_yubi_key";
+ public static final String EXTRA_YUBI_KEY_PIN = "yubi_key_pin";
+ public static final String EXTRA_YUBI_KEY_ADMIN_PIN = "yubi_key_admin_pin";
public static final String EXTRA_NFC_USER_ID = "nfc_user_id";
public static final String EXTRA_NFC_AID = "nfc_aid";
@@ -53,13 +59,33 @@ public class CreateKeyActivity extends BaseNfcActivity {
ArrayList<String> mAdditionalEmails;
Passphrase mPassphrase;
boolean mFirstTime;
+ boolean mCreateYubiKey;
+ Passphrase mYubiKeyPin;
+ Passphrase mYubiKeyAdminPin;
Fragment mCurrentFragment;
+
+ byte[] mScannedFingerprints;
+ byte[] mNfcAid;
+ String mNfcUserId;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ // React on NDEF_DISCOVERED from Manifest
+ // NOTE: ACTION_NDEF_DISCOVERED and not ACTION_TAG_DISCOVERED like in BaseNfcActivity
+ if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
+
+ handleIntentInBackground(getIntent());
+
+ setTitle(R.string.title_manage_my_keys);
+
+ // done
+ return;
+ }
+
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
// Restore value of members from saved state
@@ -68,6 +94,9 @@ public class CreateKeyActivity extends BaseNfcActivity {
mAdditionalEmails = savedInstanceState.getStringArrayList(EXTRA_ADDITIONAL_EMAILS);
mPassphrase = savedInstanceState.getParcelable(EXTRA_PASSPHRASE);
mFirstTime = savedInstanceState.getBoolean(EXTRA_FIRST_TIME);
+ mCreateYubiKey = savedInstanceState.getBoolean(EXTRA_CREATE_YUBI_KEY);
+ mYubiKeyPin = savedInstanceState.getParcelable(EXTRA_YUBI_KEY_PIN);
+ mYubiKeyAdminPin = savedInstanceState.getParcelable(EXTRA_YUBI_KEY_ADMIN_PIN);
mCurrentFragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG);
} else {
@@ -77,23 +106,36 @@ public class CreateKeyActivity extends BaseNfcActivity {
mName = intent.getStringExtra(EXTRA_NAME);
mEmail = intent.getStringExtra(EXTRA_EMAIL);
mFirstTime = intent.getBooleanExtra(EXTRA_FIRST_TIME, false);
+ mCreateYubiKey = intent.getBooleanExtra(EXTRA_CREATE_YUBI_KEY, false);
if (intent.hasExtra(EXTRA_NFC_FINGERPRINTS)) {
byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_NFC_FINGERPRINTS);
String nfcUserId = intent.getStringExtra(EXTRA_NFC_USER_ID);
byte[] nfcAid = intent.getByteArrayExtra(EXTRA_NFC_AID);
- Fragment frag2 = CreateKeyYubiKeyImportFragment.createInstance(
- nfcFingerprints, nfcAid, nfcUserId);
- loadFragment(frag2, FragAction.START);
-
- setTitle(R.string.title_import_keys);
+ if (containsKeys(nfcFingerprints)) {
+ Fragment frag = CreateYubiKeyImportFragment.newInstance(
+ nfcFingerprints, nfcAid, nfcUserId);
+ loadFragment(frag, FragAction.START);
+
+ setTitle(R.string.title_import_keys);
+ } else {
+// Fragment frag = CreateYubiKeyBlankFragment.newInstance();
+// loadFragment(frag, FragAction.START);
+// setTitle(R.string.title_manage_my_keys);
+ Notify.create(this,
+ "YubiKey key creation is currently not supported. Please follow our FAQ.",
+ Notify.Style.ERROR
+ ).show();
+ }
+
+ // done
return;
- } else {
- CreateKeyStartFragment frag = CreateKeyStartFragment.newInstance();
- loadFragment(frag, FragAction.START);
}
+ // normal key creation
+ CreateKeyStartFragment frag = CreateKeyStartFragment.newInstance();
+ loadFragment(frag, FragAction.START);
}
if (mFirstTime) {
@@ -106,35 +148,63 @@ public class CreateKeyActivity extends BaseNfcActivity {
}
@Override
- protected void onNfcPerform() throws IOException {
+ protected void doNfcInBackground() throws IOException {
if (mCurrentFragment instanceof NfcListenerFragment) {
- ((NfcListenerFragment) mCurrentFragment).onNfcPerform();
+ ((NfcListenerFragment) mCurrentFragment).doNfcInBackground();
return;
}
- byte[] scannedFingerprints = nfcGetFingerprints();
- byte[] nfcAid = nfcGetAid();
- String userId = nfcGetUserId();
-
- try {
- long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(scannedFingerprints);
- CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(masterKeyId);
- ring.getMasterKeyId();
+ mScannedFingerprints = nfcGetFingerprints();
+ mNfcAid = nfcGetAid();
+ mNfcUserId = nfcGetUserId();
+ }
- Intent intent = new Intent(this, ViewKeyActivity.class);
- intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid);
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, userId);
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, scannedFingerprints);
- startActivity(intent);
- finish();
+ @Override
+ protected void onNfcPostExecute() throws IOException {
+ if (mCurrentFragment instanceof NfcListenerFragment) {
+ ((NfcListenerFragment) mCurrentFragment).onNfcPostExecute();
+ return;
+ }
- } catch (PgpKeyNotFoundException e) {
- Fragment frag = CreateKeyYubiKeyImportFragment.createInstance(
- scannedFingerprints, nfcAid, userId);
- loadFragment(frag, FragAction.TO_RIGHT);
+ if (containsKeys(mScannedFingerprints)) {
+ try {
+ long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mScannedFingerprints);
+ CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(masterKeyId);
+ ring.getMasterKeyId();
+
+ Intent intent = new Intent(this, ViewKeyActivity.class);
+ intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mScannedFingerprints);
+ startActivity(intent);
+ finish();
+
+ } catch (PgpKeyNotFoundException e) {
+ Fragment frag = CreateYubiKeyImportFragment.newInstance(
+ mScannedFingerprints, mNfcAid, mNfcUserId);
+ loadFragment(frag, FragAction.TO_RIGHT);
+ }
+ } else {
+// Fragment frag = CreateYubiKeyBlankFragment.newInstance();
+// loadFragment(frag, FragAction.TO_RIGHT);
+ Notify.create(this,
+ "YubiKey key creation is currently not supported. Please follow our FAQ.",
+ Notify.Style.ERROR
+ ).show();
}
+ }
+ private boolean containsKeys(byte[] scannedFingerprints) {
+ // If all fingerprint bytes are 0, the card contains no keys.
+ boolean cardContainsKeys = false;
+ for (byte b : scannedFingerprints) {
+ if (b != 0) {
+ cardContainsKeys = true;
+ break;
+ }
+ }
+ return cardContainsKeys;
}
@Override
@@ -146,6 +216,9 @@ public class CreateKeyActivity extends BaseNfcActivity {
outState.putStringArrayList(EXTRA_ADDITIONAL_EMAILS, mAdditionalEmails);
outState.putParcelable(EXTRA_PASSPHRASE, mPassphrase);
outState.putBoolean(EXTRA_FIRST_TIME, mFirstTime);
+ outState.putBoolean(EXTRA_CREATE_YUBI_KEY, mCreateYubiKey);
+ outState.putParcelable(EXTRA_YUBI_KEY_PIN, mYubiKeyPin);
+ outState.putParcelable(EXTRA_YUBI_KEY_ADMIN_PIN, mYubiKeyAdminPin);
}
@Override
@@ -153,7 +226,7 @@ public class CreateKeyActivity extends BaseNfcActivity {
setContentView(R.layout.create_key_activity);
}
- public static enum FragAction {
+ public enum FragAction {
START,
TO_RIGHT,
TO_LEFT
@@ -190,7 +263,19 @@ public class CreateKeyActivity extends BaseNfcActivity {
}
interface NfcListenerFragment {
- public void onNfcPerform() throws IOException;
+ void doNfcInBackground() throws IOException;
+ void onNfcPostExecute() throws IOException;
}
+ @Override
+ public void finish() {
+ if (mFirstTime) {
+ Preferences prefs = Preferences.getPreferences(this);
+ prefs.setFirstTime(false);
+ Intent intent = new Intent(this, MainActivity.class);
+ startActivity(intent);
+ }
+
+ super.finish();
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java
index 597f04d6b..acb768f55 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
+import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -29,6 +30,7 @@ import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
@@ -37,7 +39,6 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.ui.dialog.AddEmailDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.widget.EmailEditText;
@@ -201,7 +202,7 @@ public class CreateKeyEmailFragment extends Fragment {
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
- if (message.what == SetPassphraseDialogFragment.MESSAGE_OKAY) {
+ if (message.what == AddEmailDialogFragment.MESSAGE_OKAY) {
Bundle data = message.getData();
String email = data.getString(AddEmailDialogFragment.MESSAGE_DATA_EMAIL);
@@ -232,11 +233,35 @@ public class CreateKeyEmailFragment extends Fragment {
mCreateKeyActivity.mEmail = mEmailEdit.getText().toString();
mCreateKeyActivity.mAdditionalEmails = getAdditionalEmails();
- CreateKeyPassphraseFragment frag = CreateKeyPassphraseFragment.newInstance();
- mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
+ CreateKeyActivity createKeyActivity = ((CreateKeyActivity) getActivity());
+
+ if (createKeyActivity.mCreateYubiKey) {
+ hideKeyboard();
+
+ CreateYubiKeyPinFragment frag = CreateYubiKeyPinFragment.newInstance();
+ mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
+ } else {
+ CreateKeyPassphraseFragment frag = CreateKeyPassphraseFragment.newInstance();
+ mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
+ }
}
}
+ private void hideKeyboard() {
+ if (getActivity() == null) {
+ return;
+ }
+ InputMethodManager inputManager = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ // check if no view has focus
+ View v = getActivity().getCurrentFocus();
+ if (v == null)
+ return;
+
+ inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
+ }
+
private ArrayList<String> getAdditionalEmails() {
ArrayList<String> emails = new ArrayList<>();
for (EmailAdapter.ViewModel holder : mAdditionalEmailModels) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
index b0a13c897..739eb3e35 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014-2015 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,13 +17,15 @@
package org.sufficientlysecure.keychain.ui;
+
+import java.util.Date;
+import java.util.Iterator;
+
import android.app.Activity;
-import android.app.ProgressDialog;
import android.content.Intent;
+import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
@@ -35,27 +37,28 @@ import org.spongycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
+import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences;
-import java.util.Iterator;
-
public class CreateKeyFinalFragment extends Fragment {
public static final int REQUEST_EDIT_KEY = 0x00008007;
- CreateKeyActivity mCreateKeyActivity;
-
TextView mNameEdit;
TextView mEmailEdit;
CheckBox mUploadCheckbox;
@@ -66,11 +69,18 @@ public class CreateKeyFinalFragment extends Fragment {
SaveKeyringParcel mSaveKeyringParcel;
- /**
- * Creates new instance of this fragment
- */
+ private CryptoOperationHelper<ExportKeyringParcel, ExportResult> mUploadOpHelper;
+ private CryptoOperationHelper<SaveKeyringParcel, EditKeyResult> mCreateOpHelper;
+ private CryptoOperationHelper<SaveKeyringParcel, EditKeyResult> mMoveToCardOpHelper;
+
+ // queued results which may trigger delayed actions
+ private EditKeyResult mQueuedSaveKeyResult;
+ private OperationResult mQueuedFinishResult;
+ private EditKeyResult mQueuedDisplayResult;
+
public static CreateKeyFinalFragment newInstance() {
CreateKeyFinalFragment frag = new CreateKeyFinalFragment();
+ frag.setRetainInstance(true);
Bundle args = new Bundle();
frag.setArguments(args);
@@ -90,11 +100,13 @@ public class CreateKeyFinalFragment extends Fragment {
mEditText = (TextView) view.findViewById(R.id.create_key_edit_text);
mEditButton = view.findViewById(R.id.create_key_edit_button);
+ CreateKeyActivity createKeyActivity = (CreateKeyActivity) getActivity();
+
// set values
- mNameEdit.setText(mCreateKeyActivity.mName);
- if (mCreateKeyActivity.mAdditionalEmails != null && mCreateKeyActivity.mAdditionalEmails.size() > 0) {
- String emailText = mCreateKeyActivity.mEmail + ", ";
- Iterator<?> it = mCreateKeyActivity.mAdditionalEmails.iterator();
+ mNameEdit.setText(createKeyActivity.mName);
+ if (createKeyActivity.mAdditionalEmails != null && createKeyActivity.mAdditionalEmails.size() > 0) {
+ String emailText = createKeyActivity.mEmail + ", ";
+ Iterator<?> it = createKeyActivity.mAdditionalEmails.iterator();
while (it.hasNext()) {
Object next = it.next();
emailText += next;
@@ -104,7 +116,7 @@ public class CreateKeyFinalFragment extends Fragment {
}
mEmailEdit.setText(emailText);
} else {
- mEmailEdit.setText(mCreateKeyActivity.mEmail);
+ mEmailEdit.setText(createKeyActivity.mEmail);
}
mCreateButton.setOnClickListener(new View.OnClickListener() {
@@ -117,7 +129,10 @@ public class CreateKeyFinalFragment extends Fragment {
mBackButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
+ CreateKeyActivity createKeyActivity = (CreateKeyActivity) getActivity();
+ if (createKeyActivity != null) {
+ createKeyActivity.loadFragment(null, FragAction.TO_LEFT);
+ }
}
});
@@ -130,17 +145,26 @@ public class CreateKeyFinalFragment extends Fragment {
}
});
- return view;
- }
+ // If this is a debug build, don't upload by default
+ if (Constants.DEBUG) {
+ mUploadCheckbox.setChecked(false);
+ }
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- mCreateKeyActivity = (CreateKeyActivity) getActivity();
+ return view;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mCreateOpHelper != null) {
+ mCreateOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+ if (mMoveToCardOpHelper != null) {
+ mMoveToCardOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+ if (mUploadOpHelper != null) {
+ mUploadOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+
switch (requestCode) {
case REQUEST_EDIT_KEY: {
if (resultCode == Activity.RESULT_OK) {
@@ -159,147 +183,283 @@ public class CreateKeyFinalFragment extends Fragment {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- mCreateKeyActivity = (CreateKeyActivity) getActivity();
+ CreateKeyActivity createKeyActivity = (CreateKeyActivity) getActivity();
if (mSaveKeyringParcel == null) {
mSaveKeyringParcel = new SaveKeyringParcel();
- mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.RSA, 4096, null, KeyFlags.CERTIFY_OTHER, 0L));
- mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.RSA, 4096, null, KeyFlags.SIGN_DATA, 0L));
- mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.RSA, 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L));
+
+ if (createKeyActivity.mCreateYubiKey) {
+ mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
+ 2048, null, KeyFlags.SIGN_DATA | KeyFlags.CERTIFY_OTHER, 0L));
+ mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
+ 2048, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L));
+ mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
+ 2048, null, KeyFlags.AUTHENTICATION, 0L));
+ mEditText.setText(R.string.create_key_custom);
+ mEditButton.setEnabled(false);
+
+ // use empty passphrase
+ mSaveKeyringParcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase(), null);
+ } else {
+ mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
+ 4096, null, KeyFlags.CERTIFY_OTHER, 0L));
+ mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
+ 4096, null, KeyFlags.SIGN_DATA, 0L));
+ mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
+ 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L));
+
+ mSaveKeyringParcel.mNewUnlock = createKeyActivity.mPassphrase != null
+ ? new ChangeUnlockParcel(createKeyActivity.mPassphrase, null)
+ : null;
+ }
String userId = KeyRing.createUserId(
- new KeyRing.UserId(mCreateKeyActivity.mName, mCreateKeyActivity.mEmail, null)
+ new KeyRing.UserId(createKeyActivity.mName, createKeyActivity.mEmail, null)
);
mSaveKeyringParcel.mAddUserIds.add(userId);
mSaveKeyringParcel.mChangePrimaryUserId = userId;
- if (mCreateKeyActivity.mAdditionalEmails != null
- && mCreateKeyActivity.mAdditionalEmails.size() > 0) {
- for (String email : mCreateKeyActivity.mAdditionalEmails) {
+ if (createKeyActivity.mAdditionalEmails != null
+ && createKeyActivity.mAdditionalEmails.size() > 0) {
+ for (String email : createKeyActivity.mAdditionalEmails) {
String thisUserId = KeyRing.createUserId(
- new KeyRing.UserId(mCreateKeyActivity.mName, email, null)
+ new KeyRing.UserId(createKeyActivity.mName, email, null)
);
mSaveKeyringParcel.mAddUserIds.add(thisUserId);
}
}
- mSaveKeyringParcel.mNewUnlock = mCreateKeyActivity.mPassphrase != null
- ? new ChangeUnlockParcel(mCreateKeyActivity.mPassphrase, null)
- : null;
}
- }
+ // handle queued actions
+
+ if (mQueuedFinishResult != null) {
+ finishWithResult(mQueuedFinishResult);
+ return;
+ }
+
+ if (mQueuedDisplayResult != null) {
+ try {
+ displayResult(mQueuedDisplayResult);
+ } finally {
+ // clear after operation, note that this may drop the operation if it didn't
+ // work when called from here!
+ mQueuedDisplayResult = null;
+ }
+ }
+
+ if (mQueuedSaveKeyResult != null) {
+ try {
+ uploadKey(mQueuedSaveKeyResult);
+ } finally {
+ // see above
+ mQueuedSaveKeyResult = null;
+ }
+ }
+
+ }
private void createKey() {
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING);
-
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_building_key),
- ProgressDialog.STYLE_HORIZONTAL,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
- if (returnData == null) {
- return;
- }
- final EditKeyResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null) {
- Log.e(Constants.TAG, "result == null");
- return;
- }
-
- if (result.mMasterKeyId != null && mUploadCheckbox.isChecked()) {
- // result will be displayed after upload
- uploadKey(result);
- } else {
- Intent data = new Intent();
- data.putExtra(OperationResult.EXTRA_RESULT, result);
- getActivity().setResult(Activity.RESULT_OK, data);
- getActivity().finish();
- }
+ CreateKeyActivity activity = (CreateKeyActivity) getActivity();
+ if (activity == null) {
+ // this is a ui-triggered action, nvm if it fails while detached!
+ return;
+ }
+
+ final boolean createYubiKey = activity.mCreateYubiKey;
+
+ CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult> createKeyCallback
+ = new CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult>() {
+ @Override
+ public SaveKeyringParcel createOperationInput() {
+ return mSaveKeyringParcel;
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(EditKeyResult result) {
+
+ if (createYubiKey) {
+ moveToCard(result);
+ return;
}
+
+ if (result.mMasterKeyId != null && mUploadCheckbox.isChecked()) {
+ // result will be displayed after upload
+ uploadKey(result);
+ return;
+ }
+
+ finishWithResult(result);
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(EditKeyResult result) {
+ displayResult(result);
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
}
};
- // fill values for this action
- Bundle data = new Bundle();
+ mCreateOpHelper = new CryptoOperationHelper<>(1, this, createKeyCallback, R.string.progress_building_key);
+ mCreateOpHelper.cryptoOperation();
+ }
- // get selected key entries
- data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel);
+ private void displayResult(EditKeyResult result) {
+ Activity activity = getActivity();
+ if (activity == null) {
+ mQueuedDisplayResult = result;
+ return;
+ }
+ result.createNotify(activity).show();
+ }
+
+ private void moveToCard(final EditKeyResult saveKeyResult) {
+ CreateKeyActivity activity = (CreateKeyActivity) getActivity();
+
+ final SaveKeyringParcel changeKeyringParcel;
+ CachedPublicKeyRing key = (new ProviderHelper(activity))
+ .getCachedPublicKeyRing(saveKeyResult.mMasterKeyId);
+ try {
+ changeKeyringParcel = new SaveKeyringParcel(key.getMasterKeyId(), key.getFingerprint());
+ } catch (PgpKeyNotFoundException e) {
+ Log.e(Constants.TAG, "Key that should be moved to YubiKey not found in database!");
+ return;
+ }
+
+ // define subkeys that should be moved to the card
+ Cursor cursor = activity.getContentResolver().query(
+ KeychainContract.Keys.buildKeysUri(changeKeyringParcel.mMasterKeyId),
+ new String[] { KeychainContract.Keys.KEY_ID, }, null, null, null
+ );
+ try {
+ while (cursor != null && cursor.moveToNext()) {
+ long subkeyId = cursor.getLong(0);
+ changeKeyringParcel.getOrCreateSubkeyChange(subkeyId).mMoveKeyToCard = true;
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+ // define new PIN and Admin PIN for the card
+ changeKeyringParcel.mCardPin = activity.mYubiKeyPin;
+ changeKeyringParcel.mCardAdminPin = activity.mYubiKeyAdminPin;
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+ CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult> callback
+ = new CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult>() {
- saveHandler.showProgressDialog(getActivity());
+ @Override
+ public SaveKeyringParcel createOperationInput() {
+ return changeKeyringParcel;
+ }
- getActivity().startService(intent);
+ @Override
+ public void onCryptoOperationSuccess(EditKeyResult result) {
+ handleResult(result);
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(EditKeyResult result) {
+ handleResult(result);
+ }
+
+ public void handleResult(EditKeyResult result) {
+ // merge logs of createKey with moveToCard
+ saveKeyResult.getLog().add(result, 0);
+
+ if (result.mMasterKeyId != null && mUploadCheckbox.isChecked()) {
+ // result will be displayed after upload
+ uploadKey(saveKeyResult);
+ return;
+ }
+
+ finishWithResult(saveKeyResult);
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
+ };
+
+
+ mMoveToCardOpHelper = new CryptoOperationHelper<>(2, this, callback, R.string.progress_modify);
+ mMoveToCardOpHelper.cryptoOperation(new CryptoInputParcel(new Date()));
}
- // TODO move into EditKeyOperation
private void uploadKey(final EditKeyResult saveKeyResult) {
- // Send all information needed to service to upload key in other thread
- final Intent intent = new Intent(getActivity(), KeychainIntentService.class);
-
- intent.setAction(KeychainIntentService.ACTION_UPLOAD_KEYRING);
+ Activity activity = getActivity();
+ // if the activity is gone at this point, there is nothing we can do!
+ if (activity == null) {
+ mQueuedSaveKeyResult = saveKeyResult;
+ return;
+ }
// set data uri as path to keyring
- Uri blobUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(
- saveKeyResult.mMasterKeyId);
- intent.setData(blobUri);
+ final Uri blobUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(saveKeyResult.mMasterKeyId);
+ // upload to favorite keyserver
+ final String keyserver = Preferences.getPreferences(activity).getPreferredKeyserver();
- // fill values for this action
- Bundle data = new Bundle();
+ CryptoOperationHelper.Callback<ExportKeyringParcel, ExportResult> callback
+ = new CryptoOperationHelper.Callback<ExportKeyringParcel, ExportResult>() {
+
+ @Override
+ public ExportKeyringParcel createOperationInput() {
+ return new ExportKeyringParcel(keyserver, blobUri);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ExportResult result) {
+ handleResult(result);
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
- // upload to favorite keyserver
- String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
- data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, keyserver);
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_uploading),
- ProgressDialog.STYLE_HORIZONTAL,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // TODO: upload operation needs a result!
- // TODO: then combine these results
- //if (result.getResult() == OperationResultParcel.RESULT_OK) {
- //Notify.create(getActivity(), R.string.key_send_success,
- //Notify.Style.OK).show();
-
- Intent data = new Intent();
- data.putExtra(OperationResult.EXTRA_RESULT, saveKeyResult);
- getActivity().setResult(Activity.RESULT_OK, data);
- getActivity().finish();
- }
+ }
+
+ @Override
+ public void onCryptoOperationError(ExportResult result) {
+ handleResult(result);
+ }
+
+ public void handleResult(ExportResult result) {
+ saveKeyResult.getLog().add(result, 0);
+ finishWithResult(saveKeyResult);
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
}
};
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+ mUploadOpHelper = new CryptoOperationHelper<>(3, this, callback, R.string.progress_uploading);
+ mUploadOpHelper.cryptoOperation();
+ }
- // show progress dialog
- saveHandler.showProgressDialog(getActivity());
+ public void finishWithResult(OperationResult result) {
+ Activity activity = getActivity();
+ if (activity == null) {
+ mQueuedFinishResult = result;
+ return;
+ }
- // start service with intent
- getActivity().startService(intent);
+ Intent data = new Intent();
+ data.putExtra(OperationResult.EXTRA_RESULT, result);
+ activity.setResult(Activity.RESULT_OK, data);
+ activity.finish();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java
index 3379e0a6d..d858fd6ec 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java
@@ -21,6 +21,8 @@ import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextWatcher;
import android.text.method.HideReturnsTransformationMethod;
import android.text.method.PasswordTransformationMethod;
import android.view.LayoutInflater;
@@ -79,19 +81,10 @@ public class CreateKeyPassphraseFragment extends Fragment {
return output;
}
- private static boolean areEditTextsEqual(Context context, EditText editText1, EditText editText2) {
+ private static boolean areEditTextsEqual(EditText editText1, EditText editText2) {
Passphrase p1 = new Passphrase(editText1);
Passphrase p2 = new Passphrase(editText2);
- boolean output = (p1.equals(p2));
-
- if (!output) {
- editText2.setError(context.getString(R.string.create_key_passphrases_not_equal));
- editText2.requestFocus();
- } else {
- editText2.setError(null);
- }
-
- return output;
+ return (p1.equals(p2));
}
@Override
@@ -107,8 +100,8 @@ public class CreateKeyPassphraseFragment extends Fragment {
// initial values
// TODO: using String here is unsafe...
if (mCreateKeyActivity.mPassphrase != null) {
- mPassphraseEdit.setText(new String(mCreateKeyActivity.mPassphrase.getCharArray()));
- mPassphraseEditAgain.setText(new String(mCreateKeyActivity.mPassphrase.getCharArray()));
+ mPassphraseEdit.setText(mCreateKeyActivity.mPassphrase.toStringUnsafe());
+ mPassphraseEditAgain.setText(mCreateKeyActivity.mPassphrase.toStringUnsafe());
}
mPassphraseEdit.requestFocus();
@@ -137,6 +130,35 @@ public class CreateKeyPassphraseFragment extends Fragment {
}
});
+ TextWatcher textWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ if (!isEditTextNotEmpty(getActivity(), mPassphraseEdit)) {
+ mPassphraseEditAgain.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
+ return;
+ }
+
+ if (areEditTextsEqual(mPassphraseEdit, mPassphraseEditAgain)) {
+ mPassphraseEditAgain.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_stat_retyped_ok, 0);
+ } else {
+ mPassphraseEditAgain.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_stat_retyped_bad, 0);
+ }
+
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ }
+ };
+
+ mPassphraseEdit.addTextChangedListener(textWatcher);
+ mPassphraseEditAgain.addTextChangedListener(textWatcher);
return view;
}
@@ -153,9 +175,15 @@ public class CreateKeyPassphraseFragment extends Fragment {
}
private void nextClicked() {
- if (isEditTextNotEmpty(getActivity(), mPassphraseEdit)
- && areEditTextsEqual(getActivity(), mPassphraseEdit, mPassphraseEditAgain)) {
+ if (isEditTextNotEmpty(getActivity(), mPassphraseEdit)) {
+
+ if (!areEditTextsEqual(mPassphraseEdit, mPassphraseEditAgain)) {
+ mPassphraseEditAgain.setError(getActivity().getApplicationContext().getString(R.string.create_key_passphrases_not_equal));
+ mPassphraseEditAgain.requestFocus();
+ return;
+ }
+ mPassphraseEditAgain.setError(null);
// save state
mCreateKeyActivity.mPassphrase = new Passphrase(mPassphraseEdit);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java
index 1a844e6e4..68ec0e8c8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java
@@ -81,7 +81,7 @@ public class CreateKeyStartFragment extends Fragment {
mYubiKey.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- CreateKeyYubiKeyWaitFragment frag = new CreateKeyYubiKeyWaitFragment();
+ CreateYubiKeyWaitFragment frag = new CreateYubiKeyWaitFragment();
mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
}
});
@@ -98,17 +98,10 @@ public class CreateKeyStartFragment extends Fragment {
mSkipOrCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- if (mCreateKeyActivity.mFirstTime) {
- Preferences prefs = Preferences.getPreferences(mCreateKeyActivity);
- prefs.setFirstTime(false);
- Intent intent = new Intent(mCreateKeyActivity, MainActivity.class);
- startActivity(intent);
- mCreateKeyActivity.finish();
- } else {
- // just finish activity and return data
+ if (!mCreateKeyActivity.mFirstTime) {
mCreateKeyActivity.setResult(Activity.RESULT_CANCELED);
- mCreateKeyActivity.finish();
}
+ mCreateKeyActivity.finish();
}
});
@@ -124,9 +117,6 @@ public class CreateKeyStartFragment extends Fragment {
if (mCreateKeyActivity.mFirstTime) {
Preferences prefs = Preferences.getPreferences(mCreateKeyActivity);
prefs.setFirstTime(false);
- Intent intent = new Intent(mCreateKeyActivity, MainActivity.class);
- intent.putExtras(data);
- startActivity(intent);
mCreateKeyActivity.finish();
} else {
// just finish activity and return data
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyBlankFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyBlankFragment.java
new file mode 100644
index 000000000..5b13dc88e
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyBlankFragment.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
+
+public class CreateYubiKeyBlankFragment extends Fragment {
+
+ CreateKeyActivity mCreateKeyActivity;
+ View mBackButton;
+ View mNextButton;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static CreateYubiKeyBlankFragment newInstance() {
+ CreateYubiKeyBlankFragment frag = new CreateYubiKeyBlankFragment();
+
+ Bundle args = new Bundle();
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.create_yubi_key_blank_fragment, container, false);
+
+ mBackButton = view.findViewById(R.id.create_key_back_button);
+ mNextButton = view.findViewById(R.id.create_key_next_button);
+
+ mBackButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (getFragmentManager().getBackStackEntryCount() == 0) {
+ getActivity().setResult(Activity.RESULT_CANCELED);
+ getActivity().finish();
+ } else {
+ mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
+ }
+ }
+ });
+ mNextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ nextClicked();
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mCreateKeyActivity = (CreateKeyActivity) getActivity();
+ }
+
+ private void nextClicked() {
+ mCreateKeyActivity.mCreateYubiKey = true;
+
+ CreateKeyNameFragment frag = CreateKeyNameFragment.newInstance();
+ mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyImportFragment.java
index f8d79d33b..d88e6b9f9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyImportFragment.java
@@ -17,15 +17,14 @@
package org.sufficientlysecure.keychain.ui;
+
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import android.app.Activity;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
@@ -36,19 +35,19 @@ import android.widget.TextView;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.NfcListenerFragment;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Preferences;
-public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListenerFragment {
+public class CreateYubiKeyImportFragment
+ extends QueueingCryptoOperationFragment<ImportKeyringParcel, ImportKeyResult>
+ implements NfcListenerFragment {
private static final String ARG_FINGERPRINT = "fingerprint";
public static final String ARG_AID = "aid";
@@ -57,7 +56,6 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
CreateKeyActivity mCreateKeyActivity;
private byte[] mNfcFingerprints;
- private long mNfcMasterKeyId;
private byte[] mNfcAid;
private String mNfcUserId;
private String mNfcFingerprint;
@@ -65,9 +63,13 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
private TextView vSerNo;
private TextView vUserId;
- public static Fragment createInstance(byte[] scannedFingerprints, byte[] nfcAid, String userId) {
+ // for CryptoOperationFragment key import
+ private String mKeyserver;
+ private ArrayList<ParcelableKeyRing> mKeyList;
+
+ public static Fragment newInstance(byte[] scannedFingerprints, byte[] nfcAid, String userId) {
- CreateKeyYubiKeyImportFragment frag = new CreateKeyYubiKeyImportFragment();
+ CreateYubiKeyImportFragment frag = new CreateYubiKeyImportFragment();
Bundle args = new Bundle();
args.putByteArray(ARG_FINGERPRINT, scannedFingerprints);
@@ -88,14 +90,15 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
mNfcAid = args.getByteArray(ARG_AID);
mNfcUserId = args.getString(ARG_USER_ID);
- mNfcMasterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints);
- mNfcFingerprint = KeyFormattingUtils.convertFingerprintToHex(mNfcFingerprints);
+ byte[] fp = new byte[20];
+ ByteBuffer.wrap(fp).put(mNfcFingerprints, 0, 20);
+ mNfcFingerprint = KeyFormattingUtils.convertFingerprintToHex(fp);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.create_yubikey_import_fragment, container, false);
+ View view = inflater.inflate(R.layout.create_yubi_key_import_fragment, container, false);
vSerNo = (TextView) view.findViewById(R.id.yubikey_serno);
vUserId = (TextView) view.findViewById(R.id.yubikey_userid);
@@ -164,7 +167,7 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
if (!mNfcUserId.isEmpty()) {
vUserId.setText(getString(R.string.yubikey_key_holder, mNfcUserId));
} else {
- vUserId.setText(getString(R.string.yubikey_key_holder_unset));
+ vUserId.setText(getString(R.string.yubikey_key_holder_not_set));
}
}
@@ -175,94 +178,68 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
public void importKey() {
- // Message is received after decrypting is done in KeychainIntentService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT
- ) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- ImportKeyResult result =
- returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
-
- long[] masterKeyIds = result.getImportedMasterKeyIds();
-
- // TODO handle masterKeyIds.length != 1...? sorta outlandish scenario
-
- if (!result.success() || masterKeyIds.length == 0) {
- result.createNotify(getActivity()).show();
- return;
- }
-
- Intent intent = new Intent(getActivity(), ViewKeyActivity.class);
- // use the imported masterKeyId, not the one from the yubikey, because
- // that one might* just have been a subkey of the imported key
- intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyIds[0]));
- intent.putExtra(ViewKeyActivity.EXTRA_DISPLAY_RESULT, result);
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid);
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints);
- startActivity(intent);
- getActivity().finish();
-
- }
-
- }
- };
-
- // Send all information needed to service to decrypt in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING);
-
- String hexFp = KeyFormattingUtils.convertFingerprintToHex(mNfcFingerprints);
ArrayList<ParcelableKeyRing> keyList = new ArrayList<>();
- keyList.add(new ParcelableKeyRing(hexFp, null, null));
- data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, keyList);
+ keyList.add(new ParcelableKeyRing(mNfcFingerprint, null, null));
+ mKeyList = keyList;
{
Preferences prefs = Preferences.getPreferences(getActivity());
Preferences.CloudSearchPrefs cloudPrefs =
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
- data.putString(KeychainIntentService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
+ mKeyserver = cloudPrefs.keyserver;
}
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- saveHandler.showProgressDialog(getActivity());
+ super.setProgressMessageResource(R.string.progress_importing);
- // start service with intent
- getActivity().startService(intent);
+ super.cryptoOperation();
}
@Override
- public void onNfcPerform() throws IOException {
+ public void doNfcInBackground() throws IOException {
mNfcFingerprints = mCreateKeyActivity.nfcGetFingerprints();
mNfcAid = mCreateKeyActivity.nfcGetAid();
mNfcUserId = mCreateKeyActivity.nfcGetUserId();
- mNfcMasterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints);
- mNfcFingerprint = KeyFormattingUtils.convertFingerprintToHex(mNfcFingerprints);
+ byte[] fp = new byte[20];
+ ByteBuffer.wrap(fp).put(mNfcFingerprints, 0, 20);
+ mNfcFingerprint = KeyFormattingUtils.convertFingerprintToHex(fp);
+ }
+
+ @Override
+ public void onNfcPostExecute() throws IOException {
setData();
+
refreshSearch();
+ }
+
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(mKeyList, mKeyserver);
+ }
+
+ @Override
+ public void onQueuedOperationSuccess(ImportKeyResult result) {
+ long[] masterKeyIds = result.getImportedMasterKeyIds();
+ if (masterKeyIds.length == 0) {
+ super.onCryptoOperationError(result);
+ return;
+ }
+ // null-protected from Queueing*Fragment
+ Activity activity = getActivity();
+
+ Intent intent = new Intent(activity, ViewKeyActivity.class);
+ // use the imported masterKeyId, not the one from the yubikey, because
+ // that one might* just have been a subkey of the imported key
+ intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyIds[0]));
+ intent.putExtra(ViewKeyActivity.EXTRA_DISPLAY_RESULT, result);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints);
+ startActivity(intent);
+ activity.finish();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinFragment.java
new file mode 100644
index 000000000..a793b31f2
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinFragment.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Activity;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
+import org.sufficientlysecure.keychain.util.Passphrase;
+
+import java.security.SecureRandom;
+
+public class CreateYubiKeyPinFragment extends Fragment {
+
+ // view
+ CreateKeyActivity mCreateKeyActivity;
+ TextView mPin;
+ TextView mAdminPin;
+ View mBackButton;
+ View mNextButton;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static CreateYubiKeyPinFragment newInstance() {
+ CreateYubiKeyPinFragment frag = new CreateYubiKeyPinFragment();
+
+ Bundle args = new Bundle();
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.create_yubi_key_pin_fragment, container, false);
+
+ mPin = (TextView) view.findViewById(R.id.create_yubi_key_pin);
+ mAdminPin = (TextView) view.findViewById(R.id.create_yubi_key_admin_pin);
+ mBackButton = view.findViewById(R.id.create_key_back_button);
+ mNextButton = view.findViewById(R.id.create_key_next_button);
+
+ if (mCreateKeyActivity.mYubiKeyPin == null) {
+ new AsyncTask<Void, Void, Pair<Passphrase, Passphrase>>() {
+ @Override
+ protected Pair<Passphrase, Passphrase> doInBackground(Void... unused) {
+ SecureRandom secureRandom = new SecureRandom();
+ // min = 6, we choose 6
+ String pin = "" + secureRandom.nextInt(9)
+ + secureRandom.nextInt(9)
+ + secureRandom.nextInt(9)
+ + secureRandom.nextInt(9)
+ + secureRandom.nextInt(9)
+ + secureRandom.nextInt(9);
+ // min = 8, we choose 10, but 6 are equals the PIN
+ String adminPin = pin + secureRandom.nextInt(9)
+ + secureRandom.nextInt(9)
+ + secureRandom.nextInt(9)
+ + secureRandom.nextInt(9);
+
+ return new Pair<>(new Passphrase(pin), new Passphrase(adminPin));
+ }
+
+ @Override
+ protected void onPostExecute(Pair<Passphrase, Passphrase> pair) {
+ mCreateKeyActivity.mYubiKeyPin = pair.first;
+ mCreateKeyActivity.mYubiKeyAdminPin = pair.second;
+
+ mPin.setText(mCreateKeyActivity.mYubiKeyPin.toStringUnsafe());
+ mAdminPin.setText(mCreateKeyActivity.mYubiKeyAdminPin.toStringUnsafe());
+ }
+ }.execute();
+ } else {
+ mPin.setText(mCreateKeyActivity.mYubiKeyPin.toStringUnsafe());
+ mAdminPin.setText(mCreateKeyActivity.mYubiKeyAdminPin.toStringUnsafe());
+ }
+
+ mBackButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ back();
+ }
+ });
+ mNextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ nextClicked();
+ }
+ });
+
+
+ return view;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mCreateKeyActivity = (CreateKeyActivity) getActivity();
+ }
+
+
+ private void nextClicked() {
+ CreateYubiKeyPinRepeatFragment frag = CreateYubiKeyPinRepeatFragment.newInstance();
+ mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
+ }
+
+ private void back() {
+ mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinRepeatFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinRepeatFragment.java
new file mode 100644
index 000000000..2e752e609
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinRepeatFragment.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
+
+public class CreateYubiKeyPinRepeatFragment extends Fragment {
+
+ // view
+ CreateKeyActivity mCreateKeyActivity;
+ EditText mPin;
+ EditText mAdminPin;
+ View mBackButton;
+ View mNextButton;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static CreateYubiKeyPinRepeatFragment newInstance() {
+ CreateYubiKeyPinRepeatFragment frag = new CreateYubiKeyPinRepeatFragment();
+
+ Bundle args = new Bundle();
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ /**
+ * Checks if text of given EditText is not empty. If it is empty an error is
+ * set and the EditText gets the focus.
+ *
+ * @param context
+ * @param editText
+ * @return true if EditText is not empty
+ */
+ private static boolean isEditTextNotEmpty(Context context, EditText editText) {
+ boolean output = true;
+ if (editText.getText().length() == 0) {
+ editText.setError(context.getString(R.string.create_key_empty));
+ editText.requestFocus();
+ output = false;
+ } else {
+ editText.setError(null);
+ }
+
+ return output;
+ }
+
+ private static boolean checkPin(Context context, EditText editText1, String pin) {
+ boolean output = editText1.getText().toString().equals(pin);
+
+ if (!output) {
+ editText1.setError(context.getString(R.string.create_key_yubi_key_pin_not_correct));
+ editText1.requestFocus();
+ } else {
+ editText1.setError(null);
+ }
+
+ return output;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.create_yubi_key_pin_repeat_fragment, container, false);
+
+ mPin = (EditText) view.findViewById(R.id.create_yubi_key_pin_repeat);
+ mAdminPin = (EditText) view.findViewById(R.id.create_yubi_key_admin_pin_repeat);
+ mBackButton = view.findViewById(R.id.create_key_back_button);
+ mNextButton = view.findViewById(R.id.create_key_next_button);
+
+ mPin.requestFocus();
+ mBackButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ back();
+ }
+ });
+ mNextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ nextClicked();
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mCreateKeyActivity = (CreateKeyActivity) getActivity();
+ }
+
+ private void back() {
+ hideKeyboard();
+ mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
+ }
+
+ private void nextClicked() {
+ if (isEditTextNotEmpty(getActivity(), mPin)
+ && checkPin(getActivity(), mPin, mCreateKeyActivity.mYubiKeyPin.toStringUnsafe())
+ && isEditTextNotEmpty(getActivity(), mAdminPin)
+ && checkPin(getActivity(), mAdminPin, mCreateKeyActivity.mYubiKeyAdminPin.toStringUnsafe())) {
+
+ CreateKeyFinalFragment frag = CreateKeyFinalFragment.newInstance();
+ hideKeyboard();
+ mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
+ }
+ }
+
+ private void hideKeyboard() {
+ if (getActivity() == null) {
+ return;
+ }
+ InputMethodManager inputManager = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ // check if no view has focus
+ View v = getActivity().getCurrentFocus();
+ if (v == null)
+ return;
+
+ inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyWaitFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyWaitFragment.java
index 0b8586c0a..d45195512 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyWaitFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyWaitFragment.java
@@ -28,14 +28,14 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
-public class CreateKeyYubiKeyWaitFragment extends Fragment {
+public class CreateYubiKeyWaitFragment extends Fragment {
CreateKeyActivity mCreateKeyActivity;
View mBackButton;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.create_yubikey_wait_fragment, container, false);
+ View view = inflater.inflate(R.layout.create_yubi_key_wait_fragment, container, false);
mBackButton = view.findViewById(R.id.create_key_back_button);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
new file mode 100644
index 000000000..881190ae2
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.widget.Toast;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.intents.OpenKeychainIntents;
+import org.sufficientlysecure.keychain.pgp.PgpHelper;
+import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
+import org.sufficientlysecure.keychain.ui.base.BaseActivity;
+
+
+public class DecryptActivity extends BaseActivity {
+
+ /* Intents */
+ public static final String ACTION_DECRYPT_FROM_CLIPBOARD = "DECRYPT_DATA_CLIPBOARD";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setFullScreenDialogClose(Activity.RESULT_CANCELED, false);
+
+ // Handle intent actions
+ handleActions(savedInstanceState, getIntent());
+ }
+
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.decrypt_files_activity);
+ }
+
+ /**
+ * Handles all actions with this intent
+ */
+ private void handleActions(Bundle savedInstanceState, Intent intent) {
+
+ // No need to initialize fragments if we are just being restored
+ if (savedInstanceState != null) {
+ return;
+ }
+
+ ArrayList<Uri> uris = new ArrayList<>();
+
+ String action = intent.getAction();
+
+ if (action == null) {
+ Toast.makeText(this, "Error: No action specified!", Toast.LENGTH_LONG).show();
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ try {
+
+ switch (action) {
+ case Intent.ACTION_SEND: {
+ // When sending to Keychain Decrypt via share menu
+ // Binary via content provider (could also be files)
+ // override uri to get stream from send
+ if (intent.hasExtra(Intent.EXTRA_STREAM)) {
+ uris.add(intent.<Uri>getParcelableExtra(Intent.EXTRA_STREAM));
+ } else if (intent.hasExtra(Intent.EXTRA_TEXT)) {
+ String text = intent.getStringExtra(Intent.EXTRA_TEXT);
+ Uri uri = readToTempFile(text);
+ if (uri != null) {
+ uris.add(uri);
+ }
+ }
+
+ break;
+ }
+
+ case Intent.ACTION_SEND_MULTIPLE: {
+ if (intent.hasExtra(Intent.EXTRA_STREAM)) {
+ uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
+ } else if (intent.hasExtra(Intent.EXTRA_TEXT)) {
+ for (String text : intent.getStringArrayListExtra(Intent.EXTRA_TEXT)) {
+ Uri uri = readToTempFile(text);
+ if (uri != null) {
+ uris.add(uri);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case ACTION_DECRYPT_FROM_CLIPBOARD: {
+ ClipboardManager clipMan = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
+ if (clipMan == null) {
+ break;
+ }
+
+ ClipData clip = clipMan.getPrimaryClip();
+ if (clip == null) {
+ break;
+ }
+
+ // check if data is available as uri
+ Uri uri = null;
+ for (int i = 0; i < clip.getItemCount(); i++) {
+ ClipData.Item item = clip.getItemAt(i);
+ Uri itemUri = item.getUri();
+ if (itemUri != null) {
+ uri = itemUri;
+ break;
+ }
+ }
+
+ // otherwise, coerce to text (almost always possible) and work from there
+ if (uri == null) {
+ String text = clip.getItemAt(0).coerceToText(this).toString();
+ uri = readToTempFile(text);
+ }
+ if (uri != null) {
+ uris.add(uri);
+ }
+
+ break;
+ }
+
+ // for everything else, just work on the intent data
+ case OpenKeychainIntents.DECRYPT_DATA:
+ case Intent.ACTION_VIEW:
+ default:
+ uris.add(intent.getData());
+
+ }
+
+ } catch (IOException e) {
+ Toast.makeText(this, R.string.error_reading_text, Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+
+ // Definitely need a data uri with the decrypt_data intent
+ if (uris.isEmpty()) {
+ Toast.makeText(this, "No data to decrypt!", Toast.LENGTH_LONG).show();
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ displayListFragment(uris);
+
+ }
+
+ @Nullable public Uri readToTempFile(String text) throws IOException {
+ Uri tempFile = TemporaryStorageProvider.createFile(this);
+ OutputStream outStream = getContentResolver().openOutputStream(tempFile);
+
+ // clean up ascii armored message, fixing newlines and stuff
+ String cleanedText = PgpHelper.getPgpContent(text);
+ if (cleanedText == null) {
+ return null;
+ }
+
+ // if cleanup didn't work, just try the raw data
+ outStream.write(text.getBytes());
+ outStream.close();
+ return tempFile;
+ }
+
+ public void displayListFragment(ArrayList<Uri> inputUris) {
+
+ DecryptListFragment frag = DecryptListFragment.newInstance(inputUris);
+
+ FragmentManager fragMan = getSupportFragmentManager();
+
+ FragmentTransaction trans = fragMan.beginTransaction();
+ trans.replace(R.id.decrypt_files_fragment_container, frag);
+
+ // if there already is a fragment, allow going back to that. otherwise, we're top level!
+ if (fragMan.getFragments() != null && !fragMan.getFragments().isEmpty()) {
+ trans.addToBackStack("list");
+ }
+
+ trans.commit();
+
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java
deleted file mode 100644
index c9a590c5b..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesActivity.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.View;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.intents.OpenKeychainIntents;
-import org.sufficientlysecure.keychain.ui.base.BaseActivity;
-import org.sufficientlysecure.keychain.util.Log;
-
-public class DecryptFilesActivity extends BaseActivity {
-
- /* Intents */
- public static final String ACTION_DECRYPT_DATA = OpenKeychainIntents.DECRYPT_DATA;
-
- // intern
- public static final String ACTION_DECRYPT_DATA_OPEN = Constants.INTENT_PREFIX + "DECRYPT_DATA_OPEN";
-
- DecryptFilesFragment mFragment;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setFullScreenDialogClose(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- setResult(Activity.RESULT_CANCELED);
- finish();
- }
- }, false);
-
- // Handle intent actions
- handleActions(savedInstanceState, getIntent());
- }
-
- @Override
- protected void initLayout() {
- setContentView(R.layout.decrypt_files_activity);
- }
-
- /**
- * Handles all actions with this intent
- *
- * @param intent
- */
- private void handleActions(Bundle savedInstanceState, Intent intent) {
- String action = intent.getAction();
- String type = intent.getType();
- Uri uri = intent.getData();
-
- Bundle mFileFragmentBundle = new Bundle();
-
- /*
- * Android's Action
- */
- if (Intent.ACTION_SEND.equals(action) && type != null) {
- // When sending to Keychain Decrypt via share menu
- // Binary via content provider (could also be files)
- // override uri to get stream from send
- uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
- action = ACTION_DECRYPT_DATA;
- } else if (Intent.ACTION_VIEW.equals(action)) {
- // Android's Action when opening file associated to Keychain (see AndroidManifest.xml)
-
- // override action
- action = ACTION_DECRYPT_DATA;
- }
-
- /**
- * Main Actions
- */
- if (ACTION_DECRYPT_DATA.equals(action) && uri != null) {
- mFileFragmentBundle.putParcelable(DecryptFilesFragment.ARG_URI, uri);
-
- loadFragment(savedInstanceState, uri, false);
- } else if (ACTION_DECRYPT_DATA_OPEN.equals(action)) {
- loadFragment(savedInstanceState, null, true);
- } else if (ACTION_DECRYPT_DATA.equals(action)) {
- Log.e(Constants.TAG,
- "Include an Uri with setInputData() in your Intent!");
- }
- }
-
- private void loadFragment(Bundle savedInstanceState, Uri uri, boolean openDialog) {
- // 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
- mFragment = DecryptFilesFragment.newInstance(uri, openDialog);
-
- // Add the fragment to the 'fragment_container' FrameLayout
- // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
- getSupportFragmentManager().beginTransaction()
- .replace(R.id.decrypt_files_fragment_container, mFragment)
- .commitAllowingStateLoss();
- // do it immediately!
- getSupportFragmentManager().executePendingTransactions();
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java
deleted file mode 100644
index 234362edc..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * 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.annotation.SuppressLint;
-import android.app.Activity;
-import android.app.ProgressDialog;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CheckBox;
-import android.widget.TextView;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentService.IOType;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
-import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
-import org.sufficientlysecure.keychain.ui.util.Notify;
-import org.sufficientlysecure.keychain.util.FileHelper;
-import org.sufficientlysecure.keychain.util.Log;
-
-import java.io.File;
-
-public class DecryptFilesFragment extends DecryptFragment {
- public static final String ARG_URI = "uri";
- public static final String ARG_OPEN_DIRECTLY = "open_directly";
-
- private static final int REQUEST_CODE_INPUT = 0x00007003;
- private static final int REQUEST_CODE_OUTPUT = 0x00007007;
-
- // view
- private TextView mFilename;
- private CheckBox mDeleteAfter;
- private View mDecryptButton;
-
- // model
- private Uri mInputUri = null;
- private Uri mOutputUri = null;
-
- private String mCurrentCryptoOperation;
-
- /**
- * Creates new instance of this fragment
- */
- public static DecryptFilesFragment newInstance(Uri uri, boolean openDirectly) {
- DecryptFilesFragment frag = new DecryptFilesFragment();
-
- Bundle args = new Bundle();
- args.putParcelable(ARG_URI, uri);
- args.putBoolean(ARG_OPEN_DIRECTLY, openDirectly);
-
- frag.setArguments(args);
-
- return frag;
- }
-
- /**
- * Inflate the layout for this fragment
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.decrypt_files_fragment, container, false);
-
- mFilename = (TextView) view.findViewById(R.id.decrypt_files_filename);
- mDeleteAfter = (CheckBox) view.findViewById(R.id.decrypt_files_delete_after_decryption);
- mDecryptButton = view.findViewById(R.id.decrypt_files_action_decrypt);
- view.findViewById(R.id.decrypt_files_browse).setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- FileHelper.openDocument(DecryptFilesFragment.this, "*/*", REQUEST_CODE_INPUT);
- } else {
- FileHelper.openFile(DecryptFilesFragment.this, mInputUri, "*/*",
- REQUEST_CODE_INPUT);
- }
- }
- });
- mDecryptButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- decryptAction();
- }
- });
-
- return view;
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- setInputUri(getArguments().<Uri>getParcelable(ARG_URI));
-
- if (getArguments().getBoolean(ARG_OPEN_DIRECTLY, false)) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- FileHelper.openDocument(DecryptFilesFragment.this, "*/*", REQUEST_CODE_INPUT);
- } else {
- FileHelper.openFile(DecryptFilesFragment.this, mInputUri, "*/*",
- REQUEST_CODE_INPUT);
- }
- }
- }
-
- private void setInputUri(Uri inputUri) {
- if (inputUri == null) {
- mInputUri = null;
- mFilename.setText("");
- return;
- }
-
- mInputUri = inputUri;
- mFilename.setText(FileHelper.getFilename(getActivity(), mInputUri));
- }
-
- private void decryptAction() {
- if (mInputUri == null) {
- Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR).show();
- return;
- }
-
- startDecryptFilenames();
- }
-
- private String removeEncryptedAppend(String name) {
- if (name.endsWith(Constants.FILE_EXTENSION_ASC)
- || name.endsWith(Constants.FILE_EXTENSION_PGP_MAIN)
- || name.endsWith(Constants.FILE_EXTENSION_PGP_ALTERNATE)) {
- return name.substring(0, name.length() - 4);
- }
- return name;
- }
-
- private void askForOutputFilename(String originalFilename) {
- if (TextUtils.isEmpty(originalFilename)) {
- originalFilename = removeEncryptedAppend(FileHelper.getFilename(getActivity(), mInputUri));
- }
-
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
- File file = new File(mInputUri.getPath());
- File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR;
- File targetFile = new File(parentDir, originalFilename);
- FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file),
- getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT);
- } else {
- FileHelper.saveDocument(this, "*/*", originalFilename, REQUEST_CODE_OUTPUT);
- }
- }
-
- private void startDecrypt() {
- mCurrentCryptoOperation = KeychainIntentService.ACTION_DECRYPT_VERIFY;
- cryptoOperation(new CryptoInputParcel());
- }
-
- private void startDecryptFilenames() {
- mCurrentCryptoOperation = KeychainIntentService.ACTION_DECRYPT_METADATA;
- cryptoOperation(new CryptoInputParcel());
- }
-
- @Override
- @SuppressLint("HandlerLeak")
- protected void cryptoOperation(CryptoInputParcel cryptoInput) {
- // Send all information needed to service to decrypt in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
-
- // fill values for this action
- Bundle data = new Bundle();
- // use current operation, either decrypt metadata or decrypt payload
- intent.setAction(mCurrentCryptoOperation);
-
- // data
- data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput);
-
- Log.d(Constants.TAG, "mInputUri=" + mInputUri + ", mOutputUri=" + mOutputUri);
-
- data.putInt(KeychainIntentService.SOURCE, IOType.URI.ordinal());
- data.putParcelable(KeychainIntentService.ENCRYPT_DECRYPT_INPUT_URI, mInputUri);
-
- data.putInt(KeychainIntentService.TARGET, IOType.URI.ordinal());
- data.putParcelable(KeychainIntentService.ENCRYPT_DECRYPT_OUTPUT_URI, mOutputUri);
-
- data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput);
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after decrypting is done in KeychainIntentService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_decrypting),
- ProgressDialog.STYLE_HORIZONTAL,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- // handle pending messages
- if (handlePendingMessage(message)) {
- return;
- }
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- DecryptVerifyResult pgpResult =
- returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
-
- if (pgpResult.success()) {
- switch (mCurrentCryptoOperation) {
- case KeychainIntentService.ACTION_DECRYPT_METADATA: {
- askForOutputFilename(pgpResult.getDecryptMetadata().getFilename());
- break;
- }
- case KeychainIntentService.ACTION_DECRYPT_VERIFY: {
- // display signature result in activity
- loadVerifyResult(pgpResult);
-
- if (mDeleteAfter.isChecked()) {
- // Create and show dialog to delete original file
- DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment.newInstance(mInputUri);
- deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
- setInputUri(null);
- }
-
- /*
- // A future open after decryption feature
- if () {
- Intent viewFile = new Intent(Intent.ACTION_VIEW);
- viewFile.setInputData(mOutputUri);
- startActivity(viewFile);
- }
- */
- break;
- }
- default: {
- Log.e(Constants.TAG, "Bug: not supported operation!");
- break;
- }
- }
- }
- pgpResult.createNotify(getActivity()).show(DecryptFilesFragment.this);
- }
-
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(getActivity());
-
- // start service with intent
- getActivity().startService(intent);
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case REQUEST_CODE_INPUT: {
- if (resultCode == Activity.RESULT_OK && data != null) {
- setInputUri(data.getData());
- }
- return;
- }
-
- case REQUEST_CODE_OUTPUT: {
- // This happens after output file was selected, so start our operation
- if (resultCode == Activity.RESULT_OK && data != null) {
- mOutputUri = data.getData();
- startDecrypt();
- }
- return;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
- }
- }
- }
-
- @Override
- protected void onVerifyLoaded(boolean hideErrorOverlay) {
-
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
index e9bc42a4d..37dd6afad 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
@@ -23,43 +23,43 @@ import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
+import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.View;
+import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import android.widget.ViewAnimator;
+import org.openintents.openpgp.OpenPgpDecryptionResult;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.Preferences;
-public abstract class DecryptFragment extends CryptoOperationFragment implements
- LoaderManager.LoaderCallbacks<Cursor> {
+public abstract class DecryptFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
public static final int LOADER_ID_UNIFIED = 0;
+ public static final String ARG_DECRYPT_VERIFY_RESULT = "decrypt_verify_result";
protected LinearLayout mResultLayout;
protected ImageView mEncryptionIcon;
@@ -71,10 +71,11 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements
protected TextView mSignatureEmail;
protected TextView mSignatureAction;
- private LinearLayout mContentLayout;
- private LinearLayout mErrorOverlayLayout;
-
private OpenPgpSignatureResult mSignatureResult;
+ private DecryptVerifyResult mDecryptVerifyResult;
+ private ViewAnimator mOverlayAnimator;
+
+ private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
@@ -93,53 +94,55 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements
mSignatureAction = (TextView) getActivity().findViewById(R.id.result_signature_action);
// Overlay
- mContentLayout = (LinearLayout) view.findViewById(R.id.decrypt_content);
- mErrorOverlayLayout = (LinearLayout) view.findViewById(R.id.decrypt_error_overlay);
+ mOverlayAnimator = (ViewAnimator) view;
Button vErrorOverlayButton = (Button) view.findViewById(R.id.decrypt_error_overlay_button);
- vErrorOverlayButton.setOnClickListener(new View.OnClickListener() {
+ vErrorOverlayButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- mErrorOverlayLayout.setVisibility(View.GONE);
- mContentLayout.setVisibility(View.VISIBLE);
+ mOverlayAnimator.setDisplayedChild(0);
}
});
}
- private void lookupUnknownKey(long unknownKeyId) {
+ private void showErrorOverlay(boolean overlay) {
+ int child = overlay ? 1 : 0;
+ if (mOverlayAnimator.getDisplayedChild() != child) {
+ mOverlayAnimator.setDisplayedChild(child);
+ }
+ }
- // Message is received after importing is done in KeychainIntentService
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(getActivity()) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
+ outState.putParcelable(ARG_DECRYPT_VERIFY_RESULT, mDecryptVerifyResult);
+ }
- if (returnData == null) {
- return;
- }
+ @Override
+ public void onViewStateRestored(Bundle savedInstanceState) {
+ super.onViewStateRestored(savedInstanceState);
- final ImportKeyResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
+ if (savedInstanceState == null) {
+ return;
+ }
- result.createNotify(getActivity()).show();
+ DecryptVerifyResult result = savedInstanceState.getParcelable(ARG_DECRYPT_VERIFY_RESULT);
+ if (result != null) {
+ loadVerifyResult(result);
+ }
+ }
- getLoaderManager().restartLoader(LOADER_ID_UNIFIED, null, DecryptFragment.this);
- }
- }
- };
+ private void lookupUnknownKey(long unknownKeyId) {
- // fill values for this action
- Bundle data = new Bundle();
+ final ArrayList<ParcelableKeyRing> keyList;
+ final String keyserver;
// search config
{
Preferences prefs = Preferences.getPreferences(getActivity());
Preferences.CloudSearchPrefs cloudPrefs =
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
- data.putString(KeychainIntentService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
+ keyserver = cloudPrefs.keyserver;
}
{
@@ -148,19 +151,43 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements
ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
selectedEntries.add(keyEntry);
- data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, selectedEntries);
+ keyList = selectedEntries;
}
- // Send all information needed to service to query keys in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+ CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> callback
+ = new CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult>() {
+
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(keyList, keyserver);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ImportKeyResult result) {
+ result.createNotify(getActivity()).show();
+
+ getLoaderManager().restartLoader(LOADER_ID_UNIFIED, null, DecryptFragment.this);
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+ // do nothing
+ }
+
+ @Override
+ public void onCryptoOperationError(ImportKeyResult result) {
+ result.createNotify(getActivity()).show();
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
+ };
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+ mImportOpHelper = new CryptoOperationHelper<>(1, this, callback, R.string.progress_importing);
- getActivity().startService(intent);
+ mImportOpHelper.cryptoOperation();
}
private void showKey(long keyId) {
@@ -178,43 +205,54 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements
}
}
- /**
- * @return returns false if signature is invalid, key is revoked or expired.
- */
protected void loadVerifyResult(DecryptVerifyResult decryptVerifyResult) {
+ mDecryptVerifyResult = decryptVerifyResult;
mSignatureResult = decryptVerifyResult.getSignatureResult();
+ OpenPgpDecryptionResult decryptionResult = decryptVerifyResult.getDecryptionResult();
+
mResultLayout.setVisibility(View.VISIBLE);
- // unsigned data
- if (mSignatureResult == null) {
+ switch (decryptionResult.getResult()) {
+ case OpenPgpDecryptionResult.RESULT_ENCRYPTED: {
+ mEncryptionText.setText(R.string.decrypt_result_encrypted);
+ KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.ENCRYPTED);
+ break;
+ }
+
+ case OpenPgpDecryptionResult.RESULT_INSECURE: {
+ mEncryptionText.setText(R.string.decrypt_result_insecure);
+ KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.INSECURE);
+ break;
+ }
+
+ default:
+ case OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED: {
+ mEncryptionText.setText(R.string.decrypt_result_not_encrypted);
+ KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.NOT_ENCRYPTED);
+ break;
+ }
+ }
+
+ if (mSignatureResult.getResult() == OpenPgpSignatureResult.RESULT_NO_SIGNATURE) {
+ // no signature
setSignatureLayoutVisibility(View.GONE);
mSignatureText.setText(R.string.decrypt_result_no_signature);
KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.NOT_SIGNED);
- mEncryptionText.setText(R.string.decrypt_result_encrypted);
- KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.ENCRYPTED);
getLoaderManager().destroyLoader(LOADER_ID_UNIFIED);
- mErrorOverlayLayout.setVisibility(View.GONE);
- mContentLayout.setVisibility(View.VISIBLE);
+ showErrorOverlay(false);
onVerifyLoaded(true);
-
- return;
- }
-
- if (mSignatureResult.isSignatureOnly()) {
- mEncryptionText.setText(R.string.decrypt_result_not_encrypted);
- KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.NOT_ENCRYPTED);
} else {
- mEncryptionText.setText(R.string.decrypt_result_encrypted);
- KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.ENCRYPTED);
- }
+ // signature present
- getLoaderManager().restartLoader(LOADER_ID_UNIFIED, null, this);
+ // after loader is restarted signature results are checked
+ getLoaderManager().restartLoader(LOADER_ID_UNIFIED, null, this);
+ }
}
private void setSignatureLayoutVisibility(int visibility) {
@@ -289,8 +327,9 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements
// NOTE: Don't use revoked and expired fields from database, they don't show
// revoked/expired subkeys
- boolean isRevoked = mSignatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED;
- boolean isExpired = mSignatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED;
+ boolean isRevoked = mSignatureResult.getResult() == OpenPgpSignatureResult.RESULT_INVALID_KEY_REVOKED;
+ boolean isExpired = mSignatureResult.getResult() == OpenPgpSignatureResult.RESULT_INVALID_KEY_EXPIRED;
+ boolean isInsecure = mSignatureResult.getResult() == OpenPgpSignatureResult.RESULT_INVALID_INSECURE;
boolean isVerified = data.getInt(INDEX_VERIFIED) > 0;
boolean isYours = data.getInt(INDEX_HAS_ANY_SECRET) != 0;
@@ -301,10 +340,7 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements
setSignatureLayoutVisibility(View.VISIBLE);
setShowAction(signatureKeyId);
- mErrorOverlayLayout.setVisibility(View.VISIBLE);
- mContentLayout.setVisibility(View.GONE);
-
- onVerifyLoaded(false);
+ onVerifyLoaded(true);
} else if (isExpired) {
mSignatureText.setText(R.string.decrypt_result_signature_expired_key);
@@ -313,21 +349,18 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements
setSignatureLayoutVisibility(View.VISIBLE);
setShowAction(signatureKeyId);
- mErrorOverlayLayout.setVisibility(View.GONE);
- mContentLayout.setVisibility(View.VISIBLE);
+ showErrorOverlay(false);
onVerifyLoaded(true);
- } else if (isYours) {
-
- mSignatureText.setText(R.string.decrypt_result_signature_secret);
- KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.VERIFIED);
+ } else if (isInsecure) {
+ mSignatureText.setText(R.string.decrypt_result_insecure_cryptography);
+ KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.INSECURE);
setSignatureLayoutVisibility(View.VISIBLE);
setShowAction(signatureKeyId);
- mErrorOverlayLayout.setVisibility(View.GONE);
- mContentLayout.setVisibility(View.VISIBLE);
+ showErrorOverlay(false);
onVerifyLoaded(true);
@@ -339,6 +372,8 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements
setSignatureLayoutVisibility(View.VISIBLE);
setShowAction(signatureKeyId);
+ showErrorOverlay(false);
+
onVerifyLoaded(true);
} else if (isVerified) {
@@ -348,8 +383,7 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements
setSignatureLayoutVisibility(View.VISIBLE);
setShowAction(signatureKeyId);
- mErrorOverlayLayout.setVisibility(View.GONE);
- mContentLayout.setVisibility(View.VISIBLE);
+ showErrorOverlay(false);
onVerifyLoaded(true);
@@ -360,8 +394,7 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements
setSignatureLayoutVisibility(View.VISIBLE);
setShowAction(signatureKeyId);
- mErrorOverlayLayout.setVisibility(View.GONE);
- mContentLayout.setVisibility(View.VISIBLE);
+ showErrorOverlay(false);
onVerifyLoaded(true);
}
@@ -382,9 +415,9 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements
final long signatureKeyId = mSignatureResult.getKeyId();
- int result = mSignatureResult.getStatus();
- if (result != OpenPgpSignatureResult.SIGNATURE_KEY_MISSING
- && result != OpenPgpSignatureResult.SIGNATURE_ERROR) {
+ int result = mSignatureResult.getResult();
+ if (result != OpenPgpSignatureResult.RESULT_KEY_MISSING
+ && result != OpenPgpSignatureResult.RESULT_INVALID_SIGNATURE) {
Log.e(Constants.TAG, "got missing status for non-missing key, shouldn't happen!");
}
@@ -402,9 +435,9 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements
getActivity(), mSignatureResult.getKeyId()));
}
- switch (mSignatureResult.getStatus()) {
+ switch (mSignatureResult.getResult()) {
- case OpenPgpSignatureResult.SIGNATURE_KEY_MISSING: {
+ case OpenPgpSignatureResult.RESULT_KEY_MISSING: {
mSignatureText.setText(R.string.decrypt_result_signature_missing_key);
KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.UNKNOWN_KEY);
@@ -419,22 +452,20 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements
}
});
- mErrorOverlayLayout.setVisibility(View.GONE);
- mContentLayout.setVisibility(View.VISIBLE);
+ showErrorOverlay(false);
onVerifyLoaded(true);
break;
}
- case OpenPgpSignatureResult.SIGNATURE_ERROR: {
+ case OpenPgpSignatureResult.RESULT_INVALID_SIGNATURE: {
mSignatureText.setText(R.string.decrypt_result_invalid_signature);
KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.INVALID);
setSignatureLayoutVisibility(View.GONE);
- mErrorOverlayLayout.setVisibility(View.VISIBLE);
- mContentLayout.setVisibility(View.GONE);
+ showErrorOverlay(true);
onVerifyLoaded(false);
break;
@@ -446,4 +477,11 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements
protected abstract void onVerifyLoaded(boolean hideErrorOverlay);
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mImportOpHelper != null) {
+ mImportOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java
new file mode 100644
index 000000000..26e56280a
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java
@@ -0,0 +1,945 @@
+/*
+ * 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 java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import android.app.Activity;
+import android.content.ClipDescription;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LabeledIntent;
+import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.v7.widget.DefaultItemAnimator;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.PopupMenu;
+import android.widget.PopupMenu.OnDismissListener;
+import android.widget.PopupMenu.OnMenuItemClickListener;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.ViewAnimator;
+
+import org.openintents.openpgp.OpenPgpMetadata;
+import org.openintents.openpgp.OpenPgpSignatureResult;
+import org.sufficientlysecure.keychain.BuildConfig;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
+// this import NEEDS to be above the ViewModel one, or it won't compile! (as of 06/06/15)
+import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.StatusHolder;
+import org.sufficientlysecure.keychain.ui.DecryptListFragment.DecryptFilesAdapter.ViewModel;
+import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration;
+import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
+import org.sufficientlysecure.keychain.util.FileHelper;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ParcelableHashMap;
+
+
+public class DecryptListFragment
+ extends QueueingCryptoOperationFragment<PgpDecryptVerifyInputParcel,DecryptVerifyResult>
+ implements OnMenuItemClickListener {
+
+ public static final String ARG_INPUT_URIS = "input_uris";
+ public static final String ARG_OUTPUT_URIS = "output_uris";
+ public static final String ARG_CANCELLED_URIS = "cancelled_uris";
+ public static final String ARG_RESULTS = "results";
+
+ private static final int REQUEST_CODE_OUTPUT = 0x00007007;
+ public static final String ARG_CURRENT_URI = "current_uri";
+
+ private ArrayList<Uri> mInputUris;
+ private HashMap<Uri, Uri> mOutputUris;
+ private ArrayList<Uri> mPendingInputUris;
+ private ArrayList<Uri> mCancelledInputUris;
+
+ private Uri mCurrentInputUri;
+
+ private DecryptFilesAdapter mAdapter;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static DecryptListFragment newInstance(ArrayList<Uri> uris) {
+ DecryptListFragment frag = new DecryptListFragment();
+
+ Bundle args = new Bundle();
+ args.putParcelableArrayList(ARG_INPUT_URIS, uris);
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ public DecryptListFragment() {
+ super(null);
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.decrypt_files_list_fragment, container, false);
+
+ RecyclerView vFilesList = (RecyclerView) view.findViewById(R.id.decrypted_files_list);
+
+ vFilesList.addItemDecoration(new SpacesItemDecoration(
+ FormattingUtils.dpToPx(getActivity(), 4)));
+ vFilesList.setHasFixedSize(true);
+ // TODO make this a grid, for tablets!
+ vFilesList.setLayoutManager(new LinearLayoutManager(getActivity()));
+ vFilesList.setItemAnimator(new DefaultItemAnimator());
+
+ mAdapter = new DecryptFilesAdapter(getActivity(), this);
+ vFilesList.setAdapter(mAdapter);
+
+ return view;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putParcelableArrayList(ARG_INPUT_URIS, mInputUris);
+
+ HashMap<Uri,DecryptVerifyResult> results = new HashMap<>(mInputUris.size());
+ for (Uri uri : mInputUris) {
+ if (mPendingInputUris.contains(uri)) {
+ continue;
+ }
+ DecryptVerifyResult result = mAdapter.getItemResult(uri);
+ if (result != null) {
+ results.put(uri, result);
+ }
+ }
+
+ outState.putParcelable(ARG_RESULTS, new ParcelableHashMap<>(results));
+ outState.putParcelable(ARG_OUTPUT_URIS, new ParcelableHashMap<>(mOutputUris));
+ outState.putParcelableArrayList(ARG_CANCELLED_URIS, mCancelledInputUris);
+ outState.putParcelable(ARG_CURRENT_URI, mCurrentInputUri);
+
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
+
+ ArrayList<Uri> inputUris = getArguments().getParcelableArrayList(ARG_INPUT_URIS);
+ ArrayList<Uri> cancelledUris = args.getParcelableArrayList(ARG_CANCELLED_URIS);
+ ParcelableHashMap<Uri,Uri> outputUris = args.getParcelable(ARG_OUTPUT_URIS);
+ ParcelableHashMap<Uri,DecryptVerifyResult> results = args.getParcelable(ARG_RESULTS);
+ Uri currentInputUri = args.getParcelable(ARG_CURRENT_URI);
+
+ displayInputUris(inputUris, currentInputUri, cancelledUris,
+ outputUris != null ? outputUris.getMap() : null,
+ results != null ? results.getMap() : null
+ );
+ }
+
+ private void displayInputUris(ArrayList<Uri> inputUris, Uri currentInputUri,
+ ArrayList<Uri> cancelledUris, HashMap<Uri,Uri> outputUris,
+ HashMap<Uri,DecryptVerifyResult> results) {
+
+ mInputUris = inputUris;
+ mCurrentInputUri = currentInputUri;
+ mOutputUris = outputUris != null ? outputUris : new HashMap<Uri,Uri>(inputUris.size());
+ mCancelledInputUris = cancelledUris != null ? cancelledUris : new ArrayList<Uri>();
+
+ mPendingInputUris = new ArrayList<>();
+
+ for (final Uri uri : inputUris) {
+ mAdapter.add(uri);
+
+ if (uri.equals(mCurrentInputUri)) {
+ continue;
+ }
+
+ if (mCancelledInputUris.contains(uri)) {
+ mAdapter.setCancelled(uri, new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ retryUri(uri);
+ }
+ });
+ continue;
+ }
+
+ if (results != null && results.containsKey(uri)) {
+ processResult(uri, results.get(uri));
+ } else {
+ mOutputUris.put(uri, TemporaryStorageProvider.createFile(getActivity()));
+ mPendingInputUris.add(uri);
+ }
+ }
+
+ if (mCurrentInputUri == null) {
+ cryptoOperation();
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_CODE_OUTPUT: {
+ // This happens after output file was selected, so start our operation
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ Uri decryptedFileUri = mOutputUris.get(mCurrentInputUri);
+ Uri saveUri = data.getData();
+ saveFile(decryptedFileUri, saveUri);
+ mCurrentInputUri = null;
+ }
+ return;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+ }
+
+ private void saveFile(Uri decryptedFileUri, Uri saveUri) {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ try {
+ FileHelper.copyUriData(activity, decryptedFileUri, saveUri);
+ Notify.create(activity, R.string.file_saved, Style.OK).show();
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "error saving file", e);
+ Notify.create(activity, R.string.error_saving_file, Style.ERROR).show();
+ }
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ mAdapter.setProgress(mCurrentInputUri, progress, max, msg);
+ return true;
+ }
+
+ @Override
+ public void onQueuedOperationError(DecryptVerifyResult result) {
+ final Uri uri = mCurrentInputUri;
+ mCurrentInputUri = null;
+
+ mAdapter.addResult(uri, result, null, null, null);
+
+ cryptoOperation();
+ }
+
+ @Override
+ public void onQueuedOperationSuccess(DecryptVerifyResult result) {
+ Uri uri = mCurrentInputUri;
+ mCurrentInputUri = null;
+
+ processResult(uri, result);
+
+ cryptoOperation();
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+ super.onCryptoOperationCancelled();
+
+ final Uri uri = mCurrentInputUri;
+ mCurrentInputUri = null;
+
+ mCancelledInputUris.add(uri);
+ mAdapter.setCancelled(uri, new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ retryUri(uri);
+ }
+ });
+
+ cryptoOperation();
+
+ }
+
+ private void processResult(final Uri uri, final DecryptVerifyResult result) {
+
+ new AsyncTask<Void, Void, Drawable>() {
+ @Override
+ protected Drawable doInBackground(Void... params) {
+
+ Context context = getActivity();
+ if (result.getDecryptionMetadata() == null || context == null) {
+ return null;
+ }
+
+ String type = result.getDecryptionMetadata().getMimeType();
+ Uri outputUri = mOutputUris.get(uri);
+ if (type == null || outputUri == null) {
+ return null;
+ }
+
+ TemporaryStorageProvider.setMimeType(context, outputUri, type);
+
+ if (ClipDescription.compareMimeTypes(type, "image/*")) {
+ int px = FormattingUtils.dpToPx(context, 48);
+ Bitmap bitmap = FileHelper.getThumbnail(context, outputUri, new Point(px, px));
+ return new BitmapDrawable(context.getResources(), bitmap);
+ }
+
+ final Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(outputUri, type);
+
+ final List<ResolveInfo> matches =
+ context.getPackageManager().queryIntentActivities(intent, 0);
+ //noinspection LoopStatementThatDoesntLoop
+ for (ResolveInfo match : matches) {
+ return match.loadIcon(getActivity().getPackageManager());
+ }
+
+ return null;
+
+ }
+
+ @Override
+ protected void onPostExecute(Drawable icon) {
+ processResult(uri, result, icon);
+ }
+ }.execute();
+
+ }
+
+ private void processResult(final Uri uri, DecryptVerifyResult result, Drawable icon) {
+
+ OnClickListener onFileClick = null, onKeyClick = null;
+
+ OpenPgpSignatureResult sigResult = result.getSignatureResult();
+ if (sigResult != null) {
+ final long keyId = sigResult.getKeyId();
+ if (sigResult.getResult() != OpenPgpSignatureResult.RESULT_KEY_MISSING) {
+ onKeyClick = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+ Intent intent = new Intent(activity, ViewKeyActivity.class);
+ intent.setData(KeyRings.buildUnifiedKeyRingUri(keyId));
+ activity.startActivity(intent);
+ }
+ };
+ }
+ }
+
+ if (result.success() && result.getDecryptionMetadata() != null) {
+ onFileClick = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ displayWithViewIntent(uri);
+ }
+ };
+ }
+
+ mAdapter.addResult(uri, result, icon, onFileClick, onKeyClick);
+
+ }
+
+ public void retryUri(Uri uri) {
+
+ // never interrupt running operations!
+ if (mCurrentInputUri != null) {
+ return;
+ }
+
+ // un-cancel this one
+ mCancelledInputUris.remove(uri);
+ mPendingInputUris.add(uri);
+ mAdapter.setCancelled(uri, null);
+
+ cryptoOperation();
+
+ }
+
+ public void displayWithViewIntent(final Uri uri) {
+ Activity activity = getActivity();
+ if (activity == null || mCurrentInputUri != null) {
+ return;
+ }
+
+ final Uri outputUri = mOutputUris.get(uri);
+ final DecryptVerifyResult result = mAdapter.getItemResult(uri);
+ if (outputUri == null || result == null) {
+ return;
+ }
+
+ final OpenPgpMetadata metadata = result.getDecryptionMetadata();
+
+ // text/plain is a special case where we extract the uri content into
+ // the EXTRA_TEXT extra ourselves, and display a chooser which includes
+ // OpenKeychain's internal viewer
+ if ("text/plain".equals(metadata.getMimeType())) {
+
+ // this is a significant i/o operation, use an asynctask
+ new AsyncTask<Void,Void,Intent>() {
+
+ @Override
+ protected Intent doInBackground(Void... params) {
+
+ Activity activity = getActivity();
+ if (activity == null) {
+ return null;
+ }
+
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(outputUri, "text/plain");
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ return intent;
+ }
+
+ @Override
+ protected void onPostExecute(Intent intent) {
+ // for result so we can possibly get a snackbar error from internal viewer
+ Activity activity = getActivity();
+ if (intent == null || activity == null) {
+ return;
+ }
+
+ LabeledIntent internalIntent = new LabeledIntent(
+ new Intent(intent)
+ .setClass(activity, DisplayTextActivity.class)
+ .putExtra(DisplayTextActivity.EXTRA_METADATA, result),
+ BuildConfig.APPLICATION_ID, R.string.view_internal, R.drawable.ic_launcher);
+
+ Intent chooserIntent = Intent.createChooser(intent, getString(R.string.intent_show));
+ chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
+ new Parcelable[] { internalIntent });
+
+ activity.startActivity(chooserIntent);
+ }
+
+ }.execute();
+
+ } else {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(outputUri, metadata.getMimeType());
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ Intent chooserIntent = Intent.createChooser(intent, getString(R.string.intent_show));
+ chooserIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ activity.startActivity(chooserIntent);
+ }
+
+ }
+
+ @Override
+ public PgpDecryptVerifyInputParcel createOperationInput() {
+
+ if (mCurrentInputUri == null) {
+ if (mPendingInputUris.isEmpty()) {
+ // nothing left to do
+ return null;
+ }
+
+ mCurrentInputUri = mPendingInputUris.remove(0);
+ }
+
+ Uri currentOutputUri = mOutputUris.get(mCurrentInputUri);
+ Log.d(Constants.TAG, "mInputUri=" + mCurrentInputUri + ", mOutputUri=" + currentOutputUri);
+
+ return new PgpDecryptVerifyInputParcel(mCurrentInputUri, currentOutputUri)
+ .setAllowSymmetricDecryption(true);
+
+ }
+
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem) {
+ if (mAdapter.mMenuClickedModel == null || !mAdapter.mMenuClickedModel.hasResult()) {
+ return false;
+ }
+
+ // don't process menu items until all items are done!
+ if (!mPendingInputUris.isEmpty()) {
+ return true;
+ }
+
+ Activity activity = getActivity();
+ if (activity == null) {
+ return false;
+ }
+
+ ViewModel model = mAdapter.mMenuClickedModel;
+ DecryptVerifyResult result = model.mResult;
+ switch (menuItem.getItemId()) {
+ case R.id.view_log:
+ Intent intent = new Intent(activity, LogDisplayActivity.class);
+ intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result);
+ activity.startActivity(intent);
+ return true;
+ case R.id.decrypt_save:
+ OpenPgpMetadata metadata = result.getDecryptionMetadata();
+ if (metadata == null) {
+ return true;
+ }
+ mCurrentInputUri = model.mInputUri;
+ FileHelper.saveDocument(this, metadata.getFilename(), model.mInputUri, metadata.getMimeType(),
+ R.string.title_decrypt_to_file, R.string.specify_file_to_decrypt_to, REQUEST_CODE_OUTPUT);
+ return true;
+ case R.id.decrypt_delete:
+ deleteFile(activity, model.mInputUri);
+ return true;
+ }
+ return false;
+ }
+
+ private void deleteFile(Activity activity, Uri uri) {
+
+ if ("file".equals(uri.getScheme())) {
+ File file = new File(uri.getPath());
+ if (file.delete()) {
+ Notify.create(activity, R.string.file_delete_ok, Style.OK).show();
+ } else {
+ Notify.create(activity, R.string.file_delete_none, Style.WARN).show();
+ }
+ return;
+ }
+
+ if ("content".equals(uri.getScheme())) {
+ try {
+ int deleted = activity.getContentResolver().delete(uri, null, null);
+ if (deleted > 0) {
+ Notify.create(activity, R.string.file_delete_ok, Style.OK).show();
+ } else {
+ Notify.create(activity, R.string.file_delete_none, Style.WARN).show();
+ }
+ } catch (Exception e) {
+ Log.e(Constants.TAG, "exception deleting file", e);
+ Notify.create(activity, R.string.file_delete_exception, Style.ERROR).show();
+ }
+ return;
+ }
+
+ Notify.create(activity, R.string.file_delete_exception, Style.ERROR).show();
+
+ }
+
+ public static class DecryptFilesAdapter extends RecyclerView.Adapter<ViewHolder> {
+ private Context mContext;
+ private ArrayList<ViewModel> mDataset;
+ private OnMenuItemClickListener mMenuItemClickListener;
+ private ViewModel mMenuClickedModel;
+
+ public class ViewModel {
+ Context mContext;
+ Uri mInputUri;
+ DecryptVerifyResult mResult;
+ Drawable mIcon;
+
+ OnClickListener mOnFileClickListener;
+ OnClickListener mOnKeyClickListener;
+
+ int mProgress, mMax;
+ String mProgressMsg;
+ OnClickListener mCancelled;
+
+ ViewModel(Context context, Uri uri) {
+ mContext = context;
+ mInputUri = uri;
+ mProgress = 0;
+ mMax = 100;
+ mCancelled = null;
+ }
+
+ void addResult(DecryptVerifyResult result) {
+ mResult = result;
+ }
+
+ void addIcon(Drawable icon) {
+ mIcon = icon;
+ }
+
+ void setOnClickListeners(OnClickListener onFileClick, OnClickListener onKeyClick) {
+ mOnFileClickListener = onFileClick;
+ mOnKeyClickListener = onKeyClick;
+ }
+
+ boolean hasResult() {
+ return mResult != null;
+ }
+
+ void setCancelled(OnClickListener retryListener) {
+ mCancelled = retryListener;
+ }
+
+ void setProgress(int progress, int max, String msg) {
+ if (msg != null) {
+ mProgressMsg = msg;
+ }
+ mProgress = progress;
+ mMax = max;
+ }
+
+ // Depends on inputUri only
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ViewModel viewModel = (ViewModel) o;
+ return !(mInputUri != null ? !mInputUri.equals(viewModel.mInputUri)
+ : viewModel.mInputUri != null);
+ }
+
+ // Depends on inputUri only
+ @Override
+ public int hashCode() {
+ return mResult != null ? mResult.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return mResult.toString();
+ }
+ }
+
+ // Provide a suitable constructor (depends on the kind of dataset)
+ public DecryptFilesAdapter(Context context, OnMenuItemClickListener menuItemClickListener) {
+ mContext = context;
+ mMenuItemClickListener = menuItemClickListener;
+ mDataset = new ArrayList<>();
+ }
+
+ // Create new views (invoked by the layout manager)
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ //inflate your layout and pass it to view holder
+ View v = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.decrypt_list_entry, parent, false);
+ return new ViewHolder(v);
+ }
+
+ // Replace the contents of a view (invoked by the layout manager)
+ @Override
+ public void onBindViewHolder(ViewHolder holder, final int position) {
+ // - get element from your dataset at this position
+ // - replace the contents of the view with that element
+ final ViewModel model = mDataset.get(position);
+
+ if (model.mCancelled != null) {
+ bindItemCancelled(holder, model);
+ return;
+ }
+
+ if (!model.hasResult()) {
+ bindItemProgress(holder, model);
+ return;
+ }
+
+ if (model.mResult.success()) {
+ bindItemSuccess(holder, model);
+ } else {
+ bindItemFailure(holder, model);
+ }
+
+ }
+
+ private void bindItemCancelled(ViewHolder holder, ViewModel model) {
+ if (holder.vAnimator.getDisplayedChild() != 3) {
+ holder.vAnimator.setDisplayedChild(3);
+ }
+
+ holder.vCancelledRetry.setOnClickListener(model.mCancelled);
+ }
+
+ private void bindItemProgress(ViewHolder holder, ViewModel model) {
+ if (holder.vAnimator.getDisplayedChild() != 0) {
+ holder.vAnimator.setDisplayedChild(0);
+ }
+
+ holder.vProgress.setProgress(model.mProgress);
+ holder.vProgress.setMax(model.mMax);
+ if (model.mProgressMsg != null) {
+ holder.vProgressMsg.setText(model.mProgressMsg);
+ }
+ }
+
+ private void bindItemSuccess(ViewHolder holder, final ViewModel model) {
+ if (holder.vAnimator.getDisplayedChild() != 1) {
+ holder.vAnimator.setDisplayedChild(1);
+ }
+
+ KeyFormattingUtils.setStatus(mContext, holder, model.mResult);
+
+ final OpenPgpMetadata metadata = model.mResult.getDecryptionMetadata();
+
+ String filename;
+ if (metadata == null) {
+ filename = mContext.getString(R.string.filename_unknown);
+ } else if (TextUtils.isEmpty(metadata.getFilename())) {
+ filename = mContext.getString("text/plain".equals(metadata.getMimeType())
+ ? R.string.filename_unknown_text : R.string.filename_unknown);
+ } else {
+ filename = metadata.getFilename();
+ }
+ holder.vFilename.setText(filename);
+
+ long size = metadata == null ? 0 : metadata.getOriginalSize();
+ if (size == -1 || size == 0) {
+ holder.vFilesize.setText("");
+ } else {
+ holder.vFilesize.setText(FileHelper.readableFileSize(size));
+ }
+
+ if (model.mIcon != null) {
+ holder.vThumbnail.setImageDrawable(model.mIcon);
+ } else {
+ holder.vThumbnail.setImageResource(R.drawable.ic_doc_generic_am);
+ }
+
+ holder.vFile.setOnClickListener(model.mOnFileClickListener);
+ holder.vSignatureLayout.setOnClickListener(model.mOnKeyClickListener);
+
+ holder.vContextMenu.setTag(model);
+ holder.vContextMenu.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mMenuClickedModel = model;
+ PopupMenu menu = new PopupMenu(mContext, view);
+ menu.inflate(R.menu.decrypt_item_context_menu);
+ menu.setOnMenuItemClickListener(mMenuItemClickListener);
+ menu.setOnDismissListener(new OnDismissListener() {
+ @Override
+ public void onDismiss(PopupMenu popupMenu) {
+ mMenuClickedModel = null;
+ }
+ });
+ menu.show();
+ }
+ });
+ }
+
+ private void bindItemFailure(ViewHolder holder, final ViewModel model) {
+ if (holder.vAnimator.getDisplayedChild() != 2) {
+ holder.vAnimator.setDisplayedChild(2);
+ }
+
+ holder.vErrorMsg.setText(model.mResult.getLog().getLast().mType.getMsgId());
+
+ holder.vErrorViewLog.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(mContext, LogDisplayActivity.class);
+ intent.putExtra(LogDisplayFragment.EXTRA_RESULT, model.mResult);
+ mContext.startActivity(intent);
+ }
+ });
+
+ }
+
+ // Return the size of your dataset (invoked by the layout manager)
+ @Override
+ public int getItemCount() {
+ return mDataset.size();
+ }
+
+ public DecryptVerifyResult getItemResult(Uri uri) {
+ ViewModel model = new ViewModel(mContext, uri);
+ int pos = mDataset.indexOf(model);
+ if (pos == -1) {
+ return null;
+ }
+ model = mDataset.get(pos);
+
+ return model.mResult;
+ }
+
+ public void add(Uri uri) {
+ ViewModel newModel = new ViewModel(mContext, uri);
+ mDataset.add(newModel);
+ notifyItemInserted(mDataset.size());
+ }
+
+ public void setProgress(Uri uri, int progress, int max, String msg) {
+ ViewModel newModel = new ViewModel(mContext, uri);
+ int pos = mDataset.indexOf(newModel);
+ mDataset.get(pos).setProgress(progress, max, msg);
+ notifyItemChanged(pos);
+ }
+
+ public void setCancelled(Uri uri, OnClickListener retryListener) {
+ ViewModel newModel = new ViewModel(mContext, uri);
+ int pos = mDataset.indexOf(newModel);
+ mDataset.get(pos).setCancelled(retryListener);
+ notifyItemChanged(pos);
+ }
+
+ public void addResult(Uri uri, DecryptVerifyResult result, Drawable icon,
+ OnClickListener onFileClick, OnClickListener onKeyClick) {
+
+ ViewModel model = new ViewModel(mContext, uri);
+ int pos = mDataset.indexOf(model);
+ model = mDataset.get(pos);
+
+ model.addResult(result);
+ if (icon != null) {
+ model.addIcon(icon);
+ }
+ model.setOnClickListeners(onFileClick, onKeyClick);
+
+ notifyItemChanged(pos);
+ }
+
+ }
+
+
+ // Provide a reference to the views for each data item
+ // Complex data items may need more than one view per item, and
+ // you provide access to all the views for a data item in a view holder
+ public static class ViewHolder extends RecyclerView.ViewHolder implements StatusHolder {
+ public ViewAnimator vAnimator;
+
+ public ProgressBar vProgress;
+ public TextView vProgressMsg;
+
+ public View vFile;
+ public TextView vFilename;
+ public TextView vFilesize;
+ public ImageView vThumbnail;
+
+ public ImageView vEncStatusIcon;
+ public TextView vEncStatusText;
+
+ public ImageView vSigStatusIcon;
+ public TextView vSigStatusText;
+ public View vSignatureLayout;
+ public TextView vSignatureName;
+ public TextView vSignatureMail;
+ public TextView vSignatureAction;
+ public View vContextMenu;
+
+ public TextView vErrorMsg;
+ public ImageView vErrorViewLog;
+
+ public ImageView vCancelledRetry;
+
+ public ViewHolder(View itemView) {
+ super(itemView);
+
+ vAnimator = (ViewAnimator) itemView.findViewById(R.id.view_animator);
+
+ vProgress = (ProgressBar) itemView.findViewById(R.id.progress);
+ vProgressMsg = (TextView) itemView.findViewById(R.id.progress_msg);
+
+ vFile = itemView.findViewById(R.id.file);
+ vFilename = (TextView) itemView.findViewById(R.id.filename);
+ vFilesize = (TextView) itemView.findViewById(R.id.filesize);
+ vThumbnail = (ImageView) itemView.findViewById(R.id.thumbnail);
+
+ vEncStatusIcon = (ImageView) itemView.findViewById(R.id.result_encryption_icon);
+ vEncStatusText = (TextView) itemView.findViewById(R.id.result_encryption_text);
+
+ vSigStatusIcon = (ImageView) itemView.findViewById(R.id.result_signature_icon);
+ vSigStatusText = (TextView) itemView.findViewById(R.id.result_signature_text);
+ vSignatureLayout = itemView.findViewById(R.id.result_signature_layout);
+ vSignatureName = (TextView) itemView.findViewById(R.id.result_signature_name);
+ vSignatureMail= (TextView) itemView.findViewById(R.id.result_signature_email);
+ vSignatureAction = (TextView) itemView.findViewById(R.id.result_signature_action);
+
+ vContextMenu = itemView.findViewById(R.id.context_menu);
+
+ vErrorMsg = (TextView) itemView.findViewById(R.id.result_error_msg);
+ vErrorViewLog = (ImageView) itemView.findViewById(R.id.result_error_log);
+
+ vCancelledRetry = (ImageView) itemView.findViewById(R.id.cancel_retry);
+
+ }
+
+ @Override
+ public ImageView getEncryptionStatusIcon() {
+ return vEncStatusIcon;
+ }
+
+ @Override
+ public TextView getEncryptionStatusText() {
+ return vEncStatusText;
+ }
+
+ @Override
+ public ImageView getSignatureStatusIcon() {
+ return vSigStatusIcon;
+ }
+
+ @Override
+ public TextView getSignatureStatusText() {
+ return vSigStatusText;
+ }
+
+ @Override
+ public View getSignatureLayout() {
+ return vSignatureLayout;
+ }
+
+ @Override
+ public TextView getSignatureAction() {
+ return vSignatureAction;
+ }
+
+ @Override
+ public TextView getSignatureUserName() {
+ return vSignatureName;
+ }
+
+ @Override
+ public TextView getSignatureUserEmail() {
+ return vSignatureMail;
+ }
+
+ @Override
+ public boolean hasEncrypt() {
+ return true;
+ }
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java
deleted file mode 100644
index e2eba3947..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextActivity.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2012-2015 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.Toast;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
-import org.sufficientlysecure.keychain.intents.OpenKeychainIntents;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.operations.results.SingletonResult;
-import org.sufficientlysecure.keychain.pgp.PgpHelper;
-import org.sufficientlysecure.keychain.ui.base.BaseActivity;
-import org.sufficientlysecure.keychain.util.Log;
-
-import java.util.regex.Matcher;
-
-public class DecryptTextActivity extends BaseActivity {
-
- /* Intents */
- public static final String ACTION_DECRYPT_TEXT = OpenKeychainIntents.DECRYPT_TEXT;
- public static final String EXTRA_TEXT = OpenKeychainIntents.DECRYPT_EXTRA_TEXT;
-
- // intern
- public static final String ACTION_DECRYPT_FROM_CLIPBOARD = Constants.INTENT_PREFIX + "DECRYPT_TEXT_FROM_CLIPBOARD";
-
- DecryptTextFragment mFragment;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setFullScreenDialogClose(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- setResult(Activity.RESULT_CANCELED);
- finish();
- }
- }, false);
-
- // Handle intent actions
- handleActions(savedInstanceState, getIntent());
- }
-
- @Override
- protected void initLayout() {
- setContentView(R.layout.decrypt_text_activity);
- }
-
- /**
- * Fixing broken PGP MESSAGE Strings coming from GMail/AOSP Mail
- */
- private String fixPgpMessage(String message) {
- // windows newline -> unix newline
- message = message.replaceAll("\r\n", "\n");
- // Mac OS before X newline -> unix newline
- message = message.replaceAll("\r", "\n");
-
- // remove whitespaces before newline
- message = message.replaceAll(" +\n", "\n");
- // only two consecutive newlines are allowed
- message = message.replaceAll("\n\n+", "\n\n");
-
- // replace non breakable spaces
- message = message.replaceAll("\\xa0", " ");
-
- return message;
- }
-
- /**
- * Fixing broken PGP SIGNED MESSAGE Strings coming from GMail/AOSP Mail
- */
- private String fixPgpCleartextSignature(CharSequence input) {
- if (!TextUtils.isEmpty(input)) {
- String text = input.toString();
-
- // windows newline -> unix newline
- text = text.replaceAll("\r\n", "\n");
- // Mac OS before X newline -> unix newline
- text = text.replaceAll("\r", "\n");
-
- return text;
- } else {
- return null;
- }
- }
-
- private String getPgpContent(CharSequence input) {
- // only decrypt if clipboard content is available and a pgp message or cleartext signature
- if (!TextUtils.isEmpty(input)) {
- Log.dEscaped(Constants.TAG, "input: " + input);
-
- Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(input);
- if (matcher.matches()) {
- String text = matcher.group(1);
- text = fixPgpMessage(text);
-
- Log.dEscaped(Constants.TAG, "input fixed: " + text);
- return text;
- } else {
- matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(input);
- if (matcher.matches()) {
- String text = matcher.group(1);
- text = fixPgpCleartextSignature(text);
-
- Log.dEscaped(Constants.TAG, "input fixed: " + text);
- return text;
- } else {
- return null;
- }
- }
- } else {
- return null;
- }
- }
-
- /**
- * Handles all actions with this intent
- */
- private void handleActions(Bundle savedInstanceState, Intent intent) {
- String action = intent.getAction();
- Bundle extras = intent.getExtras();
- String type = intent.getType();
-
- if (extras == null) {
- extras = new Bundle();
- }
-
- if (Intent.ACTION_SEND.equals(action) && type != null) {
- Log.d(Constants.TAG, "ACTION_SEND");
- Log.logDebugBundle(extras, "SEND extras");
-
- // When sending to Keychain Decrypt via share menu
- if ("text/plain".equals(type)) {
- String sharedText = extras.getString(Intent.EXTRA_TEXT);
- sharedText = getPgpContent(sharedText);
-
- if (sharedText != null) {
- loadFragment(savedInstanceState, sharedText);
- } else {
- Log.e(Constants.TAG, "EXTRA_TEXT does not contain PGP content!");
- Toast.makeText(this, R.string.error_invalid_data, Toast.LENGTH_LONG).show();
- finish();
- }
- } else {
- Log.e(Constants.TAG, "ACTION_SEND received non-plaintext, this should not happen in this activity!");
- Toast.makeText(this, R.string.error_invalid_data, Toast.LENGTH_LONG).show();
- finish();
- }
- } else if (ACTION_DECRYPT_TEXT.equals(action)) {
- Log.d(Constants.TAG, "ACTION_DECRYPT_TEXT");
-
- String extraText = extras.getString(EXTRA_TEXT);
- extraText = getPgpContent(extraText);
-
- if (extraText != null) {
- loadFragment(savedInstanceState, extraText);
- } else {
- Log.e(Constants.TAG, "EXTRA_TEXT does not contain PGP content!");
- Toast.makeText(this, R.string.error_invalid_data, Toast.LENGTH_LONG).show();
- finish();
- }
- } else if (ACTION_DECRYPT_FROM_CLIPBOARD.equals(action)) {
- Log.d(Constants.TAG, "ACTION_DECRYPT_FROM_CLIPBOARD");
-
- CharSequence clipboardText = ClipboardReflection.getClipboardText(this);
- String text = getPgpContent(clipboardText);
-
- if (text != null) {
- loadFragment(savedInstanceState, text);
- } else {
- returnInvalidResult();
- }
- } else if (ACTION_DECRYPT_TEXT.equals(action)) {
- Log.e(Constants.TAG, "Include the extra 'text' in your Intent!");
- Toast.makeText(this, R.string.error_invalid_data, Toast.LENGTH_LONG).show();
- finish();
- }
- }
-
- private void returnInvalidResult() {
- SingletonResult result = new SingletonResult(
- SingletonResult.RESULT_ERROR, OperationResult.LogType.MSG_NO_VALID_ENC);
- Intent intent = new Intent();
- intent.putExtra(SingletonResult.EXTRA_RESULT, result);
- setResult(RESULT_OK, intent);
- finish();
- }
-
- private void loadFragment(Bundle savedInstanceState, String ciphertext) {
- // 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
- mFragment = DecryptTextFragment.newInstance(ciphertext);
-
- // Add the fragment to the 'fragment_container' FrameLayout
- // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
- getSupportFragmentManager().beginTransaction()
- .replace(R.id.decrypt_text_fragment_container, mFragment)
- .commitAllowingStateLoss();
- // do it immediately!
- getSupportFragmentManager().executePendingTransactions();
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java
deleted file mode 100644
index 381da6f0d..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import android.app.ProgressDialog;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentService.IOType;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
-import org.sufficientlysecure.keychain.ui.util.Notify;
-import org.sufficientlysecure.keychain.util.ShareHelper;
-
-import java.io.UnsupportedEncodingException;
-
-public class DecryptTextFragment extends DecryptFragment {
- public static final String ARG_CIPHERTEXT = "ciphertext";
-
- // view
- private TextView mText;
-
- // model
- private String mCiphertext;
- private boolean mShowMenuOptions = false;
-
- /**
- * Creates new instance of this fragment
- */
- public static DecryptTextFragment newInstance(String ciphertext) {
- DecryptTextFragment frag = new DecryptTextFragment();
-
- Bundle args = new Bundle();
- args.putString(ARG_CIPHERTEXT, ciphertext);
-
- frag.setArguments(args);
-
- return frag;
- }
-
- /**
- * Inflate the layout for this fragment
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.decrypt_text_fragment, container, false);
- mText = (TextView) view.findViewById(R.id.decrypt_text_plaintext);
-
- return view;
- }
-
- /**
- * Create Intent Chooser but exclude decrypt activites
- */
- private Intent sendWithChooserExcludingDecrypt(String text) {
- Intent prototype = createSendIntent(text);
- String title = getString(R.string.title_share_message);
-
- // we don't want to decrypt the decrypted, no inception ;)
- String[] blacklist = new String[]{
- Constants.PACKAGE_NAME + ".ui.DecryptTextActivity",
- "org.thialfihar.android.apg.ui.DecryptActivity"
- };
-
- return new ShareHelper(getActivity()).createChooserExcluding(prototype, title, blacklist);
- }
-
- private Intent createSendIntent(String text) {
- Intent sendIntent = new Intent(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_TEXT, text);
- sendIntent.setType("text/plain");
- return sendIntent;
- }
-
- private void copyToClipboard(String text) {
- ClipboardReflection.copyToClipboard(getActivity(), text);
- Notify.create(getActivity(), R.string.text_copied_to_clipboard, Notify.Style.OK).show();
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setHasOptionsMenu(true);
-
- String ciphertext = getArguments().getString(ARG_CIPHERTEXT);
- if (ciphertext != null) {
- mCiphertext = ciphertext;
- cryptoOperation(new CryptoInputParcel());
- }
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- super.onCreateOptionsMenu(menu, inflater);
- if (mShowMenuOptions) {
- inflater.inflate(R.menu.decrypt_menu, menu);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.decrypt_share: {
- startActivity(sendWithChooserExcludingDecrypt(mText.getText().toString()));
- break;
- }
- case R.id.decrypt_copy: {
- copyToClipboard(mText.getText().toString());
- break;
- }
- default: {
- return super.onOptionsItemSelected(item);
- }
- }
-
- return true;
- }
-
- @Override
- protected void cryptoOperation(CryptoInputParcel cryptoInput) {
- // Send all information needed to service to decrypt in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY);
-
- // data
- data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput);
- data.putInt(KeychainIntentService.TARGET, IOType.BYTES.ordinal());
- data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, mCiphertext.getBytes());
- data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput);
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after encrypting is done in KeychainIntentService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_decrypting),
- ProgressDialog.STYLE_HORIZONTAL,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- // handle pending messages
- if (handlePendingMessage(message)) {
- return;
- }
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- DecryptVerifyResult pgpResult =
- returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
-
- if (pgpResult.success()) {
- byte[] decryptedMessage = returnData
- .getByteArray(KeychainIntentService.RESULT_DECRYPTED_BYTES);
- String displayMessage;
- if (pgpResult.getCharset() != null) {
- try {
- displayMessage = new String(decryptedMessage, pgpResult.getCharset());
- } catch (UnsupportedEncodingException e) {
- // if we can't decode properly, just fall back to utf-8
- displayMessage = new String(decryptedMessage);
- }
- } else {
- displayMessage = new String(decryptedMessage);
- }
- mText.setText(displayMessage);
-
- // display signature result in activity
- loadVerifyResult(pgpResult);
- } else {
- // TODO: show also invalid layout with different text?
- }
- pgpResult.createNotify(getActivity()).show(DecryptTextFragment.this);
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(getActivity());
-
- // start service with intent
- getActivity().startService(intent);
- }
-
- @Override
- protected void onVerifyLoaded(boolean hideErrorOverlay) {
- mShowMenuOptions = hideErrorOverlay;
- getActivity().supportInvalidateOptionsMenu();
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java
new file mode 100644
index 000000000..478259133
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Activity;
+import android.support.v7.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentActivity;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.DeleteResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.operations.results.RevokeResult;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.DeleteKeyringParcel;
+import org.sufficientlysecure.keychain.service.RevokeKeyringParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
+import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
+import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.Date;
+import java.util.HashMap;
+
+public class DeleteKeyDialogActivity extends FragmentActivity {
+ public static final String EXTRA_DELETE_MASTER_KEY_IDS = "extra_delete_master_key_ids";
+ public static final String EXTRA_HAS_SECRET = "extra_has_secret";
+ public static final String EXTRA_KEYSERVER = "extra_keyserver";
+
+ private CryptoOperationHelper<DeleteKeyringParcel, DeleteResult> mDeleteOpHelper;
+ private CryptoOperationHelper<RevokeKeyringParcel, RevokeResult> mRevokeOpHelper;
+
+ private long[] mMasterKeyIds;
+ private boolean mHasSecret;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mDeleteOpHelper = new CryptoOperationHelper<>(1, DeleteKeyDialogActivity.this,
+ getDeletionCallback(), R.string.progress_deleting);
+
+ mRevokeOpHelper = new CryptoOperationHelper<>(2, this,
+ getRevocationCallback(), R.string.progress_revoking_uploading);
+
+ mMasterKeyIds = getIntent().getLongArrayExtra(EXTRA_DELETE_MASTER_KEY_IDS);
+ mHasSecret = getIntent().getBooleanExtra(EXTRA_HAS_SECRET, false);
+
+ if (mMasterKeyIds.length > 1 && mHasSecret) {
+ // secret keys can only be deleted individually
+ OperationResult.OperationLog log = new OperationResult.OperationLog();
+ log.add(OperationResult.LogType.MSG_DEL_ERROR_MULTI_SECRET, 0);
+ returnResult(new DeleteResult(OperationResult.RESULT_ERROR, log, 0,
+ mMasterKeyIds.length));
+ }
+
+ if (mMasterKeyIds.length == 1 && mHasSecret) {
+ // if mMasterKeyIds.length == 0 we let the DeleteOperation respond
+ try {
+ HashMap<String, Object> data = new ProviderHelper(this).getUnifiedData(
+ mMasterKeyIds[0], new String[]{
+ KeychainContract.KeyRings.USER_ID,
+ KeychainContract.KeyRings.IS_REVOKED
+ }, new int[]{
+ ProviderHelper.FIELD_TYPE_STRING,
+ ProviderHelper.FIELD_TYPE_INTEGER
+ }
+ );
+
+ String name;
+ KeyRing.UserId mainUserId = KeyRing.splitUserId(
+ (String) data.get(KeychainContract.KeyRings.USER_ID));
+ if (mainUserId.name != null) {
+ name = mainUserId.name;
+ } else {
+ name = getString(R.string.user_id_no_name);
+ }
+
+ if ((long) data.get(KeychainContract.KeyRings.IS_REVOKED) > 0) {
+ showNormalDeleteDialog();
+ } else {
+ showRevokeDeleteDialog(name);
+ }
+ } catch (ProviderHelper.NotFoundException e) {
+ Log.e(Constants.TAG,
+ "Secret key to delete not found at DeleteKeyDialogActivity for "
+ + mMasterKeyIds[0], e);
+ finish();
+ }
+ } else {
+ showNormalDeleteDialog();
+ }
+ }
+
+ private void showNormalDeleteDialog() {
+
+ DeleteKeyDialogFragment deleteKeyDialogFragment
+ = DeleteKeyDialogFragment.newInstance(mMasterKeyIds, mHasSecret);
+
+ deleteKeyDialogFragment.show(getSupportFragmentManager(), "deleteKeyDialog");
+
+ }
+
+ private void showRevokeDeleteDialog(String keyname) {
+
+ RevokeDeleteDialogFragment fragment = RevokeDeleteDialogFragment.newInstance(keyname);
+ fragment.show(getSupportFragmentManager(), "deleteRevokeDialog");
+ }
+
+ private void startRevocationOperation() {
+ mRevokeOpHelper.cryptoOperation(new CryptoInputParcel(new Date(), false));
+ }
+
+ private void startDeletionOperation() {
+ mDeleteOpHelper.cryptoOperation();
+ }
+
+ private CryptoOperationHelper.Callback<RevokeKeyringParcel, RevokeResult> getRevocationCallback() {
+
+ return new CryptoOperationHelper.Callback<RevokeKeyringParcel, RevokeResult>() {
+ @Override
+ public RevokeKeyringParcel createOperationInput() {
+ return new RevokeKeyringParcel(mMasterKeyIds[0], true,
+ getIntent().getStringExtra(EXTRA_KEYSERVER));
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(RevokeResult result) {
+ returnResult(result);
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+
+ @Override
+ public void onCryptoOperationError(RevokeResult result) {
+ returnResult(result);
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
+ };
+ }
+
+ private CryptoOperationHelper.Callback<DeleteKeyringParcel, DeleteResult> getDeletionCallback() {
+
+ return new CryptoOperationHelper.Callback<DeleteKeyringParcel, DeleteResult>() {
+ @Override
+ public DeleteKeyringParcel createOperationInput() {
+ return new DeleteKeyringParcel(mMasterKeyIds, mHasSecret);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(DeleteResult result) {
+ returnResult(result);
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+
+ @Override
+ public void onCryptoOperationError(DeleteResult result) {
+ returnResult(result);
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
+ };
+ }
+
+ private void returnResult(OperationResult result) {
+ Intent intent = new Intent();
+ intent.putExtra(OperationResult.EXTRA_RESULT, result);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ mDeleteOpHelper.handleActivityResult(requestCode, resultCode, data);
+ mRevokeOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+
+ public static class DeleteKeyDialogFragment extends DialogFragment {
+
+ private static final String ARG_DELETE_MASTER_KEY_IDS = "delete_master_key_ids";
+ private static final String ARG_HAS_SECRET = "has_secret";
+
+ private TextView mMainMessage;
+ private View mInflateView;
+
+ /**
+ * Creates new instance of this delete file dialog fragment
+ */
+ public static DeleteKeyDialogFragment newInstance(long[] masterKeyIds, boolean hasSecret) {
+ DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment();
+ Bundle args = new Bundle();
+
+ args.putLongArray(ARG_DELETE_MASTER_KEY_IDS, masterKeyIds);
+ args.putBoolean(ARG_HAS_SECRET, hasSecret);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final FragmentActivity activity = getActivity();
+
+ final long[] masterKeyIds = getArguments().getLongArray(ARG_DELETE_MASTER_KEY_IDS);
+ final boolean hasSecret = getArguments().getBoolean(ARG_HAS_SECRET);
+
+ ContextThemeWrapper theme = ThemeChanger.getDialogThemeWrapper(activity);
+
+ CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(theme);
+
+ // Setup custom View to display in AlertDialog
+ LayoutInflater inflater = LayoutInflater.from(theme);
+ mInflateView = inflater.inflate(R.layout.view_key_delete_fragment, null);
+ builder.setView(mInflateView);
+
+ mMainMessage = (TextView) mInflateView.findViewById(R.id.mainMessage);
+
+ // If only a single key has been selected
+ if (masterKeyIds.length == 1) {
+ long masterKeyId = masterKeyIds[0];
+
+ try {
+ HashMap<String, Object> data = new ProviderHelper(activity).getUnifiedData(
+ masterKeyId, new String[]{
+ KeychainContract.KeyRings.USER_ID,
+ KeychainContract.KeyRings.HAS_ANY_SECRET
+ }, new int[]{
+ ProviderHelper.FIELD_TYPE_STRING,
+ ProviderHelper.FIELD_TYPE_INTEGER
+ }
+ );
+ String name;
+ KeyRing.UserId mainUserId = KeyRing.splitUserId((String) data.get(KeychainContract.KeyRings.USER_ID));
+ if (mainUserId.name != null) {
+ name = mainUserId.name;
+ } else {
+ name = getString(R.string.user_id_no_name);
+ }
+
+ if (hasSecret) {
+ // show title only for secret key deletions,
+ // see http://www.google.com/design/spec/components/dialogs.html#dialogs-behavior
+ builder.setTitle(getString(R.string.title_delete_secret_key, name));
+ mMainMessage.setText(getString(R.string.secret_key_deletion_confirmation, name));
+ } else {
+ mMainMessage.setText(getString(R.string.public_key_deletetion_confirmation, name));
+ }
+ } catch (ProviderHelper.NotFoundException e) {
+ dismiss();
+ return null;
+ }
+ } else {
+ mMainMessage.setText(R.string.key_deletion_confirmation_multi);
+ }
+
+ builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ ((DeleteKeyDialogActivity) getActivity()).startDeletionOperation();
+ }
+ });
+
+ builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+
+ return builder.show();
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+ getActivity().setResult(RESULT_CANCELED);
+ getActivity().finish();
+ }
+ }
+
+ public static class RevokeDeleteDialogFragment extends DialogFragment {
+
+ public static final String ARG_KEY_NAME = "arg_key_name";
+
+ public static RevokeDeleteDialogFragment newInstance(String keyName) {
+ Bundle args = new Bundle();
+ args.putString(ARG_KEY_NAME, keyName);
+ RevokeDeleteDialogFragment frag = new RevokeDeleteDialogFragment();
+ frag.setArguments(args);
+ return frag;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Activity activity = getActivity();
+
+ final String CHOICE_REVOKE = getString(R.string.del_rev_dialog_choice_rev_upload);
+ final String CHOICE_DELETE = getString(R.string.del_rev_dialog_choice_delete);
+
+ ContextThemeWrapper theme = ThemeChanger.getDialogThemeWrapper(activity);
+
+ CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(theme);
+ builder.setTitle(getString(R.string.del_rev_dialog_title,
+ getArguments().get(ARG_KEY_NAME)));
+
+ LayoutInflater inflater = LayoutInflater.from(theme);
+ View view = inflater.inflate(R.layout.del_rev_dialog, null);
+ builder.setView(view);
+
+ final Spinner spinner = (Spinner) view.findViewById(R.id.spinner);
+
+ builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+
+ builder.setPositiveButton(R.string.del_rev_dialog_btn_revoke,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ String choice = spinner.getSelectedItem().toString();
+ if (choice.equals(CHOICE_REVOKE)) {
+ ((DeleteKeyDialogActivity) activity)
+ .startRevocationOperation();
+ } else if (choice.equals(CHOICE_DELETE)) {
+ ((DeleteKeyDialogActivity) activity)
+ .showNormalDeleteDialog();
+ } else {
+ throw new AssertionError(
+ "Unsupported delete type in RevokeDeleteDialogFragment");
+ }
+ }
+ });
+
+ final AlertDialog alertDialog = builder.show();
+
+ spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+
+ String choice = parent.getItemAtPosition(pos).toString();
+
+ if (choice.equals(CHOICE_REVOKE)) {
+ alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
+ .setText(R.string.del_rev_dialog_btn_revoke);
+ } else if (choice.equals(CHOICE_DELETE)) {
+ alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
+ .setText(R.string.del_rev_dialog_btn_delete);
+ } else {
+ throw new AssertionError(
+ "Unsupported delete type in RevokeDeleteDialogFragment");
+ }
+ }
+
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+
+ return alertDialog;
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+ getActivity().setResult(RESULT_CANCELED);
+ getActivity().finish();
+ }
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextActivity.java
new file mode 100644
index 000000000..3c8e972b9
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextActivity.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+
+import java.io.IOException;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.View;
+import android.widget.Toast;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
+import org.sufficientlysecure.keychain.ui.base.BaseActivity;
+import org.sufficientlysecure.keychain.util.FileHelper;
+
+public class DisplayTextActivity extends BaseActivity {
+
+ public static final String EXTRA_METADATA = "metadata";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setFullScreenDialogClose(Activity.RESULT_CANCELED, false);
+
+ // Handle intent actions
+ handleActions(savedInstanceState, getIntent());
+ }
+
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.decrypt_text_activity);
+ }
+
+ /**
+ * Handles all actions with this intent
+ */
+ private void handleActions(Bundle savedInstanceState, Intent intent) {
+ if (savedInstanceState != null) {
+ return;
+ }
+
+ DecryptVerifyResult result = intent.getParcelableExtra(EXTRA_METADATA);
+
+ String plaintext;
+ try {
+ plaintext = FileHelper.readTextFromUri(this, intent.getData(), result.getCharset());
+ } catch (IOException e) {
+ Toast.makeText(this, R.string.error_preparing_data, Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ if (plaintext != null) {
+ loadFragment(plaintext, result);
+ } else {
+ Toast.makeText(this, R.string.error_invalid_data, Toast.LENGTH_LONG).show();
+ finish();
+ }
+ }
+
+ private void loadFragment(String plaintext, DecryptVerifyResult result) {
+ // Create an instance of the fragment
+ Fragment frag = DisplayTextFragment.newInstance(plaintext, result);
+
+ // Add the fragment to the 'fragment_container' FrameLayout
+ // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.decrypt_text_fragment_container, frag)
+ .commitAllowingStateLoss();
+ // do it immediately!
+ getSupportFragmentManager().executePendingTransactions();
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextFragment.java
new file mode 100644
index 000000000..dc06e9115
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DisplayTextFragment.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
+import org.sufficientlysecure.keychain.util.ShareHelper;
+
+public class DisplayTextFragment extends DecryptFragment {
+
+ public static final String ARG_PLAINTEXT = "plaintext";
+
+ // view
+ private TextView mText;
+
+ // model (no state to persist though, that's all in arguments!)
+ private boolean mShowMenuOptions = false;
+
+ public static DisplayTextFragment newInstance(String plaintext, DecryptVerifyResult result) {
+ DisplayTextFragment frag = new DisplayTextFragment();
+
+ Bundle args = new Bundle();
+ args.putString(ARG_PLAINTEXT, plaintext);
+ args.putParcelable(ARG_DECRYPT_VERIFY_RESULT, result);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ /**
+ * Create Intent Chooser but exclude decrypt activites
+ */
+ private Intent sendWithChooserExcludingDecrypt(String text) {
+ Intent prototype = createSendIntent(text);
+ String title = getString(R.string.title_share_message);
+
+ // we don't want to decrypt the decrypted, no inception ;)
+ String[] blacklist = new String[]{
+ Constants.PACKAGE_NAME + ".ui.DecryptActivity",
+ "org.thialfihar.android.apg.ui.DecryptActivity"
+ };
+
+ return new ShareHelper(getActivity()).createChooserExcluding(prototype, title, blacklist);
+ }
+
+ private Intent createSendIntent(String text) {
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_TEXT, text);
+ sendIntent.setType("text/plain");
+ return sendIntent;
+ }
+
+ private void copyToClipboard(String text) {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
+ if (clipMan == null) {
+ Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR).show();
+ return;
+ }
+
+ clipMan.setPrimaryClip(ClipData.newPlainText(Constants.CLIPBOARD_LABEL, text));
+ Notify.create(activity, R.string.text_copied_to_clipboard, Style.OK).show();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.decrypt_text_fragment, container, false);
+ mText = (TextView) view.findViewById(R.id.decrypt_text_plaintext);
+ return view;
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ Bundle args = getArguments();
+
+ String plaintext = args.getString(ARG_PLAINTEXT);
+ DecryptVerifyResult result = args.getParcelable(ARG_DECRYPT_VERIFY_RESULT);
+
+ // display signature result in activity
+ mText.setText(plaintext);
+ loadVerifyResult(result);
+
+ }
+
+ @Override
+ protected void onVerifyLoaded(boolean hideErrorOverlay) {
+ mShowMenuOptions = hideErrorOverlay;
+ getActivity().supportInvalidateOptionsMenu();
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ if (mShowMenuOptions) {
+ inflater.inflate(R.menu.decrypt_menu, menu);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.decrypt_share: {
+ startActivity(sendWithChooserExcludingDecrypt(mText.getText().toString()));
+ break;
+ }
+ case R.id.decrypt_copy: {
+ copyToClipboard(mText.getText().toString());
+ break;
+ }
+ default: {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ return true;
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
index 897de8490..07b0a12d3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@@ -42,6 +41,7 @@ import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.SingletonResult;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
@@ -49,8 +49,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
@@ -59,15 +57,21 @@ import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter;
-import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
-import org.sufficientlysecure.keychain.ui.dialog.*;
+import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
+import org.sufficientlysecure.keychain.ui.dialog.AddSubkeyDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.AddUserIdDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyExpiryDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
+import java.util.Date;
-public class EditKeyFragment extends CryptoOperationFragment implements
- LoaderManager.LoaderCallbacks<Cursor> {
+public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyringParcel, OperationResult>
+ implements LoaderManager.LoaderCallbacks<Cursor> {
public static final String ARG_DATA_URI = "uri";
public static final String ARG_SAVE_KEYRING_PARCEL = "save_keyring_parcel";
@@ -149,7 +153,7 @@ public class EditKeyFragment extends CryptoOperationFragment implements
if (mDataUri == null) {
returnKeyringParcel();
} else {
- cryptoOperation(new CryptoInputParcel());
+ cryptoOperation(new CryptoInputParcel(new Date()));
}
}
}, new OnClickListener() {
@@ -190,7 +194,7 @@ public class EditKeyFragment extends CryptoOperationFragment implements
private void loadData(Uri dataUri) {
mDataUri = dataUri;
- Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
+ Log.i(Constants.TAG, "mDataUri: " + mDataUri);
// load the secret key ring. we do verify here that the passphrase is correct, so cached won't do
try {
@@ -415,15 +419,71 @@ public class EditKeyFragment extends CryptoOperationFragment implements
mSaveKeyringParcel.mRevokeSubKeys.add(keyId);
}
break;
- case EditSubkeyDialogFragment.MESSAGE_STRIP:
+ case EditSubkeyDialogFragment.MESSAGE_STRIP: {
+ SecretKeyType secretKeyType = mSubkeysAdapter.getSecretKeyType(position);
+ if (secretKeyType == SecretKeyType.GNU_DUMMY) {
+ // Key is already stripped; this is a no-op.
+ break;
+ }
+
SubkeyChange change = mSaveKeyringParcel.getSubkeyChange(keyId);
if (change == null) {
- mSaveKeyringParcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, null));
+ mSaveKeyringParcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, false));
break;
}
// toggle
change.mDummyStrip = !change.mDummyStrip;
+ if (change.mDummyStrip && change.mMoveKeyToCard) {
+ // User had chosen to divert key, but now wants to strip it instead.
+ change.mMoveKeyToCard = false;
+ }
+ break;
+ }
+ case EditSubkeyDialogFragment.MESSAGE_MOVE_KEY_TO_CARD: {
+ // TODO: enable later when Admin PIN handling is resolved
+ Notify.create(getActivity(),
+ "This feature will be available in an upcoming OpenKeychain version.",
+ Notify.Style.WARN).show();
break;
+
+// Activity activity = EditKeyFragment.this.getActivity();
+// SecretKeyType secretKeyType = mSubkeysAdapter.getSecretKeyType(position);
+// if (secretKeyType == SecretKeyType.DIVERT_TO_CARD ||
+// secretKeyType == SecretKeyType.GNU_DUMMY) {
+// Notify.create(activity, R.string.edit_key_error_bad_nfc_stripped, Notify.Style.ERROR)
+// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
+// break;
+// }
+// int algorithm = mSubkeysAdapter.getAlgorithm(position);
+// // these are the PGP constants for RSA_GENERAL, RSA_ENCRYPT and RSA_SIGN
+// if (algorithm != 1 && algorithm != 2 && algorithm != 3) {
+// Notify.create(activity, R.string.edit_key_error_bad_nfc_algo, Notify.Style.ERROR)
+// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
+// break;
+// }
+// if (mSubkeysAdapter.getKeySize(position) != 2048) {
+// Notify.create(activity, R.string.edit_key_error_bad_nfc_size, Notify.Style.ERROR)
+// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
+// break;
+// }
+//
+//
+// SubkeyChange change;
+// change = mSaveKeyringParcel.getSubkeyChange(keyId);
+// if (change == null) {
+// mSaveKeyringParcel.mChangeSubKeys.add(
+// new SubkeyChange(keyId, false, true)
+// );
+// break;
+// }
+// // toggle
+// change.mMoveKeyToCard = !change.mMoveKeyToCard;
+// if (change.mMoveKeyToCard && change.mDummyStrip) {
+// // User had chosen to strip key, but now wants to divert it.
+// change.mDummyStrip = false;
+// }
+// break;
+ }
}
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
}
@@ -521,7 +581,7 @@ public class EditKeyFragment extends CryptoOperationFragment implements
addSubkeyDialogFragment.show(getActivity().getSupportFragmentManager(), "addSubkeyDialog");
}
- private void returnKeyringParcel() {
+ protected void returnKeyringParcel() {
if (mSaveKeyringParcel.mAddUserIds.size() == 0) {
Notify.create(getActivity(), R.string.edit_key_error_add_identity, Notify.Style.ERROR).show();
return;
@@ -540,76 +600,6 @@ public class EditKeyFragment extends CryptoOperationFragment implements
getActivity().finish();
}
- @Override
- protected void cryptoOperation(CryptoInputParcel cryptoInput) {
-
- Log.d(Constants.TAG, "cryptoInput:\n" + cryptoInput);
- Log.d(Constants.TAG, "mSaveKeyringParcel:\n" + mSaveKeyringParcel);
-
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_saving),
- ProgressDialog.STYLE_HORIZONTAL,
- true,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (handlePendingMessage(message)) {
- return;
- }
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
-
- // get returned data bundle
- Bundle returnData = message.getData();
- if (returnData == null) {
- return;
- }
- final OperationResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null) {
- return;
- }
-
- // if bad -> display here!
- if (!result.success()) {
- result.createNotify(getActivity()).show();
- return;
- }
-
- // if good -> finish, return result to showkey and display there!
- Intent intent = new Intent();
- intent.putExtra(OperationResult.EXTRA_RESULT, result);
- getActivity().setResult(EditKeyActivity.RESULT_OK, intent);
- getActivity().finish();
-
- }
- }
- };
-
- // Send all information needed to service to import key in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING);
-
- // fill values for this action
- Bundle data = new Bundle();
- data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput);
- data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(getActivity());
-
- // start service with intent
- getActivity().startService(intent);
- }
-
/**
* Closes this activity, returning a result parcel with a single error log entry.
*/
@@ -624,4 +614,23 @@ public class EditKeyFragment extends CryptoOperationFragment implements
getActivity().finish();
}
+ @Override
+ public SaveKeyringParcel createOperationInput() {
+ return mSaveKeyringParcel;
+ }
+
+ @Override
+ public void onQueuedOperationSuccess(OperationResult result) {
+
+ // null-protected from Queueing*Fragment
+ Activity activity = getActivity();
+
+ // if good -> finish, return result to showkey and display there!
+ Intent intent = new Intent();
+ intent.putExtra(OperationResult.EXTRA_RESULT, result);
+ activity.setResult(EditKeyActivity.RESULT_OK, intent);
+ activity.finish();
+
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
new file mode 100644
index 000000000..4361705f9
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.base.BaseActivity;
+
+public class EncryptActivity extends BaseActivity {
+
+ // preselect ids, for internal use
+ public static final String EXTRA_SIGNATURE_KEY_ID = Constants.EXTRA_PREFIX + "EXTRA_SIGNATURE_KEY_ID";
+ public static final String EXTRA_ENCRYPTION_KEY_IDS = Constants.EXTRA_PREFIX + "EXTRA_SIGNATURE_KEY_IDS";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ Bundle extras = intent.getExtras();
+
+ if (extras == null) {
+ extras = new Bundle();
+ }
+
+ if (savedInstanceState == null) {
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+
+ // preselect keys given by intent
+ long signingKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID);
+ long[] encryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS);
+
+ Fragment modeFragment = EncryptModeAsymmetricFragment.newInstance(signingKeyId, encryptionKeyIds);
+ transaction.replace(R.id.encrypt_mode_container, modeFragment);
+ transaction.commit();
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.check_use_symmetric: {
+ item.setChecked(!item.isChecked());
+ setModeFragment(item.isChecked());
+ return true;
+ }
+ default: {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.encrypt_activity, menu);
+
+ Fragment frag =
+ getSupportFragmentManager().findFragmentById(R.id.encrypt_mode_container);
+ boolean isSymmetric = frag instanceof EncryptModeSymmetricFragment;
+ menu.findItem(R.id.check_use_symmetric).setChecked(isSymmetric);
+
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ private void setModeFragment(boolean symmetric) {
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ transaction.replace(R.id.encrypt_mode_container,
+ symmetric
+ ? EncryptModeSymmetricFragment.newInstance()
+ : EncryptModeAsymmetricFragment.newInstance(0, null)
+ );
+
+ // doesn't matter if the user doesn't look at the activity
+ transaction.commitAllowingStateLoss();
+ }
+
+ public EncryptModeFragment getModeFragment() {
+ return (EncryptModeFragment)
+ getSupportFragmentManager().findFragmentById(R.id.encrypt_mode_container);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java
index a6fad8881..84660ca7a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java
@@ -18,27 +18,36 @@
package org.sufficientlysecure.keychain.ui;
+
+import java.util.regex.Matcher;
+
+import android.app.Activity;
import android.content.Intent;
+import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker;
-
-import java.util.regex.Matcher;
+import org.sufficientlysecure.keychain.util.FileHelper;
public class EncryptDecryptOverviewFragment extends Fragment {
View mClipboardIcon;
+ private static final int REQUEST_CODE_INPUT = 0x00007003;
+
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@@ -74,31 +83,44 @@ public class EncryptDecryptOverviewFragment extends Fragment {
mDecryptFile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- Intent filesDecrypt = new Intent(getActivity(), DecryptFilesActivity.class);
- filesDecrypt.setAction(DecryptFilesActivity.ACTION_DECRYPT_DATA_OPEN);
- startActivity(filesDecrypt);
+ FileHelper.openDocument(EncryptDecryptOverviewFragment.this, null, "*/*", false, REQUEST_CODE_INPUT);
}
});
mDecryptFromClipboard.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- Intent clipboardDecrypt = new Intent(getActivity(), DecryptTextActivity.class);
- clipboardDecrypt.setAction(DecryptTextActivity.ACTION_DECRYPT_FROM_CLIPBOARD);
- startActivityForResult(clipboardDecrypt, 0);
+ decryptFromClipboard();
}
});
return view;
}
+ private void decryptFromClipboard() {
+
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ final CharSequence clipboardText = ClipboardReflection.getClipboardText(activity);
+ if (clipboardText == null || TextUtils.isEmpty(clipboardText)) {
+ Notify.create(activity, R.string.error_clipboard_empty, Style.ERROR).show();
+ return;
+ }
+
+ Intent clipboardDecrypt = new Intent(getActivity(), DecryptActivity.class);
+ clipboardDecrypt.setAction(DecryptActivity.ACTION_DECRYPT_FROM_CLIPBOARD);
+ startActivityForResult(clipboardDecrypt, 0);
+ }
+
@Override
public void onResume() {
super.onResume();
// get text from clipboard
- final CharSequence clipboardText =
- ClipboardReflection.getClipboardText(getActivity());
+ final CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity());
// if it's null, nothing to do here /o/
if (clipboardText == null) {
@@ -135,12 +157,23 @@ public class EncryptDecryptOverviewFragment extends Fragment {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
- // if a result has been returned, display a notify
- if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
- OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
- result.createNotify(getActivity()).show();
- } else {
- super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode != REQUEST_CODE_INPUT) {
+ return;
+ }
+
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ Uri uri = data.getData();
+ if (uri == null) {
+ Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR).show();
+ return;
+ }
+
+ Intent intent = new Intent(getActivity(), DecryptActivity.class);
+ intent.setAction(Intent.ACTION_VIEW);
+ intent.setData(uri);
+ startActivity(intent);
+
}
}
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java
index b3ec60890..136787984 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java
@@ -22,76 +22,37 @@ import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.intents.OpenKeychainIntents;
-import org.sufficientlysecure.keychain.ui.base.BaseActivity;
-import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.ArrayList;
-public class EncryptFilesActivity extends BaseActivity implements
- EncryptModeAsymmetricFragment.IAsymmetric, EncryptModeSymmetricFragment.ISymmetric,
- EncryptFilesFragment.IMode {
+public class EncryptFilesActivity extends EncryptActivity {
- /* Intents */
+ // Intents
public static final String ACTION_ENCRYPT_DATA = OpenKeychainIntents.ENCRYPT_DATA;
// enables ASCII Armor for file encryption when uri is given
public static final String EXTRA_ASCII_ARMOR = OpenKeychainIntents.ENCRYPT_EXTRA_ASCII_ARMOR;
- // preselect ids, for internal use
- public static final String EXTRA_SIGNATURE_KEY_ID = Constants.EXTRA_PREFIX + "EXTRA_SIGNATURE_KEY_ID";
- public static final String EXTRA_ENCRYPTION_KEY_IDS = Constants.EXTRA_PREFIX + "EXTRA_ENCRYPTION_IDS";
-
- Fragment mModeFragment;
- EncryptFilesFragment mEncryptFragment;
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setFullScreenDialogClose(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- finish();
- }
- }, false);
-
- // Handle intent actions
- handleActions(getIntent(), savedInstanceState);
- }
+ setFullScreenDialogClose(Activity.RESULT_OK, false);
- @Override
- protected void initLayout() {
- setContentView(R.layout.encrypt_files_activity);
- }
-
- /**
- * Handles all actions with this intent
- */
- private void handleActions(Intent intent, Bundle savedInstanceState) {
+ Intent intent = getIntent();
String action = intent.getAction();
- Bundle extras = intent.getExtras();
String type = intent.getType();
ArrayList<Uri> uris = new ArrayList<>();
- if (extras == null) {
- extras = new Bundle();
- }
-
if (intent.getData() != null) {
uris.add(intent.getData());
}
- /*
- * Android's Action
- */
-
// When sending to OpenKeychain Encrypt via share menu
if (Intent.ACTION_SEND.equals(action) && type != null) {
// Files via content provider, override uri and action
@@ -103,56 +64,19 @@ public class EncryptFilesActivity extends BaseActivity implements
uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
}
- long mSigningKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID);
- long[] mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS);
- boolean useArmor = extras.getBoolean(EXTRA_ASCII_ARMOR, false);
-
if (savedInstanceState == null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- mModeFragment = EncryptModeAsymmetricFragment.newInstance(mSigningKeyId, mEncryptionKeyIds);
- transaction.replace(R.id.encrypt_mode_container, mModeFragment, "mode");
-
- mEncryptFragment = EncryptFilesFragment.newInstance(uris, useArmor);
- transaction.replace(R.id.encrypt_file_container, mEncryptFragment, "files");
-
+ EncryptFilesFragment encryptFragment = EncryptFilesFragment.newInstance(uris);
+ transaction.replace(R.id.encrypt_file_container, encryptFragment);
transaction.commit();
-
- getSupportFragmentManager().executePendingTransactions();
}
- }
-
- @Override
- public void onModeChanged(boolean symmetric) {
- // switch fragments
- getSupportFragmentManager().beginTransaction()
- .replace(R.id.encrypt_mode_container,
- symmetric
- ? EncryptModeSymmetricFragment.newInstance()
- : EncryptModeAsymmetricFragment.newInstance(0, null)
- )
- .commitAllowingStateLoss();
- getSupportFragmentManager().executePendingTransactions();
- }
-
- @Override
- public void onSignatureKeyIdChanged(long signatureKeyId) {
- mEncryptFragment.setSigningKeyId(signatureKeyId);
- }
- @Override
- public void onEncryptionKeyIdsChanged(long[] encryptionKeyIds) {
- mEncryptFragment.setEncryptionKeyIds(encryptionKeyIds);
}
@Override
- public void onEncryptionUserIdsChanged(String[] encryptionUserIds) {
- mEncryptFragment.setEncryptionUserIds(encryptionUserIds);
- }
-
- @Override
- public void onPassphraseChanged(Passphrase passphrase) {
- mEncryptFragment.setPassphrase(passphrase);
+ protected void initLayout() {
+ setContentView(R.layout.encrypt_files_activity);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
index 458810541..8572a5712 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
@@ -17,8 +17,17 @@
package org.sufficientlysecure.keychain.ui;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
import android.app.Activity;
-import android.app.ProgressDialog;
+import android.content.ClipData;
+import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -26,8 +35,7 @@ import android.graphics.Point;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
+import android.support.v4.app.FragmentActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@@ -41,106 +49,74 @@ import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
-import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.pgp.PgpConstants;
+import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration;
-import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
+import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
+import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.ShareHelper;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class EncryptFilesFragment extends CryptoOperationFragment {
-
- public interface IMode {
- public void onModeChanged(boolean symmetric);
- }
+public class EncryptFilesFragment
+ extends CachingCryptoOperationFragment<SignEncryptParcel, SignEncryptResult> {
+ public static final String ARG_DELETE_AFTER_ENCRYPT = "delete_after_encrypt";
+ public static final String ARG_ENCRYPT_FILENAMES = "encrypt_filenames";
+ public static final String ARG_USE_COMPRESSION = "use_compression";
public static final String ARG_USE_ASCII_ARMOR = "use_ascii_armor";
public static final String ARG_URIS = "uris";
- private static final int REQUEST_CODE_INPUT = 0x00007003;
+ public static final int REQUEST_CODE_INPUT = 0x00007003;
private static final int REQUEST_CODE_OUTPUT = 0x00007007;
- private IMode mModeInterface;
-
- private boolean mSymmetricMode = false;
- private boolean mUseArmor = false;
- private boolean mUseCompression = true;
- private boolean mDeleteAfterEncrypt = false;
- private boolean mShareAfterEncrypt = false;
- private boolean mEncryptFilenames = true;
+ private boolean mUseArmor;
+ private boolean mUseCompression;
+ private boolean mDeleteAfterEncrypt;
+ private boolean mEncryptFilenames;
private boolean mHiddenRecipients = false;
- private long mEncryptionKeyIds[] = null;
- private String mEncryptionUserIds[] = null;
- private long mSigningKeyId = Constants.key.none;
- private Passphrase mPassphrase = new Passphrase();
+ private AfterEncryptAction mAfterEncryptAction;
+ private enum AfterEncryptAction {
+ SAVE, SHARE, COPY;
+ }
- private ArrayList<Uri> mOutputUris = new ArrayList<>();
+ private ArrayList<Uri> mOutputUris;
private RecyclerView mSelectedFiles;
- ArrayList<FilesAdapter.ViewModel> mFilesModels;
FilesAdapter mFilesAdapter;
/**
* Creates new instance of this fragment
*/
- public static EncryptFilesFragment newInstance(ArrayList<Uri> uris, boolean useArmor) {
+ public static EncryptFilesFragment newInstance(ArrayList<Uri> uris) {
EncryptFilesFragment frag = new EncryptFilesFragment();
Bundle args = new Bundle();
- args.putBoolean(ARG_USE_ASCII_ARMOR, useArmor);
args.putParcelableArrayList(ARG_URIS, uris);
frag.setArguments(args);
return frag;
}
- public void setEncryptionKeyIds(long[] encryptionKeyIds) {
- mEncryptionKeyIds = encryptionKeyIds;
- }
-
- public void setEncryptionUserIds(String[] encryptionUserIds) {
- mEncryptionUserIds = encryptionUserIds;
- }
-
- public void setSigningKeyId(long signingKeyId) {
- mSigningKeyId = signingKeyId;
- }
-
- public void setPassphrase(Passphrase passphrase) {
- mPassphrase = passphrase;
- }
-
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
- try {
- mModeInterface = (IMode) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity + " must be IMode");
+ if ( ! (activity instanceof EncryptActivity) ) {
+ throw new AssertionError(activity + " must inherit from EncryptionActivity");
}
}
@@ -158,38 +134,70 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
mSelectedFiles.setLayoutManager(new LinearLayoutManager(getActivity()));
mSelectedFiles.setItemAnimator(new DefaultItemAnimator());
- mFilesModels = new ArrayList<>();
- mFilesAdapter = new FilesAdapter(getActivity(), mFilesModels, new View.OnClickListener() {
+ mFilesAdapter = new FilesAdapter(getActivity(), new View.OnClickListener() {
@Override
public void onClick(View v) {
addInputUri();
}
});
- ArrayList<Uri> inputUris = getArguments().getParcelableArrayList(ARG_URIS);
+ Bundle args = savedInstanceState == null ? getArguments() : savedInstanceState;
+
+ ArrayList<Uri> inputUris = args.getParcelableArrayList(ARG_URIS);
if (inputUris != null) {
mFilesAdapter.addAll(inputUris);
}
- mUseArmor = getArguments().getBoolean(ARG_USE_ASCII_ARMOR);
mSelectedFiles.setAdapter(mFilesAdapter);
return view;
}
@Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putBoolean(ARG_DELETE_AFTER_ENCRYPT, mDeleteAfterEncrypt);
+ outState.putBoolean(ARG_USE_ASCII_ARMOR, mUseArmor);
+ outState.putBoolean(ARG_USE_COMPRESSION, mUseCompression);
+ outState.putBoolean(ARG_ENCRYPT_FILENAMES, mEncryptFilenames);
+
+ outState.putParcelableArrayList(ARG_URIS, mFilesAdapter.getAsArrayList());
+ }
+
+ @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ Preferences prefs = Preferences.getPreferences(getActivity());
+
+ Bundle args = savedInstanceState == null ? getArguments() : savedInstanceState;
+ mDeleteAfterEncrypt = args.getBoolean(ARG_DELETE_AFTER_ENCRYPT, false);
+
+ if (args.containsKey(ARG_USE_ASCII_ARMOR)) {
+ mUseArmor = args.getBoolean(ARG_USE_ASCII_ARMOR, false);
+ } else {
+ mUseArmor = prefs.getUseArmor();
+ }
+
+ if (args.containsKey(ARG_USE_COMPRESSION)) {
+ mUseCompression = args.getBoolean(ARG_USE_COMPRESSION, true);
+ } else {
+ mUseCompression = prefs.getFilesUseCompression();
+ }
+
+ if (args.containsKey(ARG_ENCRYPT_FILENAMES)) {
+ mEncryptFilenames = args.getBoolean(ARG_ENCRYPT_FILENAMES, true);
+ } else {
+ mEncryptFilenames = prefs.getEncryptFilenames();
+ }
+
setHasOptionsMenu(true);
}
private void addInputUri() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- FileHelper.openDocument(EncryptFilesFragment.this, "*/*", true, REQUEST_CODE_INPUT);
- } else {
- FileHelper.openFile(EncryptFilesFragment.this, mFilesModels.isEmpty() ?
- null : mFilesModels.get(mFilesModels.size() - 1).inputUri,
- "*/*", REQUEST_CODE_INPUT);
- }
+ FileHelper.openDocument(EncryptFilesFragment.this, mFilesAdapter.getModelCount() == 0 ?
+ null : mFilesAdapter.getModelItem(mFilesAdapter.getModelCount() - 1).inputUri,
+ "*/*", true, REQUEST_CODE_INPUT);
}
private void addInputUri(Uri inputUri) {
@@ -209,49 +217,16 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
}
private void showOutputFileDialog() {
- if (mFilesModels.size() > 1 || mFilesModels.isEmpty()) {
+ if (mFilesAdapter.getModelCount() != 1) {
throw new IllegalStateException();
}
- FilesAdapter.ViewModel model = mFilesModels.get(0);
+ FilesAdapter.ViewModel model = mFilesAdapter.getModelItem(0);
String targetName =
(mEncryptFilenames ? "1" : FileHelper.getFilename(getActivity(), model.inputUri))
+ (mUseArmor ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN);
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
- File file = new File(model.inputUri.getPath());
- File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR;
- File targetFile = new File(parentDir, targetName);
- FileHelper.saveFile(this, getString(R.string.title_encrypt_to_file),
- getString(R.string.specify_file_to_encrypt_to), targetFile, REQUEST_CODE_OUTPUT);
- } else {
- FileHelper.saveDocument(this, "*/*", targetName, REQUEST_CODE_OUTPUT);
- }
- }
-
- private void encryptClicked(boolean share) {
- if (mFilesModels.isEmpty()) {
- Notify.create(getActivity(), R.string.error_no_file_selected,
- Notify.Style.ERROR).show(this);
- return;
- }
- if (share) {
- mOutputUris.clear();
- int filenameCounter = 1;
- for (FilesAdapter.ViewModel model : mFilesModels) {
- String targetName =
- (mEncryptFilenames ? String.valueOf(filenameCounter) : FileHelper.getFilename(getActivity(), model.inputUri))
- + (mUseArmor ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN);
- mOutputUris.add(TemporaryStorageProvider.createFile(getActivity(), targetName));
- filenameCounter++;
- }
- startEncrypt(true);
- } else {
- if (mFilesModels.size() > 1) {
- Notify.create(getActivity(), R.string.error_multi_not_supported,
- Notify.Style.ERROR).show(this);
- return;
- }
- showOutputFileDialog();
- }
+ Uri inputUri = model.inputUri;
+ FileHelper.saveDocument(this, targetName, inputUri,
+ R.string.title_encrypt_to_file, R.string.specify_file_to_encrypt_to, REQUEST_CODE_OUTPUT);
}
public void addFile(Intent data) {
@@ -276,41 +251,49 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.encrypt_file_fragment, menu);
+
+ menu.findItem(R.id.check_delete_after_encrypt).setChecked(mDeleteAfterEncrypt);
+ menu.findItem(R.id.check_use_armor).setChecked(mUseArmor);
+ menu.findItem(R.id.check_enable_compression).setChecked(mUseCompression);
+ menu.findItem(R.id.check_encrypt_filenames).setChecked(mEncryptFilenames);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- if (item.isCheckable()) {
- item.setChecked(!item.isChecked());
- }
switch (item.getItemId()) {
case R.id.encrypt_save: {
- encryptClicked(false);
+ hideKeyboard();
+ mAfterEncryptAction = AfterEncryptAction.SAVE;
+ cryptoOperation(new CryptoInputParcel(new Date()));
break;
}
case R.id.encrypt_share: {
- encryptClicked(true);
+ hideKeyboard();
+ mAfterEncryptAction = AfterEncryptAction.SHARE;
+ cryptoOperation(new CryptoInputParcel(new Date()));
break;
}
- case R.id.check_use_symmetric: {
- mSymmetricMode = item.isChecked();
- mModeInterface.onModeChanged(mSymmetricMode);
+ case R.id.encrypt_copy: {
+ hideKeyboard();
+ mAfterEncryptAction = AfterEncryptAction.COPY;
+ cryptoOperation(new CryptoInputParcel(new Date()));
break;
}
case R.id.check_use_armor: {
- mUseArmor = item.isChecked();
+ toggleUseArmor(item, !item.isChecked());
break;
}
case R.id.check_delete_after_encrypt: {
+ item.setChecked(!item.isChecked());
mDeleteAfterEncrypt = item.isChecked();
break;
}
case R.id.check_enable_compression: {
- mUseCompression = item.isChecked();
+ toggleEnableCompression(item, !item.isChecked());
break;
}
case R.id.check_encrypt_filenames: {
- mEncryptFilenames = item.isChecked();
+ toggleEncryptFilenamesCheck(item, !item.isChecked());
break;
}
// case R.id.check_hidden_recipients: {
@@ -325,111 +308,282 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
return true;
}
- protected boolean inputIsValid() {
- // file checks
+ public void toggleUseArmor(MenuItem item, final boolean useArmor) {
- if (mFilesModels.isEmpty()) {
- Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR)
- .show(this);
- return false;
- } else if (mFilesModels.size() > 1 && !mShareAfterEncrypt) {
- Log.e(Constants.TAG, "Aborting: mInputUris.size() > 1 && !mShareAfterEncrypt");
- // This should be impossible...
- return false;
- } else if (mFilesModels.size() != mOutputUris.size()) {
- Log.e(Constants.TAG, "Aborting: mInputUris.size() != mOutputUris.size()");
- // This as well
- return false;
- }
+ mUseArmor = useArmor;
+ item.setChecked(useArmor);
- if (mSymmetricMode) {
- // symmetric encryption checks
+ Notify.create(getActivity(), useArmor
+ ? R.string.snack_armor_on
+ : R.string.snack_armor_off,
+ Notify.LENGTH_LONG, Style.OK, new ActionListener() {
+ @Override
+ public void onAction() {
+ Preferences.getPreferences(getActivity()).setUseArmor(useArmor);
+ Notify.create(getActivity(), useArmor
+ ? R.string.snack_armor_on
+ : R.string.snack_armor_off,
+ Notify.LENGTH_SHORT, Style.OK, null, R.string.btn_saved)
+ .show(EncryptFilesFragment.this, false);
+ }
+ }, R.string.btn_save_default).show(this);
- if (mPassphrase == null) {
- Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR)
- .show(this);
- return false;
- }
- if (mPassphrase.isEmpty()) {
- Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR)
- .show(this);
- return false;
- }
+ }
- } else {
- // asymmetric encryption checks
+ public void toggleEnableCompression(MenuItem item, final boolean compress) {
- boolean gotEncryptionKeys = (mEncryptionKeyIds != null
- && mEncryptionKeyIds.length > 0);
+ mUseCompression = compress;
+ item.setChecked(compress);
- // Files must be encrypted, only text can be signed-only right now
- if (!gotEncryptionKeys) {
- Notify.create(getActivity(), R.string.select_encryption_key, Notify.Style.ERROR)
- .show(this);
- return false;
+ Notify.create(getActivity(), compress
+ ? R.string.snack_compression_on
+ : R.string.snack_compression_off,
+ Notify.LENGTH_LONG, Style.OK, new ActionListener() {
+ @Override
+ public void onAction() {
+ Preferences.getPreferences(getActivity()).setFilesUseCompression(compress);
+ Notify.create(getActivity(), compress
+ ? R.string.snack_compression_on
+ : R.string.snack_compression_off,
+ Notify.LENGTH_SHORT, Style.OK, null, R.string.btn_saved)
+ .show(EncryptFilesFragment.this, false);
+ }
+ }, R.string.btn_save_default).show(this);
+
+ }
+
+ public void toggleEncryptFilenamesCheck(MenuItem item, final boolean encryptFilenames) {
+
+ mEncryptFilenames = encryptFilenames;
+ item.setChecked(encryptFilenames);
+
+ Notify.create(getActivity(), encryptFilenames
+ ? R.string.snack_encrypt_filenames_on
+ : R.string.snack_encrypt_filenames_off,
+ Notify.LENGTH_LONG, Style.OK, new ActionListener() {
+ @Override
+ public void onAction() {
+ Preferences.getPreferences(getActivity()).setEncryptFilenames(encryptFilenames);
+ Notify.create(getActivity(), encryptFilenames
+ ? R.string.snack_encrypt_filenames_on
+ : R.string.snack_encrypt_filenames_off,
+ Notify.LENGTH_SHORT, Style.OK, null, R.string.btn_saved)
+ .show(EncryptFilesFragment.this, false);
}
- }
- return true;
+ }, R.string.btn_save_default).show(this);
+
}
- public void onEncryptSuccess(final SignEncryptResult result) {
+ @Override
+ public void onQueuedOperationSuccess(final SignEncryptResult result) {
+ super.onQueuedOperationSuccess(result);
+
+ hideKeyboard();
+
+ // protected by Queueing*Fragment
+ FragmentActivity activity = getActivity();
+
if (mDeleteAfterEncrypt) {
+ // TODO make behavior coherent here
DeleteFileDialogFragment deleteFileDialog =
DeleteFileDialogFragment.newInstance(mFilesAdapter.getAsArrayList());
deleteFileDialog.setOnDeletedListener(new DeleteFileDialogFragment.OnDeletedListener() {
@Override
public void onDeleted() {
- if (mShareAfterEncrypt) {
+ if (mAfterEncryptAction == AfterEncryptAction.SHARE) {
// Share encrypted message/file
startActivity(sendWithChooserExcludingEncrypt());
} else {
+ Activity activity = getActivity();
+ if (activity == null) {
+ // it's gone, there's nothing we can do here
+ return;
+ }
// Save encrypted file
- result.createNotify(getActivity()).show();
+ result.createNotify(activity).show();
}
}
});
- deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
+ deleteFileDialog.show(activity.getSupportFragmentManager(), "deleteDialog");
} else {
- if (mShareAfterEncrypt) {
- // Share encrypted message/file
- startActivity(sendWithChooserExcludingEncrypt());
- } else {
- // Save encrypted file
- result.createNotify(getActivity()).show();
+
+ switch (mAfterEncryptAction) {
+
+ case SHARE:
+ // Share encrypted message/file
+ startActivity(sendWithChooserExcludingEncrypt());
+ break;
+
+ case COPY:
+
+ ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
+ if (clipMan == null) {
+ Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR).show();
+ break;
+ }
+ ClipData clip = new ClipData(getString(R.string.label_clip_title),
+ // make available as application/pgp-encrypted
+ new String[] { "text/plain" },
+ new ClipData.Item(mOutputUris.get(0))
+ );
+ clipMan.setPrimaryClip(clip);
+ result.createNotify(activity).show();
+ break;
+
+ case SAVE:
+ // Encrypted file was saved already, just show notification
+ result.createNotify(activity).show();
+ break;
}
}
+
+ }
+
+ // prepares mOutputUris, either directly and returns false, or indirectly
+ // which returns true and will call cryptoOperation after mOutputUris has
+ // been set at a later point.
+ private boolean prepareOutputStreams() {
+
+ switch (mAfterEncryptAction) {
+ default:
+ case SHARE:
+ mOutputUris = new ArrayList<>();
+ int filenameCounter = 1;
+ for (FilesAdapter.ViewModel model : mFilesAdapter.mDataset) {
+ String targetName = (mEncryptFilenames
+ ? String.valueOf(filenameCounter) : FileHelper.getFilename(getActivity(), model.inputUri))
+ + (mUseArmor ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN);
+ mOutputUris.add(TemporaryStorageProvider.createFile(getActivity(), targetName));
+ filenameCounter++;
+ }
+ return false;
+
+ case SAVE:
+ if (mFilesAdapter.getModelCount() > 1) {
+ Notify.create(getActivity(), R.string.error_multi_files, Notify.Style.ERROR).show(this);
+ return true;
+ }
+ showOutputFileDialog();
+ return true;
+
+ case COPY:
+ // nothing to do here, but make sure
+ if (mFilesAdapter.getModelCount() > 1) {
+ Notify.create(getActivity(), R.string.error_multi_clipboard, Notify.Style.ERROR).show(this);
+ return true;
+ }
+ mOutputUris = new ArrayList<>();
+ String targetName = (mEncryptFilenames
+ ? String.valueOf(1) : FileHelper.getFilename(getActivity(),
+ mFilesAdapter.getModelItem(0).inputUri)) + Constants.FILE_EXTENSION_ASC;
+ mOutputUris.add(TemporaryStorageProvider.createFile(getActivity(), targetName, "text/plain"));
+ return false;
+ }
+
}
- protected SignEncryptParcel createEncryptBundle() {
+ public SignEncryptParcel createOperationInput() {
+
+ SignEncryptParcel actionsParcel = getCachedActionsParcel();
+
+ // we have three cases here: nothing cached, cached except output, fully cached
+ if (actionsParcel == null) {
+
+ // clear output uris for now, they will be created by prepareOutputStreams later
+ mOutputUris = null;
+
+ actionsParcel = createIncompleteCryptoInput();
+ // this is null if invalid, just return in that case
+ if (actionsParcel == null) {
+ return null;
+ }
+
+ cacheActionsParcel(actionsParcel);
+
+ }
+
+ // if it's incomplete, prepare output streams
+ if (actionsParcel.isIncomplete()) {
+ // if this is still null, prepare output streams again
+ if (mOutputUris == null) {
+ // this may interrupt the flow, and call us again from onActivityResult
+ if (prepareOutputStreams()) {
+ return null;
+ }
+ }
+
+ actionsParcel.addOutputUris(mOutputUris);
+ cacheActionsParcel(actionsParcel);
+
+ }
+
+ return actionsParcel;
+
+ }
+
+ protected SignEncryptParcel createIncompleteCryptoInput() {
+
+ if (mFilesAdapter.getModelCount() == 0) {
+ Notify.create(getActivity(), R.string.error_no_file_selected, Notify.Style.ERROR).show(this);
+ return null;
+ }
+
// fill values for this action
SignEncryptParcel data = new SignEncryptParcel();
data.addInputUris(mFilesAdapter.getAsArrayList());
- data.addOutputUris(mOutputUris);
if (mUseCompression) {
- data.setCompressionId(PgpConstants.sPreferredCompressionAlgorithms.get(0));
+ data.setCompressionAlgorithm(
+ PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.USE_DEFAULT);
} else {
- data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED);
+ data.setCompressionAlgorithm(
+ PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.UNCOMPRESSED);
}
data.setHiddenRecipients(mHiddenRecipients);
- data.setEnableAsciiArmorOutput(mUseArmor);
- data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
- data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
+ data.setEnableAsciiArmorOutput(mAfterEncryptAction == AfterEncryptAction.COPY || mUseArmor);
+ data.setSymmetricEncryptionAlgorithm(
+ PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT);
+ data.setSignatureHashAlgorithm(
+ PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT);
+
+ EncryptActivity encryptActivity = (EncryptActivity) getActivity();
+ EncryptModeFragment modeFragment = encryptActivity.getModeFragment();
- if (mSymmetricMode) {
- Log.d(Constants.TAG, "Symmetric encryption enabled!");
- Passphrase passphrase = mPassphrase;
+ if (modeFragment.isAsymmetric()) {
+ long[] encryptionKeyIds = modeFragment.getAsymmetricEncryptionKeyIds();
+ long signingKeyId = modeFragment.getAsymmetricSigningKeyId();
+
+ boolean gotEncryptionKeys = (encryptionKeyIds != null && encryptionKeyIds.length > 0);
+
+ if (!gotEncryptionKeys && signingKeyId != 0) {
+ Notify.create(getActivity(), R.string.error_detached_signature, Notify.Style.ERROR).show(this);
+ return null;
+ }
+ if (!gotEncryptionKeys) {
+ Notify.create(getActivity(), R.string.select_encryption_key, Notify.Style.ERROR).show(this);
+ return null;
+ }
+
+ data.setEncryptionMasterKeyIds(encryptionKeyIds);
+ data.setSignatureMasterKeyId(signingKeyId);
+ } else {
+ Passphrase passphrase = modeFragment.getSymmetricPassphrase();
+ if (passphrase == null) {
+ Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR)
+ .show(this);
+ return null;
+ }
if (passphrase.isEmpty()) {
- passphrase = null;
+ Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR)
+ .show(this);
+ return null;
}
data.setSymmetricPassphrase(passphrase);
- } else {
- data.setEncryptionMasterKeyIds(mEncryptionKeyIds);
- data.setSignatureMasterKeyId(mSigningKeyId);
}
+
return data;
}
@@ -442,7 +596,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
// we don't want to encrypt the encrypted, no inception ;)
String[] blacklist = new String[]{
- Constants.PACKAGE_NAME + ".ui.EncryptFileActivity",
+ Constants.PACKAGE_NAME + ".ui.EncryptFilesActivity",
"org.thialfihar.android.apg.ui.EncryptActivity"
};
@@ -461,81 +615,28 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
}
sendIntent.setType(Constants.ENCRYPTED_FILES_MIME);
- if (!mSymmetricMode && mEncryptionUserIds != null) {
- Set<String> users = new HashSet<>();
- for (String user : mEncryptionUserIds) {
- KeyRing.UserId userId = KeyRing.splitUserId(user);
- if (userId.email != null) {
- users.add(userId.email);
- }
- }
- sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()]));
+ EncryptActivity modeInterface = (EncryptActivity) getActivity();
+ EncryptModeFragment modeFragment = modeInterface.getModeFragment();
+ if (!modeFragment.isAsymmetric()) {
+ return sendIntent;
}
- return sendIntent;
- }
-
- public void startEncrypt(boolean share) {
- mShareAfterEncrypt = share;
- cryptoOperation();
- }
- @Override
- protected void cryptoOperation(CryptoInputParcel cryptoInput) {
-
- if (!inputIsValid()) {
- // Notify was created by inputIsValid.
- Log.d(Constants.TAG, "Input not valid!");
- return;
+ String[] encryptionUserIds = modeFragment.getAsymmetricEncryptionUserIds();
+ if (encryptionUserIds == null) {
+ return sendIntent;
}
- Log.d(Constants.TAG, "Input valid!");
-
- // Send all information needed to service to edit key in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT);
-
- final SignEncryptParcel input = createEncryptBundle();
-
- Bundle data = new Bundle();
- data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, input);
- data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after encrypting is done in KeychainIntentService
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_encrypting),
- ProgressDialog.STYLE_HORIZONTAL,
- true,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- // handle pending messages
- if (handlePendingMessage(message)) {
- return;
- }
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- SignEncryptResult result =
- message.getData().getParcelable(SignEncryptResult.EXTRA_RESULT);
- if (result.success()) {
- onEncryptSuccess(result);
- } else {
- result.createNotify(getActivity()).show();
- }
- }
+ Set<String> users = new HashSet<>();
+ for (String user : encryptionUserIds) {
+ KeyRing.UserId userId = KeyRing.splitUserId(user);
+ if (userId.email != null) {
+ users.add(userId.email);
}
- };
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- serviceHandler.showProgressDialog(getActivity());
+ }
+ // pass trough email addresses as extra for email applications
+ sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()]));
- // start service with intent
- getActivity().startService(intent);
+ return sendIntent;
}
@Override
@@ -550,9 +651,11 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
case REQUEST_CODE_OUTPUT: {
// This happens after output file was selected, so start our operation
if (resultCode == Activity.RESULT_OK && data != null) {
- mOutputUris.clear();
+ mOutputUris = new ArrayList<>(1);
mOutputUris.add(data.getData());
- startEncrypt(false);
+ // make sure this is correct at this point
+ mAfterEncryptAction = AfterEncryptAction.SAVE;
+ cryptoOperation(new CryptoInputParcel(new Date()));
}
return;
}
@@ -640,9 +743,9 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
}
// Provide a suitable constructor (depends on the kind of dataset)
- public FilesAdapter(Activity activity, List<ViewModel> myDataset, View.OnClickListener onFooterClickListener) {
+ public FilesAdapter(Activity activity, View.OnClickListener onFooterClickListener) {
mActivity = activity;
- mDataset = myDataset;
+ mDataset = new ArrayList<>();
mFooterOnClickListener = onFooterClickListener;
}
@@ -696,7 +799,8 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
- return mDataset.size() + 1;
+ // one extra for the footer!
+ return mDataset.size() +1;
}
@Override
@@ -727,7 +831,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
for (Uri inputUri : inputUris) {
ViewModel newModel = new ViewModel(mActivity, inputUri);
if (mDataset.contains(newModel)) {
- Log.e(Constants.TAG, "Skipped duplicate " + inputUri.toString());
+ Log.e(Constants.TAG, "Skipped duplicate " + inputUri);
} else {
mDataset.add(newModel);
}
@@ -736,6 +840,14 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
}
}
+ public int getModelCount() {
+ return mDataset.size();
+ }
+
+ public ViewModel getModelItem(int position) {
+ return mDataset.get(position);
+ }
+
public void remove(ViewModel model) {
int position = mDataset.indexOf(model);
mDataset.remove(position);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java
index 6f56f2dc4..355c649e7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java
@@ -17,18 +17,15 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.Activity;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ViewAnimator;
-import com.tokenautocomplete.TokenCompleteTextView;
-
+import com.tokenautocomplete.TokenCompleteTextView.TokenListener;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
@@ -38,39 +35,21 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter.KeyItem;
import org.sufficientlysecure.keychain.ui.widget.EncryptKeyCompletionView;
import org.sufficientlysecure.keychain.ui.widget.KeySpinner;
+import org.sufficientlysecure.keychain.ui.widget.KeySpinner.OnKeyChangedListener;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
-public class EncryptModeAsymmetricFragment extends Fragment {
-
- public interface IAsymmetric {
-
- public void onSignatureKeyIdChanged(long signatureKeyId);
-
- public void onEncryptionKeyIdsChanged(long[] encryptionKeyIds);
-
- public void onEncryptionUserIdsChanged(String[] encryptionUserIds);
- }
+public class EncryptModeAsymmetricFragment extends EncryptModeFragment {
ProviderHelper mProviderHelper;
- // view
- private KeySpinner mSign;
+ private KeySpinner mSignKeySpinner;
private EncryptKeyCompletionView mEncryptKeyView;
- // model
- private IAsymmetric mEncryptInterface;
-
-// @Override
-// public void updateUi() {
-// if (mSign != null) {
-// mSign.setSelectedKeyId(mEncryptInterface.getSignatureKey());
-// }
-// }
-
public static final String ARG_SINGATURE_KEY_ID = "signature_key_id";
public static final String ARG_ENCRYPTION_KEY_IDS = "encryption_key_ids";
@@ -89,16 +68,6 @@ public class EncryptModeAsymmetricFragment extends Fragment {
return frag;
}
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- try {
- mEncryptInterface = (IAsymmetric) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity + " must implement IAsymmetric");
- }
- }
-
/**
* Inflate the layout for this fragment
*/
@@ -106,15 +75,38 @@ public class EncryptModeAsymmetricFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.encrypt_asymmetric_fragment, container, false);
- mSign = (KeySpinner) view.findViewById(R.id.sign);
- mSign.setOnKeyChangedListener(new KeySpinner.OnKeyChangedListener() {
+ mSignKeySpinner = (KeySpinner) view.findViewById(R.id.sign);
+ mEncryptKeyView = (EncryptKeyCompletionView) view.findViewById(R.id.recipient_list);
+ mEncryptKeyView.setThreshold(1); // Start working from first character
+
+ final ViewAnimator vSignatureIcon = (ViewAnimator) view.findViewById(R.id.result_signature_icon);
+ mSignKeySpinner.setOnKeyChangedListener(new OnKeyChangedListener() {
@Override
public void onKeyChanged(long masterKeyId) {
- mEncryptInterface.onSignatureKeyIdChanged(masterKeyId);
+ int child = masterKeyId != Constants.key.none ? 1 : 0;
+ if (vSignatureIcon.getDisplayedChild() != child) {
+ vSignatureIcon.setDisplayedChild(child);
+ }
+ }
+ });
+
+ final ViewAnimator vEncryptionIcon = (ViewAnimator) view.findViewById(R.id.result_encryption_icon);
+ mEncryptKeyView.setTokenListener(new TokenListener() {
+ @Override
+ public void onTokenAdded(Object o) {
+ if (vEncryptionIcon.getDisplayedChild() != 1) {
+ vEncryptionIcon.setDisplayedChild(1);
+ }
+ }
+
+ @Override
+ public void onTokenRemoved(Object o) {
+ int child = mEncryptKeyView.getObjects().isEmpty() ? 0 : 1;
+ if (vEncryptionIcon.getDisplayedChild() != child) {
+ vEncryptionIcon.setDisplayedChild(child);
+ }
}
});
- mEncryptKeyView = (EncryptKeyCompletionView) view.findViewById(R.id.recipient_list);
- mEncryptKeyView.setThreshold(1); // Start working from first character
return view;
}
@@ -124,39 +116,28 @@ public class EncryptModeAsymmetricFragment extends Fragment {
super.onActivityCreated(savedInstanceState);
mProviderHelper = new ProviderHelper(getActivity());
- // preselect keys given
- long signatureKeyId = getArguments().getLong(ARG_SINGATURE_KEY_ID);
- long[] encryptionKeyIds = getArguments().getLongArray(ARG_ENCRYPTION_KEY_IDS);
- preselectKeys(signatureKeyId, encryptionKeyIds);
-
- mEncryptKeyView.setTokenListener(new TokenCompleteTextView.TokenListener() {
- @Override
- public void onTokenAdded(Object token) {
- if (token instanceof KeyItem) {
- updateEncryptionKeys();
- }
+ // preselect keys given, from state or arguments
+ if (savedInstanceState == null) {
+ Long signatureKeyId = getArguments().getLong(ARG_SINGATURE_KEY_ID);
+ if (signatureKeyId == Constants.key.none) {
+ signatureKeyId = null;
}
+ long[] encryptionKeyIds = getArguments().getLongArray(ARG_ENCRYPTION_KEY_IDS);
+ preselectKeys(signatureKeyId, encryptionKeyIds);
+ }
- @Override
- public void onTokenRemoved(Object token) {
- if (token instanceof KeyItem) {
- updateEncryptionKeys();
- }
- }
- });
}
/**
* If an Intent gives a signatureMasterKeyId and/or encryptionMasterKeyIds, preselect those!
*/
- private void preselectKeys(long signatureKeyId, long[] encryptionKeyIds) {
- if (signatureKeyId != Constants.key.none) {
+ private void preselectKeys(Long signatureKeyId, long[] encryptionKeyIds) {
+ if (signatureKeyId != null) {
try {
CachedPublicKeyRing keyring = mProviderHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(signatureKeyId));
if (keyring.hasAnySecret()) {
- mEncryptInterface.onSignatureKeyIdChanged(keyring.getMasterKeyId());
- mSign.setSelectedKeyId(signatureKeyId);
+ mSignKeySpinner.setPreSelectedKeyId(signatureKeyId);
}
} catch (PgpKeyNotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
@@ -175,27 +156,55 @@ public class EncryptModeAsymmetricFragment extends Fragment {
}
// This is to work-around a rendering bug in TokenCompleteTextView
mEncryptKeyView.requestFocus();
- updateEncryptionKeys();
}
}
- private void updateEncryptionKeys() {
- List<Object> objects = mEncryptKeyView.getObjects();
+ @Override
+ public boolean isAsymmetric() {
+ return true;
+ }
+
+ @Override
+ public long getAsymmetricSigningKeyId() {
+ return mSignKeySpinner.getSelectedKeyId();
+ }
+
+ @Override
+ public long[] getAsymmetricEncryptionKeyIds() {
List<Long> keyIds = new ArrayList<>();
- List<String> userIds = new ArrayList<>();
- for (Object object : objects) {
+ for (Object object : mEncryptKeyView.getObjects()) {
if (object instanceof KeyItem) {
keyIds.add(((KeyItem) object).mKeyId);
- userIds.add(((KeyItem) object).mUserIdFull);
}
}
+
long[] keyIdsArr = new long[keyIds.size()];
Iterator<Long> iterator = keyIds.iterator();
for (int i = 0; i < keyIds.size(); i++) {
keyIdsArr[i] = iterator.next();
}
- mEncryptInterface.onEncryptionKeyIdsChanged(keyIdsArr);
- mEncryptInterface.onEncryptionUserIdsChanged(userIds.toArray(new String[userIds.size()]));
+
+ return keyIdsArr;
+ }
+
+ @Override
+ public String[] getAsymmetricEncryptionUserIds() {
+
+ List<String> userIds = new ArrayList<>();
+ for (Object object : mEncryptKeyView.getObjects()) {
+ if (object instanceof KeyItem) {
+ userIds.add(((KeyItem) object).mUserIdFull);
+ }
+ }
+
+ return userIds.toArray(new String[userIds.size()]);
+
}
+
+ @Override
+ public Passphrase getSymmetricPassphrase() {
+ throw new UnsupportedOperationException("should never happen, this is a programming error!");
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeFragment.java
new file mode 100644
index 000000000..0b9672654
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeFragment.java
@@ -0,0 +1,19 @@
+package org.sufficientlysecure.keychain.ui;
+
+
+import android.support.v4.app.Fragment;
+
+import org.sufficientlysecure.keychain.util.Passphrase;
+
+
+public abstract class EncryptModeFragment extends Fragment {
+
+ public abstract boolean isAsymmetric();
+
+ public abstract long getAsymmetricSigningKeyId();
+ public abstract long[] getAsymmetricEncryptionKeyIds();
+ public abstract String[] getAsymmetricEncryptionUserIds();
+
+ public abstract Passphrase getSymmetricPassphrase();
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeSymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeSymmetricFragment.java
index 48b1f4983..b92a73731 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeSymmetricFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeSymmetricFragment.java
@@ -17,11 +17,7 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.Activity;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.text.Editable;
-import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -30,14 +26,7 @@ import android.widget.EditText;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Passphrase;
-public class EncryptModeSymmetricFragment extends Fragment {
-
- public interface ISymmetric {
-
- public void onPassphraseChanged(Passphrase passphrase);
- }
-
- private ISymmetric mEncryptInterface;
+public class EncryptModeSymmetricFragment extends EncryptModeFragment {
private EditText mPassphrase;
private EditText mPassphraseAgain;
@@ -55,52 +44,53 @@ public class EncryptModeSymmetricFragment extends Fragment {
}
@Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- try {
- mEncryptInterface = (ISymmetric) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString() + " must implement ISymmetric");
- }
- }
-
- /**
- * Inflate the layout for this fragment
- */
- @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.encrypt_symmetric_fragment, container, false);
mPassphrase = (EditText) view.findViewById(R.id.passphrase);
mPassphraseAgain = (EditText) view.findViewById(R.id.passphraseAgain);
- TextWatcher textWatcher = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
+ return view;
+ }
+
+ @Override
+ public boolean isAsymmetric() {
+ return false;
+ }
- @Override
- public void afterTextChanged(Editable s) {
- // update passphrase in EncryptActivity
- Passphrase p1 = new Passphrase(mPassphrase.getText());
- Passphrase p2 = new Passphrase(mPassphraseAgain.getText());
- boolean passesEquals = (p1.equals(p2));
+ @Override
+ public long getAsymmetricSigningKeyId() {
+ throw new UnsupportedOperationException("should never happen, this is a programming error!");
+ }
+
+ @Override
+ public long[] getAsymmetricEncryptionKeyIds() {
+ throw new UnsupportedOperationException("should never happen, this is a programming error!");
+ }
+
+ @Override
+ public String[] getAsymmetricEncryptionUserIds() {
+ throw new UnsupportedOperationException("should never happen, this is a programming error!");
+ }
+
+ @Override
+ public Passphrase getSymmetricPassphrase() {
+ Passphrase p1 = null, p2 = null;
+ try {
+ p1 = new Passphrase(mPassphrase.getText());
+ p2 = new Passphrase(mPassphraseAgain.getText());
+ if (!p1.equals(p2)) {
+ return null;
+ }
+ return new Passphrase(mPassphrase.getText());
+ } finally {
+ if (p1 != null) {
p1.removeFromMemory();
+ }
+ if (p2 != null) {
p2.removeFromMemory();
- if (passesEquals) {
- mEncryptInterface.onPassphraseChanged(new Passphrase(mPassphrase.getText()));
- } else {
- mEncryptInterface.onPassphraseChanged(null);
- }
}
- };
- mPassphrase.addTextChangedListener(textWatcher);
- mPassphraseAgain.addTextChangedListener(textWatcher);
-
- return view;
+ }
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java
index 52d098adc..a849cdf12 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java
@@ -18,22 +18,17 @@
package org.sufficientlysecure.keychain.ui;
+import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.intents.OpenKeychainIntents;
-import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.Passphrase;
-public class EncryptTextActivity extends BaseActivity implements
- EncryptModeAsymmetricFragment.IAsymmetric, EncryptModeSymmetricFragment.ISymmetric,
- EncryptTextFragment.IMode {
+public class EncryptTextActivity extends EncryptActivity {
/* Intents */
public static final String ACTION_ENCRYPT_TEXT = OpenKeychainIntents.ENCRYPT_TEXT;
@@ -41,40 +36,13 @@ public class EncryptTextActivity extends BaseActivity implements
/* EXTRA keys for input */
public static final String EXTRA_TEXT = OpenKeychainIntents.ENCRYPT_EXTRA_TEXT;
- // preselect ids, for internal use
- public static final String EXTRA_SIGNATURE_KEY_ID = Constants.EXTRA_PREFIX + "EXTRA_SIGNATURE_KEY_ID";
- public static final String EXTRA_ENCRYPTION_KEY_IDS = Constants.EXTRA_PREFIX + "EXTRA_SIGNATURE_KEY_IDS";
-
- Fragment mModeFragment;
- EncryptTextFragment mEncryptFragment;
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setFullScreenDialogClose(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- finish();
- }
- }, false);
-
- // Handle intent actions
- handleActions(getIntent(), savedInstanceState);
- }
-
- @Override
- protected void initLayout() {
- setContentView(R.layout.encrypt_text_activity);
- }
-
+ setFullScreenDialogClose(Activity.RESULT_OK, false);
- /**
- * Handles all actions with this intent
- *
- * @param intent
- */
- private void handleActions(Intent intent, Bundle savedInstanceState) {
+ Intent intent = getIntent();
String action = intent.getAction();
Bundle extras = intent.getExtras();
String type = intent.getType();
@@ -83,10 +51,6 @@ public class EncryptTextActivity extends BaseActivity implements
extras = new Bundle();
}
- /*
- * Android's Action
- */
-
// When sending to OpenKeychain Encrypt via share menu
if (Intent.ACTION_SEND.equals(action) && type != null) {
Log.logDebugBundle(extras, "extras");
@@ -108,55 +72,19 @@ public class EncryptTextActivity extends BaseActivity implements
textData = "";
}
- // preselect keys given by intent
- long mSigningKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID);
- long[] mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS);
-
if (savedInstanceState == null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- mModeFragment = EncryptModeAsymmetricFragment.newInstance(mSigningKeyId, mEncryptionKeyIds);
- transaction.replace(R.id.encrypt_mode_container, mModeFragment, "mode");
-
- mEncryptFragment = EncryptTextFragment.newInstance(textData);
- transaction.replace(R.id.encrypt_text_container, mEncryptFragment, "text");
-
+ EncryptTextFragment encryptFragment = EncryptTextFragment.newInstance(textData);
+ transaction.replace(R.id.encrypt_text_container, encryptFragment);
transaction.commit();
-
- getSupportFragmentManager().executePendingTransactions();
}
- }
-
- @Override
- public void onModeChanged(boolean symmetric) {
- // switch fragments
- getSupportFragmentManager().beginTransaction()
- .replace(R.id.encrypt_mode_container,
- symmetric
- ? EncryptModeSymmetricFragment.newInstance()
- : EncryptModeAsymmetricFragment.newInstance(0, null)
- )
- .commitAllowingStateLoss();
- getSupportFragmentManager().executePendingTransactions();
- }
-
- @Override
- public void onSignatureKeyIdChanged(long signatureKeyId) {
- mEncryptFragment.setSigningKeyId(signatureKeyId);
- }
- @Override
- public void onEncryptionKeyIdsChanged(long[] encryptionKeyIds) {
- mEncryptFragment.setEncryptionKeyIds(encryptionKeyIds);
}
@Override
- public void onEncryptionUserIdsChanged(String[] encryptionUserIds) {
- mEncryptFragment.setEncryptionUserIds(encryptionUserIds);
+ protected void initLayout() {
+ setContentView(R.layout.encrypt_text_activity);
}
- @Override
- public void onPassphraseChanged(Passphrase passphrase) {
- mEncryptFragment.setSymmetricPassphrase(passphrase);
- }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java
index 3f9147cc4..ab676285e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java
@@ -18,11 +18,11 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
-import android.app.ProgressDialog;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
@@ -33,67 +33,37 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.pgp.PgpConstants;
+import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
-import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
-import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.Passphrase;
+import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.ShareHelper;
+import java.util.Date;
import java.util.HashSet;
import java.util.Set;
-public class EncryptTextFragment extends CryptoOperationFragment {
-
- public interface IMode {
- public void onModeChanged(boolean symmetric);
- }
+public class EncryptTextFragment
+ extends CachingCryptoOperationFragment<SignEncryptParcel, SignEncryptResult> {
public static final String ARG_TEXT = "text";
+ public static final String ARG_USE_COMPRESSION = "use_compression";
- private IMode mModeInterface;
-
- private boolean mSymmetricMode = false;
- private boolean mShareAfterEncrypt = false;
- private boolean mUseCompression = true;
+ private boolean mShareAfterEncrypt;
+ private boolean mUseCompression;
private boolean mHiddenRecipients = false;
- private long mEncryptionKeyIds[] = null;
- private String mEncryptionUserIds[] = null;
- // TODO Constants.key.none? What's wrong with a null value?
- private long mSigningKeyId = Constants.key.none;
- private Passphrase mSymmetricPassphrase = new Passphrase();
private String mMessage = "";
- private TextView mText;
-
- public void setEncryptionKeyIds(long[] encryptionKeyIds) {
- mEncryptionKeyIds = encryptionKeyIds;
- }
-
- public void setEncryptionUserIds(String[] encryptionUserIds) {
- mEncryptionUserIds = encryptionUserIds;
- }
-
- public void setSigningKeyId(long signingKeyId) {
- mSigningKeyId = signingKeyId;
- }
-
- public void setSymmetricPassphrase(Passphrase passphrase) {
- mSymmetricPassphrase = passphrase;
- }
-
/**
* Creates new instance of this fragment
*/
@@ -110,10 +80,8 @@ public class EncryptTextFragment extends CryptoOperationFragment {
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
- try {
- mModeInterface = (IMode) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString() + " must implement IMode");
+ if ( ! (activity instanceof EncryptActivity) ) {
+ throw new AssertionError(activity + " must inherit from EncryptionActivity");
}
}
@@ -124,8 +92,8 @@ public class EncryptTextFragment extends CryptoOperationFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.encrypt_text_fragment, container, false);
- mText = (TextView) view.findViewById(R.id.encrypt_text_text);
- mText.addTextChangedListener(new TextWatcher() {
+ TextView textView = (TextView) view.findViewById(R.id.encrypt_text_text);
+ textView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -144,39 +112,53 @@ public class EncryptTextFragment extends CryptoOperationFragment {
// set initial text
if (mMessage != null) {
- mText.setText(mMessage);
+ textView.setText(mMessage);
}
return view;
}
@Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(ARG_USE_COMPRESSION, mUseCompression);
+ }
+
+ @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mMessage = getArguments().getString(ARG_TEXT);
+ if (savedInstanceState == null) {
+ mMessage = getArguments().getString(ARG_TEXT);
+ }
+
+ Preferences prefs = Preferences.getPreferences(getActivity());
+
+ Bundle args = savedInstanceState == null ? getArguments() : savedInstanceState;
+
+ mUseCompression = args.getBoolean(ARG_USE_COMPRESSION, true);
+ if (args.containsKey(ARG_USE_COMPRESSION)) {
+ mUseCompression = args.getBoolean(ARG_USE_COMPRESSION, true);
+ } else {
+ mUseCompression = prefs.getTextUseCompression();
+ }
setHasOptionsMenu(true);
+
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.encrypt_text_fragment, menu);
+
+ menu.findItem(R.id.check_enable_compression).setChecked(mUseCompression);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- if (item.isCheckable()) {
- item.setChecked(!item.isChecked());
- }
switch (item.getItemId()) {
- case R.id.check_use_symmetric: {
- mSymmetricMode = item.isChecked();
- mModeInterface.onModeChanged(mSymmetricMode);
- break;
- }
case R.id.check_enable_compression: {
- mUseCompression = item.isChecked();
+ toggleEnableCompression(item, !item.isChecked());
break;
}
// case R.id.check_hidden_recipients: {
@@ -185,11 +167,15 @@ public class EncryptTextFragment extends CryptoOperationFragment {
// break;
// }
case R.id.encrypt_copy: {
- startEncrypt(false);
+ hideKeyboard();
+ mShareAfterEncrypt = false;
+ cryptoOperation(new CryptoInputParcel(new Date()));
break;
}
case R.id.encrypt_share: {
- startEncrypt(true);
+ hideKeyboard();
+ mShareAfterEncrypt = true;
+ cryptoOperation(new CryptoInputParcel(new Date()));
break;
}
default: {
@@ -199,22 +185,36 @@ public class EncryptTextFragment extends CryptoOperationFragment {
return true;
}
+ public void toggleEnableCompression(MenuItem item, final boolean compress) {
+
+ mUseCompression = compress;
+ item.setChecked(compress);
+
+ Notify.create(getActivity(), compress
+ ? R.string.snack_compression_on
+ : R.string.snack_compression_off,
+ Notify.LENGTH_LONG, Style.OK, new ActionListener() {
+ @Override
+ public void onAction() {
+ Preferences.getPreferences(getActivity()).setTextUseCompression(compress);
+ Notify.create(getActivity(), compress
+ ? R.string.snack_compression_on
+ : R.string.snack_compression_off,
+ Notify.LENGTH_SHORT, Style.OK, null, R.string.btn_saved)
+ .show(EncryptTextFragment.this, false);
+ }
+ }, R.string.btn_save_default).show(this);
- protected void onEncryptSuccess(SignEncryptResult result) {
- if (mShareAfterEncrypt) {
- // Share encrypted message/file
- startActivity(sendWithChooserExcludingEncrypt(result.getResultBytes()));
- } else {
- // Copy to clipboard
- copyToClipboard(result.getResultBytes());
- result.createNotify(getActivity()).show();
- // Notify.create(EncryptTextActivity.this,
- // R.string.encrypt_sign_clipboard_successful, Notify.Style.OK)
- // .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment));
- }
}
- protected SignEncryptParcel createEncryptBundle() {
+ public SignEncryptParcel createOperationInput() {
+
+ if (mMessage == null || mMessage.isEmpty()) {
+ Notify.create(getActivity(), R.string.error_empty_text, Notify.Style.ERROR)
+ .show(this);
+ return null;
+ }
+
// fill values for this action
SignEncryptParcel data = new SignEncryptParcel();
@@ -222,33 +222,71 @@ public class EncryptTextFragment extends CryptoOperationFragment {
data.setCleartextSignature(true);
if (mUseCompression) {
- data.setCompressionId(PgpConstants.sPreferredCompressionAlgorithms.get(0));
+ data.setCompressionAlgorithm(
+ PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.USE_DEFAULT);
} else {
- data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED);
+ data.setCompressionAlgorithm(
+ PgpSecurityConstants.OpenKeychainCompressionAlgorithmTags.UNCOMPRESSED);
}
data.setHiddenRecipients(mHiddenRecipients);
- data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
- data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
+ data.setSymmetricEncryptionAlgorithm(
+ PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT);
+ data.setSignatureHashAlgorithm(
+ PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_DEFAULT);
// Always use armor for messages
data.setEnableAsciiArmorOutput(true);
- if (mSymmetricMode) {
- Log.d(Constants.TAG, "Symmetric encryption enabled!");
- Passphrase passphrase = mSymmetricPassphrase;
+ EncryptActivity modeInterface = (EncryptActivity) getActivity();
+ EncryptModeFragment modeFragment = modeInterface.getModeFragment();
+
+ if (modeFragment.isAsymmetric()) {
+ long[] encryptionKeyIds = modeFragment.getAsymmetricEncryptionKeyIds();
+ long signingKeyId = modeFragment.getAsymmetricSigningKeyId();
+
+ boolean gotEncryptionKeys = (encryptionKeyIds != null
+ && encryptionKeyIds.length > 0);
+
+ if (!gotEncryptionKeys && signingKeyId == Constants.key.none) {
+ Notify.create(getActivity(), R.string.error_no_encryption_or_signature_key, Notify.Style.ERROR)
+ .show(this);
+ return null;
+ }
+
+ data.setEncryptionMasterKeyIds(encryptionKeyIds);
+ data.setSignatureMasterKeyId(signingKeyId);
+ } else {
+ Passphrase passphrase = modeFragment.getSymmetricPassphrase();
+ if (passphrase == null) {
+ Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR)
+ .show(this);
+ return null;
+ }
if (passphrase.isEmpty()) {
- passphrase = null;
+ Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR)
+ .show(this);
+ return null;
}
data.setSymmetricPassphrase(passphrase);
- } else {
- data.setEncryptionMasterKeyIds(mEncryptionKeyIds);
- data.setSignatureMasterKeyId(mSigningKeyId);
}
return data;
}
- private void copyToClipboard(byte[] resultBytes) {
- ClipboardReflection.copyToClipboard(getActivity(), new String(resultBytes));
+ private void copyToClipboard(SignEncryptResult result) {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
+ if (clipMan == null) {
+ Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR).show();
+ return;
+ }
+
+ ClipData clip = ClipData.newPlainText(Constants.CLIPBOARD_LABEL, new String(result.getResultBytes()));
+ clipMan.setPrimaryClip(clip);
+ result.createNotify(activity).show();
}
/**
@@ -273,114 +311,44 @@ public class EncryptTextFragment extends CryptoOperationFragment {
sendIntent.setType(Constants.ENCRYPTED_TEXT_MIME);
sendIntent.putExtra(Intent.EXTRA_TEXT, new String(resultBytes));
- if (!mSymmetricMode && mEncryptionUserIds != null) {
- Set<String> users = new HashSet<>();
- for (String user : mEncryptionUserIds) {
- KeyRing.UserId userId = KeyRing.splitUserId(user);
- if (userId.email != null) {
- users.add(userId.email);
- }
- }
- // pass trough email addresses as extra for email applications
- sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()]));
+ EncryptActivity modeInterface = (EncryptActivity) getActivity();
+ EncryptModeFragment modeFragment = modeInterface.getModeFragment();
+ if (!modeFragment.isAsymmetric()) {
+ return sendIntent;
}
- return sendIntent;
- }
- protected boolean inputIsValid() {
- if (mMessage == null || mMessage.isEmpty()) {
- Notify.create(getActivity(), R.string.error_empty_text, Notify.Style.ERROR)
- .show(this);
- return false;
+ String[] encryptionUserIds = modeFragment.getAsymmetricEncryptionUserIds();
+ if (encryptionUserIds == null) {
+ return sendIntent;
}
- if (mSymmetricMode) {
- // symmetric encryption checks
-
- if (mSymmetricPassphrase == null) {
- Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR)
- .show(this);
- return false;
- }
- if (mSymmetricPassphrase.isEmpty()) {
- Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR)
- .show(this);
- return false;
- }
-
- } else {
- // asymmetric encryption checks
-
- boolean gotEncryptionKeys = (mEncryptionKeyIds != null
- && mEncryptionKeyIds.length > 0);
-
- if (!gotEncryptionKeys && mSigningKeyId == 0) {
- Notify.create(getActivity(), R.string.select_encryption_or_signature_key, Notify.Style.ERROR)
- .show(this);
- return false;
+ Set<String> users = new HashSet<>();
+ for (String user : encryptionUserIds) {
+ KeyRing.UserId userId = KeyRing.splitUserId(user);
+ if (userId.email != null) {
+ users.add(userId.email);
}
}
- return true;
- }
+ // pass trough email addresses as extra for email applications
+ sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()]));
-
- public void startEncrypt(boolean share) {
- mShareAfterEncrypt = share;
- cryptoOperation();
+ return sendIntent;
}
@Override
- protected void cryptoOperation(CryptoInputParcel cryptoInput) {
- if (!inputIsValid()) {
- // Notify was created by inputIsValid.
- return;
- }
+ public void onQueuedOperationSuccess(SignEncryptResult result) {
+ super.onQueuedOperationSuccess(result);
- // Send all information needed to service to edit key in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT);
-
- final SignEncryptParcel input = createEncryptBundle();
- final Bundle data = new Bundle();
- data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, input);
- data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after encrypting is done in KeychainIntentService
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_encrypting),
- ProgressDialog.STYLE_HORIZONTAL,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (handlePendingMessage(message)) {
- return;
- }
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- SignEncryptResult result =
- message.getData().getParcelable(SignEncryptResult.EXTRA_RESULT);
-
- if (result.success()) {
- onEncryptSuccess(result);
- } else {
- result.createNotify(getActivity()).show();
- }
- }
- }
- };
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+ hideKeyboard();
- // show progress dialog
- serviceHandler.showProgressDialog(getActivity());
+ if (mShareAfterEncrypt) {
+ // Share encrypted message/file
+ startActivity(sendWithChooserExcludingEncrypt(result.getResultBytes()));
+ } else {
+ // Copy to clipboard
+ copyToClipboard(result);
+ }
- // start service with intent
- getActivity().startService(intent);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java
index ac4b94d64..7a1e167bb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2015 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
@@ -51,14 +51,11 @@ public class HelpAboutFragment extends Fragment {
try {
String html = new Markdown4jProcessor().process(
getActivity().getResources().openRawResource(R.raw.help_about));
- aboutTextView.setHtmlFromString(html, true);
+ aboutTextView.setHtmlFromString(html, new HtmlTextView.LocalImageGetter());
} catch (IOException e) {
Log.e(Constants.TAG, "IOException", e);
}
- // no flickering when clicking textview for Android < 4
- aboutTextView.setTextColor(getResources().getColor(android.R.color.black));
-
return view;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpMarkdownFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpMarkdownFragment.java
index 97d39feb1..15098b8d6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpMarkdownFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpMarkdownFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2015 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,7 +17,6 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.TypedValue;
@@ -34,9 +33,6 @@ import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
public class HelpMarkdownFragment extends Fragment {
- private Activity mActivity;
-
- private int mHtmlFile;
public static final String ARG_MARKDOWN_RES = "htmlFile";
@@ -56,15 +52,13 @@ public class HelpMarkdownFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- mActivity = getActivity();
-
- mHtmlFile = getArguments().getInt(ARG_MARKDOWN_RES);
+ int mHtmlFile = getArguments().getInt(ARG_MARKDOWN_RES);
- ScrollView scroller = new ScrollView(mActivity);
- HtmlTextView text = new HtmlTextView(mActivity);
+ ScrollView scroller = new ScrollView(getActivity());
+ HtmlTextView text = new HtmlTextView(getActivity());
// padding
- int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, mActivity
+ int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, getActivity()
.getResources().getDisplayMetrics());
text.setPadding(padding, padding, padding, 0);
@@ -72,15 +66,13 @@ public class HelpMarkdownFragment extends Fragment {
// load markdown from raw resource
try {
- String html = new Markdown4jProcessor().process(getActivity().getResources().openRawResource(mHtmlFile));
- text.setHtmlFromString(html, true);
+ String html = new Markdown4jProcessor().process(
+ getActivity().getResources().openRawResource(mHtmlFile));
+ text.setHtmlFromString(html, new HtmlTextView.LocalImageGetter());
} catch (IOException e) {
Log.e(Constants.TAG, "IOException", e);
}
- // no flickering when clicking textview for Android < 4
- text.setTextColor(getResources().getColor(android.R.color.black));
-
return scroller;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 4cba62d5b..4ef6c40dc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -17,12 +17,11 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.ProgressDialog;
+import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
-import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.view.View;
import android.view.View.OnClickListener;
@@ -35,11 +34,10 @@ import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
-import org.sufficientlysecure.keychain.service.CloudImportService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
@@ -49,7 +47,8 @@ import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize
import java.io.IOException;
import java.util.ArrayList;
-public class ImportKeysActivity extends BaseNfcActivity {
+public class ImportKeysActivity extends BaseNfcActivity
+ implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
public static final String ACTION_IMPORT_KEY = OpenKeychainIntents.IMPORT_KEY;
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER = OpenKeychainIntents.IMPORT_KEY_FROM_KEYSERVER;
@@ -84,10 +83,17 @@ public class ImportKeysActivity extends BaseNfcActivity {
private Fragment mTopFragment;
private View mImportButton;
+ // for CryptoOperationHelper.Callback
+ private String mKeyserver;
+ private ArrayList<ParcelableKeyRing> mKeyList;
+
+ private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setFullScreenDialogClose(Activity.RESULT_CANCELED, true);
mImportButton = findViewById(R.id.import_import);
mImportButton.setOnClickListener(new OnClickListener() {
@Override
@@ -220,9 +226,9 @@ public class ImportKeysActivity extends BaseNfcActivity {
Notify.Style.WARN).show(mTopFragment);
// we just set the keyserver
startCloudFragment(savedInstanceState, null, false, keyserver);
- // it's not necessary to set the keyserver for ImportKeysListFragment since
- // it'll be taken care of by ImportKeysCloudFragment when the user clicks
- // the search button
+ // we don't set the keyserver for ImportKeysListFragment since
+ // it'll be set in the cloudSearchPrefs of ImportKeysCloudFragment
+ // which is used when the user clicks on the search button
startListFragment(savedInstanceState, null, null, null, null);
} else {
// we allow our users to edit the query if they wish
@@ -265,7 +271,7 @@ public class ImportKeysActivity extends BaseNfcActivity {
// 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) {
+ if (mListFragment != null) {
return;
}
@@ -285,7 +291,7 @@ public class ImportKeysActivity extends BaseNfcActivity {
// 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) {
+ if (mTopFragment != null) {
return;
}
@@ -312,11 +318,12 @@ public class ImportKeysActivity extends BaseNfcActivity {
* specified in user preferences
*/
- private void startCloudFragment(Bundle savedInstanceState, String query, boolean disableQueryEdit, String keyserver) {
+ private void startCloudFragment(Bundle savedInstanceState, String query, boolean disableQueryEdit, String
+ keyserver) {
// 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) {
+ if (mTopFragment != null) {
return;
}
@@ -342,7 +349,7 @@ public class ImportKeysActivity extends BaseNfcActivity {
}
}
- public void loadCallback(ImportKeysListFragment.LoaderState loaderState) {
+ public void loadCallback(final ImportKeysListFragment.LoaderState loaderState) {
mListFragment.loadNew(loaderState);
}
@@ -383,32 +390,20 @@ public class ImportKeysActivity extends BaseNfcActivity {
* Import keys with mImportData
*/
public void importKeys() {
- ImportKeysListFragment.LoaderState ls = mListFragment.getLoaderState();
- if (ls instanceof ImportKeysListFragment.BytesLoaderState) {
- Log.d(Constants.TAG, "importKeys started");
-
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(
- this,
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL,
- true,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- ImportKeysActivity.this.handleMessage(message);
- }
- };
- // TODO: Currently not using CloudImport here due to https://github.com/open-keychain/open-keychain/issues/1221
- // Send all information needed to service to import key in other thread
- Intent intent = new Intent(this, KeychainIntentService.class);
+ if (mListFragment.getSelectedEntries().size() == 0) {
+ Notify.create(this, R.string.error_nothing_import_selected, Notify.Style.ERROR)
+ .show((ViewGroup) findViewById(R.id.import_snackbar));
+ return;
+ }
- intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING);
+ mOperationHelper = new CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult>(
+ 1, this, this, R.string.progress_importing
+ );
- // fill values for this action
- Bundle data = new Bundle();
+ ImportKeysListFragment.LoaderState ls = mListFragment.getLoaderState();
+ if (ls instanceof ImportKeysListFragment.BytesLoaderState) {
+ Log.d(Constants.TAG, "importKeys started");
// get DATA from selected key entries
IteratorWithSize<ParcelableKeyRing> selectedEntries = mListFragment.getSelectedData();
@@ -423,46 +418,18 @@ public class ImportKeysActivity extends BaseNfcActivity {
new ParcelableFileCache<>(this, "key_import.pcl");
cache.writeCache(selectedEntries);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- serviceHandler.showProgressDialog(this);
+ mKeyList = null;
+ mKeyserver = null;
+ mOperationHelper.cryptoOperation();
- // start service with intent
- startService(intent);
} catch (IOException e) {
Log.e(Constants.TAG, "Problem writing cache file", e);
Notify.create(this, "Problem writing cache file!", Notify.Style.ERROR)
.show((ViewGroup) findViewById(R.id.import_snackbar));
}
} else if (ls instanceof ImportKeysListFragment.CloudLoaderState) {
- ImportKeysListFragment.CloudLoaderState sls = (ImportKeysListFragment.CloudLoaderState) ls;
-
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(
- this,
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL,
- true,
- ProgressDialogFragment.ServiceType.CLOUD_IMPORT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- ImportKeysActivity.this.handleMessage(message);
- }
- };
-
- // Send all information needed to service to query keys in other thread
- Intent intent = new Intent(this, CloudImportService.class);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- data.putString(CloudImportService.IMPORT_KEY_SERVER, sls.mCloudPrefs.keyserver);
+ ImportKeysListFragment.CloudLoaderState sls =
+ (ImportKeysListFragment.CloudLoaderState) ls;
// get selected key entries
ArrayList<ParcelableKeyRing> keys = new ArrayList<>();
@@ -475,23 +442,70 @@ public class ImportKeysActivity extends BaseNfcActivity {
);
}
}
- data.putParcelableArrayList(CloudImportService.IMPORT_KEY_LIST, keys);
- intent.putExtra(CloudImportService.EXTRA_DATA, data);
+ mKeyList = keys;
+ mKeyserver = sls.mCloudPrefs.keyserver;
+ mOperationHelper.cryptoOperation();
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(CloudImportService.EXTRA_MESSENGER, messenger);
+ }
+ }
- // show progress dialog
- serviceHandler.showProgressDialog(this);
+ @Override
+ protected void onNfcPostExecute() throws IOException {
+ // either way, finish after NFC AsyncTask
+ finish();
+ }
- // start service with intent
- startService(intent);
- } else {
- Notify.create(this, R.string.error_nothing_import, Notify.Style.ERROR)
- .show((ViewGroup) findViewById(R.id.import_snackbar));
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mOperationHelper == null ||
+ !mOperationHelper.handleActivityResult(requestCode, resultCode, data)) {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ public void handleResult(ImportKeyResult result) {
+ if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(getIntent().getAction())
+ || ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(getIntent().getAction())) {
+ Intent intent = new Intent();
+ intent.putExtra(ImportKeyResult.EXTRA_RESULT, result);
+ ImportKeysActivity.this.setResult(RESULT_OK, intent);
+ ImportKeysActivity.this.finish();
+ return;
}
+ if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE.equals(getIntent().getAction())) {
+ ImportKeysActivity.this.setResult(RESULT_OK, mPendingIntentData);
+ ImportKeysActivity.this.finish();
+ return;
+ }
+
+ result.createNotify(ImportKeysActivity.this)
+ .show((ViewGroup) findViewById(R.id.import_snackbar));
+ }
+ // methods from CryptoOperationHelper.Callback
+
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(mKeyList, mKeyserver);
}
+ @Override
+ public void onCryptoOperationSuccess(ImportKeyResult result) {
+ handleResult(result);
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+ // do nothing
+ }
+
+ @Override
+ public void onCryptoOperationError(ImportKeyResult result) {
+ handleResult(result);
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
index 538fa16c7..746c75600 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
@@ -64,8 +64,8 @@ public class ImportKeysFileFragment extends Fragment {
// open .asc or .gpg files
// setting it to text/plain prevents Cyanogenmod's file manager from selecting asc
// or gpg types!
- FileHelper.openFile(ImportKeysFileFragment.this, Uri.fromFile(Constants.Path.APP_DIR),
- "*/*", REQUEST_CODE_FILE);
+ FileHelper.openDocument(ImportKeysFileFragment.this,
+ Uri.fromFile(Constants.Path.APP_DIR), "*/*", false, REQUEST_CODE_FILE);
}
});
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
index bf7e41045..8502798cd 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
@@ -34,6 +35,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.GetKeyResult;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListCloudLoader;
@@ -41,7 +43,9 @@ import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
+import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
@@ -62,6 +66,7 @@ public class ImportKeysListFragment extends ListFragment implements
private Activity mActivity;
private ImportKeysAdapter mAdapter;
+ private ParcelableProxy mParcelableProxy;
private LoaderState mLoaderState;
@@ -71,6 +76,8 @@ public class ImportKeysListFragment extends ListFragment implements
private LongSparseArray<ParcelableKeyRing> mCachedKeyData;
private boolean mNonInteractive;
+ private boolean mShowingOrbotDialog;
+
public LoaderState getLoaderState() {
return mLoaderState;
}
@@ -126,6 +133,7 @@ public class ImportKeysListFragment extends ListFragment implements
/**
* Creates an interactive ImportKeyListFragment which reads keyrings from bytes, or file specified
* by dataUri, or searches a keyserver for serverQuery, if parameter is not null, in that order
+ * Will immediately load data if non-null bytes/dataUri/serverQuery
*
* @param bytes byte data containing list of keyrings to be imported
* @param dataUri file from which keyrings are to be imported
@@ -141,7 +149,7 @@ public class ImportKeysListFragment extends ListFragment implements
/**
* Visually consists of a list of keyrings with checkboxes to specify which are to be imported
- * Can immediately load keyrings specified by any of its parameters
+ * Will immediately load data if non-null bytes/dataUri/serverQuery is supplied
*
* @param bytes byte data containing list of keyrings to be imported
* @param dataUri file from which keyrings are to be imported
@@ -259,6 +267,7 @@ public class ImportKeysListFragment extends ListFragment implements
}
public void loadNew(LoaderState loaderState) {
+
mLoaderState = loaderState;
restartLoaders();
@@ -301,7 +310,8 @@ public class ImportKeysListFragment extends ListFragment implements
}
case LOADER_ID_CLOUD: {
CloudLoaderState ls = (CloudLoaderState) mLoaderState;
- return new ImportKeysListCloudLoader(getActivity(), ls.mServerQuery, ls.mCloudPrefs);
+ return new ImportKeysListCloudLoader(getActivity(), ls.mServerQuery, ls.mCloudPrefs,
+ mParcelableProxy);
}
default:
@@ -349,6 +359,52 @@ public class ImportKeysListFragment extends ListFragment implements
if (getKeyResult.success()) {
// No error
+ } else if (getKeyResult.isPending()) {
+ if (getKeyResult.getRequiredInputParcel().mType ==
+ RequiredInputParcel.RequiredInputType.ENABLE_ORBOT) {
+ if (mShowingOrbotDialog) {
+ // to prevent dialogs stacking
+ return;
+ }
+
+ // this is because we can't commit fragment dialogs in onLoadFinished
+ Runnable showOrbotDialog = new Runnable() {
+ @Override
+ public void run() {
+ OrbotHelper.DialogActions dialogActions =
+ new OrbotHelper.DialogActions() {
+ @Override
+ public void onOrbotStarted() {
+ mShowingOrbotDialog = false;
+ restartLoaders();
+ }
+
+ @Override
+ public void onNeutralButton() {
+ mParcelableProxy = ParcelableProxy
+ .getForNoProxy();
+ mShowingOrbotDialog = false;
+ restartLoaders();
+ }
+
+ @Override
+ public void onCancel() {
+ mShowingOrbotDialog = false;
+ }
+ };
+
+ if (OrbotHelper.putOrbotInRequiredState(dialogActions,
+ getActivity())) {
+ // looks like we didn't have to show the
+ // dialog after all
+ mShowingOrbotDialog = false;
+ restartLoaders();
+ }
+ }
+ };
+ new Handler().post(showOrbotDialog);
+ mShowingOrbotDialog = true;
+ }
} else {
getKeyResult.createNotify(getActivity()).show();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
index dc8752d1a..b60f3984c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.ui;
import android.annotation.TargetApi;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.net.Uri;
@@ -26,8 +25,6 @@ import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
import android.os.Build;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.os.Parcelable;
import android.support.v4.app.FragmentActivity;
import android.widget.Toast;
@@ -41,10 +38,10 @@ import org.sufficientlysecure.keychain.intents.OpenKeychainIntents;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.SingletonResult;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
@@ -55,7 +52,8 @@ import java.util.Locale;
/**
* Proxy activity (just a transparent content view) to scan QR Codes using the Barcode Scanner app
*/
-public class ImportKeysProxyActivity extends FragmentActivity {
+public class ImportKeysProxyActivity extends FragmentActivity
+ implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
public static final String ACTION_QR_CODE_API = OpenKeychainIntents.IMPORT_KEY_FROM_QR_CODE;
// implies activity returns scanned fingerprint as extra and does not import
@@ -64,6 +62,11 @@ public class ImportKeysProxyActivity extends FragmentActivity {
public static final String EXTRA_FINGERPRINT = "fingerprint";
+ // for CryptoOperationHelper
+ private String mKeyserver;
+ private ArrayList<ParcelableKeyRing> mKeyList;
+ private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -106,6 +109,19 @@ public class ImportKeysProxyActivity extends FragmentActivity {
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mImportOpHelper != null) {
+ if (!mImportOpHelper.handleActivityResult(requestCode, resultCode, data)) {
+ // if a result has been returned, and it does not belong to mImportOpHelper,
+ // return it down to other activity
+ if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
+ returnResult(data);
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ finish();
+ }
+ }
+ }
+
if (requestCode == IntentIntegratorSupportV4.REQUEST_CODE) {
IntentResult scanResult = IntentIntegratorSupportV4.parseActivityResult(requestCode,
resultCode, data);
@@ -121,13 +137,6 @@ public class ImportKeysProxyActivity extends FragmentActivity {
return;
}
- // if a result has been returned, return it down to other activity
- if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
- returnResult(data);
- } else {
- super.onActivityResult(requestCode, resultCode, data);
- finish();
- }
}
private void processScannedContent(String content) {
@@ -141,24 +150,34 @@ public class ImportKeysProxyActivity extends FragmentActivity {
Log.d(Constants.TAG, "scanned: " + uri);
// example: openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282
- if (uri != null && uri.getScheme() != null && uri.getScheme().toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) {
- String fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH);
-
- if (ACTION_SCAN_WITH_RESULT.equals(action)) {
- Intent result = new Intent();
- result.putExtra(EXTRA_FINGERPRINT, fingerprint);
- setResult(RESULT_OK, result);
- finish();
- } else {
- importKeys(fingerprint);
- }
- } else {
+ if (uri == null || uri.getScheme() == null ||
+ !uri.getScheme().toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) {
SingletonResult result = new SingletonResult(
- SingletonResult.RESULT_ERROR, OperationResult.LogType.MSG_WRONG_QR_CODE);
+ SingletonResult.RESULT_ERROR, LogType.MSG_WRONG_QR_CODE);
Intent intent = new Intent();
intent.putExtra(SingletonResult.EXTRA_RESULT, result);
returnResult(intent);
+ return;
+ }
+ final String fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH);
+ if (!fingerprint.matches("[a-fA-F0-9]{40}")) {
+ SingletonResult result = new SingletonResult(
+ SingletonResult.RESULT_ERROR, LogType.MSG_WRONG_QR_CODE_FP);
+ Intent intent = new Intent();
+ intent.putExtra(SingletonResult.EXTRA_RESULT, result);
+ returnResult(intent);
+ return;
+ }
+
+ if (ACTION_SCAN_WITH_RESULT.equals(action)) {
+ Intent result = new Intent();
+ result.putExtra(EXTRA_FINGERPRINT, fingerprint);
+ setResult(RESULT_OK, result);
+ finish();
+ } else {
+ importKeys(fingerprint);
}
+
}
public void returnResult(Intent data) {
@@ -194,77 +213,55 @@ public class ImportKeysProxyActivity extends FragmentActivity {
private void startImportService(ArrayList<ParcelableKeyRing> keyRings) {
- // Message is received after importing is done in KeychainIntentService
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(
- this,
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL,
- true,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
- if (returnData == null) {
- finish();
- return;
- }
- final ImportKeyResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null) {
- Log.e(Constants.TAG, "result == null");
- finish();
- return;
- }
-
- if (!result.success()) {
- // only return if no success...
- Intent data = new Intent();
- data.putExtras(returnData);
- returnResult(data);
- return;
- }
-
- Intent certifyIntent = new Intent(ImportKeysProxyActivity.this,
- CertifyKeyActivity.class);
- certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
- certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS,
- result.getImportedMasterKeyIds());
- startActivityForResult(certifyIntent, 0);
- }
- }
- };
-
- // fill values for this action
- Bundle data = new Bundle();
-
// search config
{
Preferences prefs = Preferences.getPreferences(this);
Preferences.CloudSearchPrefs cloudPrefs =
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
- data.putString(KeychainIntentService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
+ mKeyserver = cloudPrefs.keyserver;
}
- data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, keyRings);
+ mKeyList = keyRings;
- // Send all information needed to service to query keys in other thread
- Intent intent = new Intent(this, KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+ mImportOpHelper = new CryptoOperationHelper<>(1, this, this, R.string.progress_importing);
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+ mImportOpHelper.cryptoOperation();
+ }
+
+
+ // CryptoOperationHelper.Callback methods
- // show progress dialog
- serviceHandler.showProgressDialog(this);
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(mKeyList, mKeyserver);
+ }
- // start service with intent
- startService(intent);
+ @Override
+ public void onCryptoOperationSuccess(ImportKeyResult result) {
+ Intent certifyIntent = new Intent(this, CertifyKeyActivity.class);
+ certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
+ certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS,
+ result.getImportedMasterKeyIds());
+ startActivityForResult(certifyIntent, 0);
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(ImportKeyResult result) {
+ Bundle returnData = new Bundle();
+ returnData.putParcelable(OperationResult.EXTRA_RESULT, result);
+ Intent data = new Intent();
+ data.putExtras(returnData);
+ returnResult(data);
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
}
/**
@@ -276,7 +273,8 @@ public class ImportKeysProxyActivity extends FragmentActivity {
// only one message sent during the beam
NdefMessage msg = (NdefMessage) rawMsgs[0];
// record 0 contains the MIME type, record 1 is the AAR, if present
- byte[] receivedKeyringBytes = msg.getRecords()[0].getPayload();
+ final byte[] receivedKeyringBytes = msg.getRecords()[0].getPayload();
+
importKeys(receivedKeyringBytes);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
index d8c3e0350..ce6994ba4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -21,22 +21,21 @@ package org.sufficientlysecure.keychain.ui;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.app.Activity;
-import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MergeCursor;
import android.graphics.Color;
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.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.SearchView;
+import android.text.TextUtils;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -52,51 +51,45 @@ import android.widget.TextView;
import com.getbase.floatingactionbutton.FloatingActionButton;
import com.getbase.floatingactionbutton.FloatingActionsMenu;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
-import org.sufficientlysecure.keychain.operations.results.DeleteResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.CloudImportService;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.ConsolidateInputParcel;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
-import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
+import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
-import org.sufficientlysecure.keychain.util.ExportHelper;
import org.sufficientlysecure.keychain.util.FabContainer;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
+import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
+import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
-import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
-import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
-
/**
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
* StickyListHeaders library which does not extend upon ListView.
*/
public class KeyListFragment extends LoaderFragment
implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
- LoaderManager.LoaderCallbacks<Cursor>, FabContainer {
-
- static final int REQUEST_REPEAT_PASSPHRASE = 1;
- static final int REQUEST_ACTION = 2;
+ LoaderManager.LoaderCallbacks<Cursor>, FabContainer,
+ CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
- ExportHelper mExportHelper;
+ static final int REQUEST_ACTION = 1;
+ private static final int REQUEST_DELETE = 2;
+ private static final int REQUEST_VIEW_KEY = 3;
private KeyListAdapter mAdapter;
private StickyListHeadersListView mStickyList;
@@ -108,17 +101,13 @@ public class KeyListFragment extends LoaderFragment
private FloatingActionsMenu mFab;
- // This ids for multiple key export.
- private ArrayList<Long> mIdsForRepeatAskPassphrase;
- // This index for remembering the number of master key.
- private int mIndex;
+ // for CryptoOperationHelper import
+ private ArrayList<ParcelableKeyRing> mKeyList;
+ private String mKeyserver;
+ private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mExportHelper = new ExportHelper(getActivity());
- }
+ // for ConsolidateOperation
+ private CryptoOperationHelper<ConsolidateInputParcel, ConsolidateResult> mConsolidateOpHelper;
/**
* Load custom layout with StickyListView from library
@@ -230,19 +219,7 @@ public class KeyListFragment extends LoaderFragment
}
case R.id.menu_key_list_multi_delete: {
ids = mAdapter.getCurrentSelectedMasterKeyIds();
- showDeleteKeyDialog(mode, ids, mAdapter.isAnySecretSelected());
- break;
- }
- case R.id.menu_key_list_multi_export: {
- ids = mAdapter.getCurrentSelectedMasterKeyIds();
- showMultiExportDialog(ids);
- break;
- }
- case R.id.menu_key_list_multi_select_all: {
- // select all
- for (int i = 0; i < mAdapter.getCount(); i++) {
- mStickyList.setItemChecked(i, true);
- }
+ showDeleteKeyDialog(ids, mAdapter.isAnySecretSelected());
break;
}
}
@@ -289,7 +266,6 @@ public class KeyListFragment extends LoaderFragment
static final String ORDER =
KeyRings.HAS_ANY_SECRET + " DESC, UPPER(" + KeyRings.USER_ID + ") ASC";
-
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
@@ -322,6 +298,22 @@ public class KeyListFragment extends LoaderFragment
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.setSearchQuery(mQuery);
+
+ if (data != null && (mQuery == null || TextUtils.isEmpty(mQuery))) {
+ boolean isSecret = data.moveToFirst() && data.getInt(KeyListAdapter.INDEX_HAS_ANY_SECRET) != 0;
+ if (!isSecret) {
+ MatrixCursor headerCursor = new MatrixCursor(KeyListAdapter.PROJECTION);
+ Long[] row = new Long[KeyListAdapter.PROJECTION.length];
+ row[KeyListAdapter.INDEX_HAS_ANY_SECRET] = 1L;
+ row[KeyListAdapter.INDEX_MASTER_KEY_ID] = 0L;
+ headerCursor.addRow(row);
+
+ Cursor dataCursor = data;
+ data = new MergeCursor(new Cursor[] {
+ headerCursor, dataCursor
+ });
+ }
+ }
mAdapter.swapCursor(data);
mStickyList.setAdapter(mAdapter);
@@ -358,7 +350,7 @@ public class KeyListFragment extends LoaderFragment
Intent viewIntent = new Intent(getActivity(), ViewKeyActivity.class);
viewIntent.setData(
KeyRings.buildGenericKeyRingUri(mAdapter.getMasterKeyId(position)));
- startActivity(viewIntent);
+ startActivityForResult(viewIntent, REQUEST_VIEW_KEY);
}
protected void encrypt(ActionMode mode, long[] masterKeyIds) {
@@ -376,38 +368,15 @@ public class KeyListFragment extends LoaderFragment
*
* @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not
*/
- public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) {
- // Can only work on singular secret keys
- if (hasSecret && masterKeyIds.length > 1) {
- Notify.create(getActivity(), R.string.secret_cannot_multiple,
- Notify.Style.ERROR).show();
- return;
+ public void showDeleteKeyDialog(long[] masterKeyIds, boolean hasSecret) {
+ Intent intent = new Intent(getActivity(), DeleteKeyDialogActivity.class);
+ intent.putExtra(DeleteKeyDialogActivity.EXTRA_DELETE_MASTER_KEY_IDS, masterKeyIds);
+ intent.putExtra(DeleteKeyDialogActivity.EXTRA_HAS_SECRET, hasSecret);
+ if (hasSecret) {
+ intent.putExtra(DeleteKeyDialogActivity.EXTRA_KEYSERVER,
+ Preferences.getPreferences(getActivity()).getPreferredKeyserver());
}
-
- // Message is received after key is deleted
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.arg1 == DeleteKeyDialogFragment.MESSAGE_OKAY) {
- Bundle data = message.getData();
- if (data != null) {
- DeleteResult result = data.getParcelable(DeleteResult.EXTRA_RESULT);
- if (result != null) {
- result.createNotify(getActivity()).show();
- }
- }
- mode.finish();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
- masterKeyIds);
-
- deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
+ startActivityForResult(intent, REQUEST_DELETE);
}
@@ -462,18 +431,10 @@ public class KeyListFragment extends LoaderFragment
createKey();
return true;
- case R.id.menu_key_list_export:
- mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true);
- return true;
-
case R.id.menu_key_list_update_all_keys:
updateAllKeys();
return true;
- case R.id.menu_key_list_debug_cons:
- consolidate();
- return true;
-
case R.id.menu_key_list_debug_read:
try {
KeychainDatabase.debugBackup(getActivity(), true);
@@ -504,6 +465,10 @@ public class KeyListFragment extends LoaderFragment
getActivity().finish();
return true;
+ case R.id.menu_key_list_debug_cons:
+ consolidate();
+ return true;
+
default:
return super.onOptionsItemSelected(item);
}
@@ -555,9 +520,12 @@ public class KeyListFragment extends LoaderFragment
}
private void updateAllKeys() {
- Context context = getActivity();
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
- ProviderHelper providerHelper = new ProviderHelper(context);
+ ProviderHelper providerHelper = new ProviderHelper(activity);
Cursor cursor = providerHelper.getContentResolver().query(
KeyRings.buildUnifiedKeyRingsUri(), new String[]{
@@ -565,182 +533,115 @@ public class KeyListFragment extends LoaderFragment
}, null, null, null
);
- ArrayList<ParcelableKeyRing> keyList = new ArrayList<>();
-
- while (cursor.moveToNext()) {
- byte[] blob = cursor.getBlob(0);//fingerprint column is 0
- String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
- ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
- keyList.add(keyEntry);
+ if (cursor == null) {
+ Notify.create(activity, R.string.error_loading_keys, Notify.Style.ERROR);
+ return;
}
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_updating),
- ProgressDialog.STYLE_HORIZONTAL,
- true,
- ProgressDialogFragment.ServiceType.CLOUD_IMPORT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
- if (returnData == null) {
- return;
- }
- final ImportKeyResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null) {
- Log.e(Constants.TAG, "result == null");
- return;
- }
-
- result.createNotify(getActivity()).show();
- }
+ ArrayList<ParcelableKeyRing> keyList = new ArrayList<>();
+ try {
+ while (cursor.moveToNext()) {
+ byte[] blob = cursor.getBlob(0);//fingerprint column is 0
+ String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
+ ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
+ keyList.add(keyEntry);
}
- };
-
- // Send all information needed to service to query keys in other thread
- Intent intent = new Intent(getActivity(), CloudImportService.class);
-
- // fill values for this action
- Bundle data = new Bundle();
+ mKeyList = keyList;
+ } finally {
+ cursor.close();
+ }
// search config
{
Preferences prefs = Preferences.getPreferences(getActivity());
Preferences.CloudSearchPrefs cloudPrefs =
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
- data.putString(CloudImportService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
+ mKeyserver = cloudPrefs.keyserver;
}
- data.putParcelableArrayList(CloudImportService.IMPORT_KEY_LIST, keyList);
-
- intent.putExtra(CloudImportService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(CloudImportService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- serviceHandler.showProgressDialog(getActivity());
-
- // start service with intent
- getActivity().startService(intent);
+ mImportOpHelper = new CryptoOperationHelper<>(1, this,
+ this, R.string.progress_updating);
+ mImportOpHelper.cryptoOperation();
}
private void consolidate() {
- // Message is received after importing is done in KeychainIntentService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
- if (returnData == null) {
- return;
- }
- final ConsolidateResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null) {
- return;
- }
- result.createNotify(getActivity()).show();
- }
+ CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult> callback
+ = new CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult>() {
+
+ @Override
+ public ConsolidateInputParcel createOperationInput() {
+ return new ConsolidateInputParcel(false); // we want to perform a full consolidate
}
- };
- // Send all information needed to service to import key in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+ @Override
+ public void onCryptoOperationSuccess(ConsolidateResult result) {
+ result.createNotify(getActivity()).show();
+ }
- intent.setAction(KeychainIntentService.ACTION_CONSOLIDATE);
+ @Override
+ public void onCryptoOperationCancelled() {
- // fill values for this action
- Bundle data = new Bundle();
+ }
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+ @Override
+ public void onCryptoOperationError(ConsolidateResult result) {
+ result.createNotify(getActivity()).show();
+ }
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
+ };
- // show progress dialog
- saveHandler.showProgressDialog(getActivity());
+ mConsolidateOpHelper =
+ new CryptoOperationHelper<>(2, this, callback, R.string.progress_importing);
- // start service with intent
- getActivity().startService(intent);
+ mConsolidateOpHelper.cryptoOperation();
}
- private void showMultiExportDialog(long[] masterKeyIds) {
- mIdsForRepeatAskPassphrase = new ArrayList<>();
- for (long id : masterKeyIds) {
- try {
- if (PassphraseCacheService.getCachedPassphrase(
- getActivity(), id, id) == null) {
- mIdsForRepeatAskPassphrase.add(id);
- }
- } catch (PassphraseCacheService.KeyNotFoundException e) {
- // This happens when the master key is stripped
- // and ignore this key.
- }
- }
- mIndex = 0;
- if (mIdsForRepeatAskPassphrase.size() != 0) {
- startPassphraseActivity();
- return;
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mImportOpHelper != null) {
+ mImportOpHelper.handleActivityResult(requestCode, resultCode, data);
}
- long[] idsForMultiExport = new long[mIdsForRepeatAskPassphrase.size()];
- for (int i = 0; i < mIdsForRepeatAskPassphrase.size(); ++i) {
- idsForMultiExport[i] = mIdsForRepeatAskPassphrase.get(i);
+
+ if (mConsolidateOpHelper != null) {
+ mConsolidateOpHelper.handleActivityResult(requestCode, resultCode, data);
}
- mExportHelper.showExportKeysDialog(idsForMultiExport,
- Constants.Path.APP_DIR_FILE,
- mAdapter.isAnySecretSelected());
- }
- private void startPassphraseActivity() {
- Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class);
- long masterKeyId = mIdsForRepeatAskPassphrase.get(mIndex++);
- intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, masterKeyId);
- startActivityForResult(intent, REQUEST_REPEAT_PASSPHRASE);
- }
+ switch (requestCode) {
+ case REQUEST_DELETE:
+ if (mActionMode != null) {
+ mActionMode.finish();
+ }
+ if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
+ OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
+ result.createNotify(getActivity()).show();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ break;
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == REQUEST_REPEAT_PASSPHRASE) {
- if (resultCode != Activity.RESULT_OK) {
- return;
- }
- if (mIndex < mIdsForRepeatAskPassphrase.size()) {
- startPassphraseActivity();
- return;
- }
- long[] idsForMultiExport = new long[mIdsForRepeatAskPassphrase.size()];
- for (int i = 0; i < mIdsForRepeatAskPassphrase.size(); ++i) {
- idsForMultiExport[i] = mIdsForRepeatAskPassphrase.get(i);
- }
- mExportHelper.showExportKeysDialog(idsForMultiExport,
- Constants.Path.APP_DIR_FILE,
- mAdapter.isAnySecretSelected());
- }
+ case REQUEST_ACTION:
+ // if a result has been returned, display a notify
+ if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
+ OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
+ result.createNotify(getActivity()).show();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ break;
- if (requestCode == REQUEST_ACTION) {
- // if a result has been returned, display a notify
- if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
- OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
- result.createNotify(getActivity()).show();
- } else {
- super.onActivityResult(requestCode, resultCode, data);
- }
+ case REQUEST_VIEW_KEY:
+ if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
+ OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
+ result.createNotify(getActivity()).show();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ break;
}
}
@@ -761,12 +662,41 @@ public class KeyListFragment extends LoaderFragment
anim.start();
}
+ // CryptoOperationHelper.Callback methods
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(mKeyList, mKeyserver);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ImportKeyResult result) {
+ result.createNotify(getActivity()).show();
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(ImportKeyResult result) {
+ result.createNotify(getActivity()).show();
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
+
public class KeyListAdapter extends KeyAdapter implements StickyListHeadersAdapter {
private HashMap<Integer, Boolean> mSelection = new HashMap<>();
+ private Context mContext;
+
public KeyListAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
+ mContext = context;
}
@Override
@@ -795,9 +725,11 @@ public class KeyListFragment extends LoaderFragment
// let the adapter handle setting up the row views
View v = super.getView(position, convertView, parent);
+ int colorEmphasis = FormattingUtils.getColorFromAttr(mContext, R.attr.colorEmphasis);
+
if (mSelection.get(position) != null) {
// selected position color
- v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis));
+ v.setBackgroundColor(colorEmphasis);
} else {
// default color
v.setBackgroundColor(Color.TRANSPARENT);
@@ -806,6 +738,29 @@ public class KeyListFragment extends LoaderFragment
return v;
}
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ boolean isSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) != 0;
+ long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
+ if (isSecret && masterKeyId == 0L) {
+
+ // sort of a hack: if this item isn't enabled, we make it clickable
+ // to intercept its click events
+ view.setClickable(true);
+
+ KeyItemViewHolder h = (KeyItemViewHolder) view.getTag();
+ h.setDummy(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ createKey();
+ }
+ });
+ return;
+ }
+
+ super.bindView(view, context, cursor);
+ }
+
private class HeaderViewHolder {
TextView mText;
TextView mCount;
@@ -844,6 +799,10 @@ public class KeyListFragment extends LoaderFragment
if (mCursor.getInt(INDEX_HAS_ANY_SECRET) != 0) {
{ // set contact count
int num = mCursor.getCount();
+ // If this is a dummy secret key, subtract one
+ if (mCursor.getLong(INDEX_MASTER_KEY_ID) == 0L) {
+ num -= 1;
+ }
String contactsTotal = mContext.getResources().getQuantityString(R.plurals.n_keys, num, num);
holder.mCount.setText(contactsTotal);
holder.mCount.setVisibility(View.VISIBLE);
@@ -902,8 +861,9 @@ public class KeyListFragment extends LoaderFragment
public boolean isAnySecretSelected() {
for (int pos : mSelection.keySet()) {
- if (isSecretAvailable(pos))
+ if (isSecretAvailable(pos)) {
return true;
+ }
}
return false;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
index 138f2f4e7..4de83337e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
@@ -43,13 +43,13 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogLevel;
import org.sufficientlysecure.keychain.operations.results.OperationResult.SubLogEntryParcel;
+import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.Log;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
-import java.util.Iterator;
public class LogDisplayFragment extends ListFragment implements OnItemClickListener {
@@ -58,9 +58,12 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe
OperationResult mResult;
public static final String EXTRA_RESULT = "log";
+ protected int mTextColor;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mTextColor = FormattingUtils.getColorFromAttr(getActivity(), R.attr.colorText);
setHasOptionsMenu(true);
}
@@ -75,7 +78,12 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe
return;
}
- mResult = intent.getParcelableExtra(EXTRA_RESULT);
+ if (savedInstanceState != null) {
+ mResult = savedInstanceState.getParcelable(EXTRA_RESULT);
+ } else {
+ mResult = intent.getParcelableExtra(EXTRA_RESULT);
+ }
+
if (mResult == null) {
getActivity().finish();
return;
@@ -92,6 +100,14 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe
}
@Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ // need to parcel this again, logs are only single-instance parcelable
+ outState.putParcelable(EXTRA_RESULT, mResult);
+ }
+
+ @Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
inflater.inflate(R.menu.log_display, menu);
@@ -110,7 +126,6 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe
}
private void exportLog() {
-
showExportLogDialog(new File(Constants.Path.APP_DIR, "export.log"));
}
@@ -142,7 +157,9 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe
}
}
- if (!error) currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_SUCCESS, 1);
+ if (!error) {
+ currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_SUCCESS, 1);
+ }
int opResultCode = error ? OperationResult.RESULT_ERROR : OperationResult.RESULT_OK;
OperationResult opResult = new LogExportResult(opResultCode, currLog);
@@ -158,8 +175,8 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe
*/
private String getPrintableOperationLog(OperationResult.OperationLog opLog, String basePadding) {
String log = "";
- for (Iterator<LogEntryParcel> logIterator = opLog.iterator(); logIterator.hasNext(); ) {
- log += getPrintableLogEntry(logIterator.next(), basePadding) + "\n";
+ for (LogEntryParcel anOpLog : opLog) {
+ log += getPrintableLogEntry(anOpLog, basePadding) + "\n";
}
log = log.substring(0, log.length() - 1);//gets rid of extra new line
return log;
@@ -235,7 +252,7 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe
String message = this.getString(R.string.specify_file_to_export_log_to);
- FileHelper.saveFile(new FileHelper.FileDialogCallback() {
+ FileHelper.saveDocumentDialog(new FileHelper.FileDialogCallback() {
@Override
public void onFileSelected(File file, boolean checked) {
writeToLogFile(mResult.getLog(), file);
@@ -343,13 +360,13 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe
ih.mSecondText.setText(getResources().getString(subEntry.mType.getMsgId(),
subEntry.mParameters));
}
- ih.mSecondText.setTextColor(subEntry.mType.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK);
+ ih.mSecondText.setTextColor(subEntry.mType.mLevel == LogLevel.DEBUG ? Color.GRAY : mTextColor);
switch (subEntry.mType.mLevel) {
case DEBUG: ih.mSecondImg.setBackgroundColor(Color.GRAY); break;
- case INFO: ih.mSecondImg.setBackgroundColor(Color.BLACK); break;
+ case INFO: ih.mSecondImg.setBackgroundColor(mTextColor); break;
case WARN: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_orange_light)); break;
case ERROR: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break;
- case START: ih.mSecondImg.setBackgroundColor(Color.BLACK); break;
+ case START: ih.mSecondImg.setBackgroundColor(mTextColor); break;
case OK: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_green_light)); break;
case CANCELLED: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break;
}
@@ -374,13 +391,13 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe
entry.mParameters));
}
convertView.setPadding((entry.mIndent) * dipFactor, 0, 0, 0);
- ih.mText.setTextColor(entry.mType.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK);
+ ih.mText.setTextColor(entry.mType.mLevel == LogLevel.DEBUG ? Color.GRAY : mTextColor);
switch (entry.mType.mLevel) {
case DEBUG: ih.mImg.setBackgroundColor(Color.GRAY); break;
- case INFO: ih.mImg.setBackgroundColor(Color.BLACK); break;
+ case INFO: ih.mImg.setBackgroundColor(mTextColor); break;
case WARN: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_orange_light)); break;
case ERROR: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break;
- case START: ih.mImg.setBackgroundColor(Color.BLACK); break;
+ case START: ih.mImg.setBackgroundColor(mTextColor); break;
case OK: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_green_light)); break;
case CANCELLED: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java
index f571ba1e6..6f5d98afd 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java
@@ -23,8 +23,8 @@ import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentManager.OnBackStackChangedListener;
import android.support.v4.app.FragmentTransaction;
-import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.AdapterView;
@@ -33,23 +33,31 @@ import com.mikepenz.community_material_typeface_library.CommunityMaterial;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import com.mikepenz.iconics.typeface.FontAwesome;
import com.mikepenz.materialdrawer.Drawer;
+import com.mikepenz.materialdrawer.DrawerBuilder;
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem;
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.remote.ui.AppsListFragment;
+import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
import org.sufficientlysecure.keychain.util.FabContainer;
import org.sufficientlysecure.keychain.util.Preferences;
-public class MainActivity extends AppCompatActivity implements FabContainer {
+public class MainActivity extends BaseNfcActivity implements FabContainer, OnBackStackChangedListener {
- public Drawer.Result result;
+ static final int ID_KEYS = 1;
+ static final int ID_ENCRYPT_DECRYPT = 2;
+ static final int ID_APPS = 3;
+ static final int ID_BACKUP = 4;
+ static final int ID_SETTINGS = 5;
+ static final int ID_HELP = 6;
- private KeyListFragment mKeyListFragment ;
- private AppsListFragment mAppsListFragment;
- private EncryptDecryptOverviewFragment mEncryptDecryptOverviewFragment;
- private Fragment mLastUsedFragment;
+ // both of these are used for instrumentation testing only
+ public static final String EXTRA_SKIP_FIRST_TIME = "skip_first_time";
+ public static final String EXTRA_INIT_FRAG = "init_frag";
+
+ public Drawer mDrawer;
private Toolbar mToolbar;
@Override
@@ -57,50 +65,51 @@ public class MainActivity extends AppCompatActivity implements FabContainer {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
- //initialize FragmentLayout with KeyListFragment at first
- Fragment mainFragment = new KeyListFragment();
- FragmentManager fm = getSupportFragmentManager();
- FragmentTransaction transaction = fm.beginTransaction();
- transaction.replace(R.id.main_fragment_container, mainFragment);
- transaction.commit();
-
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mToolbar.setTitle(R.string.app_name);
setSupportActionBar(mToolbar);
- result = new Drawer()
+ mDrawer = new DrawerBuilder()
.withActivity(this)
.withHeader(R.layout.main_drawer_header)
.withToolbar(mToolbar)
.addDrawerItems(
- new PrimaryDrawerItem().withName(R.string.nav_keys).withIcon(CommunityMaterial.Icon.cmd_key).withIdentifier(1).withCheckable(false),
- new PrimaryDrawerItem().withName(R.string.nav_encrypt_decrypt).withIcon(FontAwesome.Icon.faw_lock).withIdentifier(2).withCheckable(false),
- new PrimaryDrawerItem().withName(R.string.title_api_registered_apps).withIcon(CommunityMaterial.Icon.cmd_apps).withIdentifier(3).withCheckable(false)
+ new PrimaryDrawerItem().withName(R.string.nav_keys).withIcon(CommunityMaterial.Icon.cmd_key)
+ .withIdentifier(ID_KEYS).withCheckable(false),
+ new PrimaryDrawerItem().withName(R.string.nav_encrypt_decrypt).withIcon(FontAwesome.Icon.faw_lock)
+ .withIdentifier(ID_ENCRYPT_DECRYPT).withCheckable(false),
+ new PrimaryDrawerItem().withName(R.string.title_api_registered_apps).withIcon(CommunityMaterial.Icon.cmd_apps)
+ .withIdentifier(ID_APPS).withCheckable(false),
+ new PrimaryDrawerItem().withName(R.string.nav_backup).withIcon(CommunityMaterial.Icon.cmd_backup_restore)
+ .withIdentifier(ID_BACKUP).withCheckable(false)
)
.addStickyDrawerItems(
// display and stick on bottom of drawer
- new PrimaryDrawerItem().withName(R.string.menu_preferences).withIcon(GoogleMaterial.Icon.gmd_settings).withIdentifier(4).withCheckable(false),
- new PrimaryDrawerItem().withName(R.string.menu_help).withIcon(CommunityMaterial.Icon.cmd_help_circle).withIdentifier(5).withCheckable(false)
+ new PrimaryDrawerItem().withName(R.string.menu_preferences).withIcon(GoogleMaterial.Icon.gmd_settings).withIdentifier(ID_SETTINGS).withCheckable(false),
+ new PrimaryDrawerItem().withName(R.string.menu_help).withIcon(CommunityMaterial.Icon.cmd_help_circle).withIdentifier(ID_HELP).withCheckable(false)
)
.withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id, IDrawerItem drawerItem) {
+ public boolean onItemClick(AdapterView<?> parent, View view, int position, long id, IDrawerItem drawerItem) {
if (drawerItem != null) {
Intent intent = null;
switch(drawerItem.getIdentifier()) {
- case 1:
+ case ID_KEYS:
onKeysSelected();
break;
- case 2:
+ case ID_ENCRYPT_DECRYPT:
onEnDecryptSelected();
break;
- case 3:
+ case ID_APPS:
onAppsSelected();
break;
- case 4:
+ case ID_BACKUP:
+ onBackupSelected();
+ break;
+ case ID_SETTINGS:
intent = new Intent(MainActivity.this, SettingsActivity.class);
break;
- case 5:
+ case ID_HELP:
intent = new Intent(MainActivity.this, HelpActivity.class);
break;
}
@@ -108,6 +117,8 @@ public class MainActivity extends AppCompatActivity implements FabContainer {
MainActivity.this.startActivity(intent);
}
}
+
+ return false;
}
})
.withSelectedItem(-1)
@@ -116,7 +127,7 @@ public class MainActivity extends AppCompatActivity implements FabContainer {
// if this is the first time show first time activity
Preferences prefs = Preferences.getPreferences(this);
- if (prefs.isFirstTime()) {
+ if (!getIntent().getBooleanExtra(EXTRA_SKIP_FIRST_TIME, false) && prefs.isFirstTime()) {
Intent intent = new Intent(this, CreateKeyActivity.class);
intent.putExtra(CreateKeyActivity.EXTRA_FIRST_TIME, true);
startActivity(intent);
@@ -124,82 +135,91 @@ public class MainActivity extends AppCompatActivity implements FabContainer {
return;
}
+ getSupportFragmentManager().addOnBackStackChangedListener(this);
+
+ // all further initialization steps are saved as instance state
+ if (savedInstanceState != null) {
+ return;
+ }
+
Intent data = getIntent();
// If we got an EXTRA_RESULT in the intent, show the notification
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
result.createNotify(this).show();
}
- }
- private void clearFragments() {
- mKeyListFragment = null;
- mAppsListFragment = null;
- mEncryptDecryptOverviewFragment = null;
+ // always initialize keys fragment to the bottom of the backstack
+ onKeysSelected();
- getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
- }
+ if (data != null && data.hasExtra(EXTRA_INIT_FRAG)) {
+ // initialize FragmentLayout with KeyListFragment at first
+ switch (data.getIntExtra(EXTRA_INIT_FRAG, -1)) {
+ case ID_ENCRYPT_DECRYPT:
+ onEnDecryptSelected();
+ break;
+ case ID_APPS:
+ onAppsSelected();
+ break;
+ }
+ }
- private void setFragment(Fragment fragment) {
- setFragment(fragment, true);
}
private void setFragment(Fragment fragment, boolean addToBackStack) {
- this.mLastUsedFragment = fragment;
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
+
+ FragmentTransaction ft = fragmentManager.beginTransaction();
ft.replace(R.id.main_fragment_container, fragment);
if (addToBackStack) {
ft.addToBackStack(null);
}
ft.commit();
+
}
- private boolean onKeysSelected() {
+ private void onKeysSelected() {
mToolbar.setTitle(R.string.app_name);
- clearFragments();
-
- if (mKeyListFragment == null) {
- mKeyListFragment = new KeyListFragment();
- }
-
- setFragment(mKeyListFragment, false);
- return true;
+ mDrawer.setSelectionByIdentifier(ID_KEYS, false);
+ Fragment frag = new KeyListFragment();
+ setFragment(frag, false);
}
- private boolean onEnDecryptSelected() {
+ private void onEnDecryptSelected() {
mToolbar.setTitle(R.string.nav_encrypt_decrypt);
- clearFragments();
- if (mEncryptDecryptOverviewFragment == null) {
- mEncryptDecryptOverviewFragment = new EncryptDecryptOverviewFragment();
- }
-
- setFragment(mEncryptDecryptOverviewFragment);
- return true;
+ mDrawer.setSelectionByIdentifier(ID_ENCRYPT_DECRYPT, false);
+ Fragment frag = new EncryptDecryptOverviewFragment();
+ setFragment(frag, true);
}
- private boolean onAppsSelected() {
+ private void onAppsSelected() {
mToolbar.setTitle(R.string.nav_apps);
- clearFragments();
- if (mAppsListFragment == null) {
- mAppsListFragment = new AppsListFragment();
- }
+ mDrawer.setSelectionByIdentifier(ID_APPS, false);
+ Fragment frag = new AppsListFragment();
+ setFragment(frag, true);
+ }
- setFragment(mAppsListFragment);
- return true;
+ private void onBackupSelected() {
+ mToolbar.setTitle(R.string.nav_backup);
+ mDrawer.setSelectionByIdentifier(ID_APPS, false);
+ Fragment frag = new BackupFragment();
+ setFragment(frag, true);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
- //add the values which need to be saved from the drawer to the bundle
- outState = result.saveInstanceState(outState);
+ // add the values which need to be saved from the drawer to the bundle
+ outState = mDrawer.saveInstanceState(outState);
super.onSaveInstanceState(outState);
}
@Override
- public void onBackPressed(){
- //handle the back press :D close the drawer first and if the drawer is closed close the activity
- if (result != null && result.isDrawerOpen()) {
- result.closeDrawer();
+ public void onBackPressed() {
+ // close the drawer first and if the drawer is closed do regular backstack handling
+ if (mDrawer != null && mDrawer.isDrawerOpen()) {
+ mDrawer.closeDrawer();
} else {
super.onBackPressed();
}
@@ -223,4 +243,32 @@ public class MainActivity extends AppCompatActivity implements FabContainer {
}
}
+
+ @Override
+ public void onBackStackChanged() {
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ if (fragmentManager == null) {
+ return;
+ }
+ Fragment frag = fragmentManager.findFragmentById(R.id.main_fragment_container);
+ if (frag == null) {
+ return;
+ }
+
+ // make sure the selected icon is the one shown at this point
+ if (frag instanceof KeyListFragment) {
+ mToolbar.setTitle(R.string.app_name);
+ mDrawer.setSelection(mDrawer.getPositionFromIdentifier(ID_KEYS), false);
+ } else if (frag instanceof EncryptDecryptOverviewFragment) {
+ mToolbar.setTitle(R.string.nav_encrypt_decrypt);
+ mDrawer.setSelection(mDrawer.getPositionFromIdentifier(ID_ENCRYPT_DECRYPT), false);
+ } else if (frag instanceof AppsListFragment) {
+ mToolbar.setTitle(R.string.nav_apps);
+ mDrawer.setSelection(mDrawer.getPositionFromIdentifier(ID_APPS), false);
+ } else if (frag instanceof BackupFragment) {
+ mToolbar.setTitle(R.string.nav_backup);
+ mDrawer.setSelection(mDrawer.getPositionFromIdentifier(ID_BACKUP), false);
+ }
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java
index aa66053fa..b811b218e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java
@@ -1,52 +1,120 @@
-/**
- * Copyright (c) 2013-2014 Philipp Jakubeit, Signe Rüsch, Dominik Schürmann
+/*
+ * Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2013-2014 Signe Rüsch
+ * Copyright (C) 2013-2014 Philipp Jakubeit
+ *
+ * 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.
*
- * Licensed under the Bouncy Castle License (MIT license). See LICENSE file for details.
+ * 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.os.AsyncTask;
import android.os.Bundle;
+import android.view.View;
import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.ViewAnimator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.CryptoInputParcelCacheService;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
+import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.OrientationUtils;
+import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
/**
* This class provides a communication interface to OpenPGP applications on ISO SmartCard compliant
* NFC devices.
- * <p/>
* For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf
*/
public class NfcOperationActivity extends BaseNfcActivity {
public static final String EXTRA_REQUIRED_INPUT = "required_input";
+ public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
// passthrough for OpenPgpService
public static final String EXTRA_SERVICE_INTENT = "data";
- public static final String RESULT_DATA = "result_data";
+ public static final String RESULT_CRYPTO_INPUT = "result_data";
+
+ public ViewAnimator vAnimator;
+ public TextView vErrorText;
+ public Button vErrorTryAgainButton;
private RequiredInputParcel mRequiredInput;
private Intent mServiceIntent;
+ private static final byte[] BLANK_FINGERPRINT = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+ private CryptoInputParcel mInputParcel;
+
+ @Override
+ protected void initTheme() {
+ mThemeChanger = new ThemeChanger(this);
+ mThemeChanger.setThemes(R.style.Theme_Keychain_Light_Dialog,
+ R.style.Theme_Keychain_Dark_Dialog);
+ mThemeChanger.changeTheme();
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(Constants.TAG, "NfcOperationActivity.onCreate");
+ // prevent annoying orientation changes while fumbling with the device
+ OrientationUtils.lockOrientation(this);
+
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ mInputParcel = getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT);
+
+ setTitle(R.string.nfc_text);
+
+ vAnimator = (ViewAnimator) findViewById(R.id.view_animator);
+ vAnimator.setDisplayedChild(0);
+ vErrorText = (TextView) findViewById(R.id.nfc_activity_3_error_text);
+ vErrorTryAgainButton = (Button) findViewById(R.id.nfc_activity_3_error_try_again);
+ vErrorTryAgainButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ resumeTagHandling();
+
+ // obtain passphrase for this subkey
+ if (mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_MOVE_KEY_TO_CARD) {
+ obtainYubiKeyPin(mRequiredInput);
+ }
+ vAnimator.setDisplayedChild(0);
+ }
+ });
+
Intent intent = getIntent();
Bundle data = intent.getExtras();
@@ -54,49 +122,186 @@ public class NfcOperationActivity extends BaseNfcActivity {
mServiceIntent = data.getParcelable(EXTRA_SERVICE_INTENT);
// obtain passphrase for this subkey
- obtainYubiKeyPin(RequiredInputParcel.createRequiredPassphrase(mRequiredInput));
+ if (mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_MOVE_KEY_TO_CARD) {
+ obtainYubiKeyPin(mRequiredInput);
+ }
}
@Override
protected void initLayout() {
- setContentView(R.layout.nfc_activity);
+ setContentView(R.layout.nfc_operation_activity);
}
@Override
- protected void onNfcPerform() throws IOException {
+ public void onNfcPreExecute() {
+ // start with indeterminate progress
+ vAnimator.setDisplayedChild(1);
+ }
- CryptoInputParcel inputParcel = new CryptoInputParcel(mRequiredInput.mSignatureTime);
+ @Override
+ protected void doNfcInBackground() throws IOException {
switch (mRequiredInput.mType) {
case NFC_DECRYPT: {
- for (int i = 0; i < mRequiredInput.mInputHashes.length; i++) {
- byte[] hash = mRequiredInput.mInputHashes[i];
- byte[] decryptedSessionKey = nfcDecryptSessionKey(hash);
- inputParcel.addCryptoData(hash, decryptedSessionKey);
+ for (int i = 0; i < mRequiredInput.mInputData.length; i++) {
+ byte[] encryptedSessionKey = mRequiredInput.mInputData[i];
+ byte[] decryptedSessionKey = nfcDecryptSessionKey(encryptedSessionKey);
+ mInputParcel.addCryptoData(encryptedSessionKey, decryptedSessionKey);
}
break;
}
case NFC_SIGN: {
- for (int i = 0; i < mRequiredInput.mInputHashes.length; i++) {
- byte[] hash = mRequiredInput.mInputHashes[i];
+ mInputParcel.addSignatureTime(mRequiredInput.mSignatureTime);
+
+ for (int i = 0; i < mRequiredInput.mInputData.length; i++) {
+ byte[] hash = mRequiredInput.mInputData[i];
int algo = mRequiredInput.mSignAlgos[i];
byte[] signedHash = nfcCalculateSignature(hash, algo);
- inputParcel.addCryptoData(hash, signedHash);
+ mInputParcel.addCryptoData(hash, signedHash);
}
break;
}
+ case NFC_MOVE_KEY_TO_CARD: {
+ // TODO: assume PIN and Admin PIN to be default for this operation
+ mPin = new Passphrase("123456");
+ mAdminPin = new Passphrase("12345678");
+
+ ProviderHelper providerHelper = new ProviderHelper(this);
+ CanonicalizedSecretKeyRing secretKeyRing;
+ try {
+ secretKeyRing = providerHelper.getCanonicalizedSecretKeyRing(
+ KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(mRequiredInput.getMasterKeyId())
+ );
+ } catch (ProviderHelper.NotFoundException e) {
+ throw new IOException("Couldn't find subkey for key to card operation.");
+ }
+
+ byte[] newPin = mRequiredInput.mInputData[0];
+ byte[] newAdminPin = mRequiredInput.mInputData[1];
+
+ for (int i = 2; i < mRequiredInput.mInputData.length; i++) {
+ byte[] subkeyBytes = mRequiredInput.mInputData[i];
+ ByteBuffer buf = ByteBuffer.wrap(subkeyBytes);
+ long subkeyId = buf.getLong();
+
+ CanonicalizedSecretKey key = secretKeyRing.getSecretKey(subkeyId);
+
+ long keyGenerationTimestampMillis = key.getCreationTime().getTime();
+ long keyGenerationTimestamp = keyGenerationTimestampMillis / 1000;
+ byte[] timestampBytes = ByteBuffer.allocate(4).putInt((int) keyGenerationTimestamp).array();
+ byte[] cardSerialNumber = Arrays.copyOf(nfcGetAid(), 16);
+
+ Passphrase passphrase;
+ try {
+ passphrase = PassphraseCacheService.getCachedPassphrase(this,
+ mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId());
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ throw new IOException("Unable to get cached passphrase!");
+ }
+
+ if (key.canSign() || key.canCertify()) {
+ if (shouldPutKey(key.getFingerprint(), 0)) {
+ nfcPutKey(0xB6, key, passphrase);
+ nfcPutData(0xCE, timestampBytes);
+ nfcPutData(0xC7, key.getFingerprint());
+ } else {
+ throw new IOException("Key slot occupied; card must be reset to put new signature key.");
+ }
+ } else if (key.canEncrypt()) {
+ if (shouldPutKey(key.getFingerprint(), 1)) {
+ nfcPutKey(0xB8, key, passphrase);
+ nfcPutData(0xCF, timestampBytes);
+ nfcPutData(0xC8, key.getFingerprint());
+ } else {
+ throw new IOException("Key slot occupied; card must be reset to put new decryption key.");
+ }
+ } else if (key.canAuthenticate()) {
+ if (shouldPutKey(key.getFingerprint(), 2)) {
+ nfcPutKey(0xA4, key, passphrase);
+ nfcPutData(0xD0, timestampBytes);
+ nfcPutData(0xC9, key.getFingerprint());
+ } else {
+ throw new IOException("Key slot occupied; card must be reset to put new authentication key.");
+ }
+ } else {
+ throw new IOException("Inappropriate key flags for smart card key.");
+ }
+
+ // TODO: Is this really used anywhere?
+ mInputParcel.addCryptoData(subkeyBytes, cardSerialNumber);
+ }
+
+ // change PINs afterwards
+ nfcModifyPIN(0x81, newPin);
+ nfcModifyPIN(0x83, newAdminPin);
+
+ break;
+ }
+ default: {
+ throw new AssertionError("Unhandled mRequiredInput.mType");
+ }
}
+ }
+
+ @Override
+ protected void onNfcPostExecute() throws IOException {
if (mServiceIntent != null) {
- CryptoInputParcelCacheService.addCryptoInputParcel(this, mServiceIntent, inputParcel);
+ // if we're triggered by OpenPgpService
+ // save updated cryptoInputParcel in cache
+ CryptoInputParcelCacheService.addCryptoInputParcel(this, mServiceIntent, mInputParcel);
setResult(RESULT_OK, mServiceIntent);
} else {
Intent result = new Intent();
- result.putExtra(NfcOperationActivity.RESULT_DATA, inputParcel);
+ // send back the CryptoInputParcel we received
+ result.putExtra(RESULT_CRYPTO_INPUT, mInputParcel);
setResult(RESULT_OK, result);
}
- finish();
+ // show finish
+ vAnimator.setDisplayedChild(2);
+
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ // check all 200ms if YubiKey has been taken away
+ while (true) {
+ if (isNfcConnected()) {
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException ignored) {
+ }
+ } else {
+ return null;
+ }
+ }
+ }
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ finish();
+ }
+ }.execute();
+ }
+
+ @Override
+ protected void onNfcError(String error) {
+ pauseTagHandling();
+
+ vErrorText.setText(error + "\n\n" + getString(R.string.nfc_try_again_text));
+ vAnimator.setDisplayedChild(3);
+ }
+
+ private boolean shouldPutKey(byte[] fingerprint, int idx) throws IOException {
+ byte[] cardFingerprint = nfcGetFingerprint(idx);
+ // Slot is empty, or contains this key already. PUT KEY operation is safe
+ if (Arrays.equals(cardFingerprint, BLANK_FINGERPRINT) ||
+ Arrays.equals(cardFingerprint, fingerprint)) {
+ return true;
+ }
+
+ // Slot already contains a different key; don't overwrite it.
+ return false;
}
@Override
@@ -114,8 +319,6 @@ public class NfcOperationActivity extends BaseNfcActivity {
// clear (invalid) passphrase
PassphraseCacheService.clearCachedPassphrase(
this, mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId());
-
- obtainYubiKeyPin(RequiredInputParcel.createRequiredPassphrase(mRequiredInput));
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/OrbotRequiredDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/OrbotRequiredDialogActivity.java
new file mode 100644
index 000000000..0e70cda14
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/OrbotRequiredDialogActivity.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.support.v4.app.FragmentActivity;
+import android.view.ContextThemeWrapper;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ParcelableProxy;
+import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
+
+/**
+ * Simply encapsulates a dialog. If orbot is not installed, it shows an install dialog, else a
+ * dialog to enable orbot.
+ */
+public class OrbotRequiredDialogActivity extends FragmentActivity
+ implements OrbotHelper.DialogActions {
+
+ public static final int MESSAGE_ORBOT_STARTED = 1;
+ public static final int MESSAGE_ORBOT_IGNORE = 2;
+ public static final int MESSAGE_DIALOG_CANCEL = 3;
+
+ // if suppplied and true will start Orbot directly without showing dialog
+ public static final String EXTRA_START_ORBOT = "start_orbot";
+ // used for communicating results when triggered from a service
+ public static final String EXTRA_MESSENGER = "messenger";
+
+ // to provide any previous crypto input into which proxy preference is merged
+ public static final String EXTRA_CRYPTO_INPUT = "extra_crypto_input";
+
+ public static final String RESULT_CRYPTO_INPUT = "result_crypto_input";
+
+ private CryptoInputParcel mCryptoInputParcel;
+ private Messenger mMessenger;
+
+ private ProgressDialog mShowOrbotProgressDialog;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mCryptoInputParcel = getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT);
+ if (mCryptoInputParcel == null) {
+ // compatibility with usages that don't use a CryptoInputParcel
+ mCryptoInputParcel = new CryptoInputParcel();
+ }
+
+ mMessenger = getIntent().getParcelableExtra(EXTRA_MESSENGER);
+
+ boolean startOrbotDirect = getIntent().getBooleanExtra(EXTRA_START_ORBOT, false);
+ if (startOrbotDirect) {
+ ContextThemeWrapper theme = ThemeChanger.getDialogThemeWrapper(this);
+ mShowOrbotProgressDialog = new ProgressDialog(theme);
+ mShowOrbotProgressDialog.setTitle(R.string.progress_starting_orbot);
+ mShowOrbotProgressDialog.setCancelable(false);
+ mShowOrbotProgressDialog.show();
+ OrbotHelper.bestPossibleOrbotStart(this, this, false);
+ } else {
+ showDialog();
+ }
+ }
+
+ /**
+ * Displays an install or start orbot dialog (or silent orbot start) depending on orbot's
+ * presence and state
+ */
+ public void showDialog() {
+ DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
+ public void run() {
+
+ if (OrbotHelper.putOrbotInRequiredState(OrbotRequiredDialogActivity.this,
+ OrbotRequiredDialogActivity.this)) {
+ // no action required after all
+ onOrbotStarted();
+ }
+ }
+ });
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ switch (requestCode) {
+ case OrbotHelper.START_TOR_RESULT: {
+ dismissOrbotProgressDialog();
+ // unfortunately, this result is returned immediately and not when Orbot is started
+ // 10s is approximately the longest time Orbot has taken to start
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ onOrbotStarted(); // assumption that orbot was started
+ }
+ }, 10000);
+ }
+ }
+ }
+
+ /**
+ * for when Orbot is started without showing the dialog by the EXTRA_START_ORBOT intent extra
+ */
+ private void dismissOrbotProgressDialog() {
+ if (mShowOrbotProgressDialog != null) {
+ mShowOrbotProgressDialog.dismiss();
+ }
+ }
+
+ @Override
+ public void onOrbotStarted() {
+ dismissOrbotProgressDialog();
+ sendMessage(MESSAGE_ORBOT_STARTED);
+ Intent intent = new Intent();
+ // send back unmodified CryptoInputParcel for a retry
+ intent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+
+ @Override
+ public void onNeutralButton() {
+ sendMessage(MESSAGE_ORBOT_IGNORE);
+ Intent intent = new Intent();
+ mCryptoInputParcel.addParcelableProxy(ParcelableProxy.getForNoProxy());
+ intent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+
+ @Override
+ public void onCancel() {
+ sendMessage(MESSAGE_DIALOG_CANCEL);
+ finish();
+ }
+
+ private void sendMessage(int what) {
+ if (mMessenger != null) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ try {
+ mMessenger.send(msg);
+ } catch (RemoteException e) {
+ Log.e(Constants.TAG, "Could not deliver message", e);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java
index c6431bfaf..e71349880 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java
@@ -17,16 +17,18 @@
package org.sufficientlysecure.keychain.ui;
+
import android.app.Activity;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
+import android.support.v7.app.AlertDialog;
import android.text.InputType;
import android.text.method.PasswordTransformationMethod;
import android.view.ContextThemeWrapper;
@@ -43,20 +45,21 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
-import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.remote.CryptoInputParcelCacheService;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
+import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences;
@@ -64,6 +67,8 @@ import org.sufficientlysecure.keychain.util.Preferences;
/**
* We can not directly create a dialog on the application context.
* This activity encapsulates a DialogFragment to emulate a dialog.
+ * NOTE: If no CryptoInputParcel is passed via EXTRA_CRYPTO_INPUT, the CryptoInputParcel is created
+ * internally and is NOT meant to be used by signing operations before adding a signature time
*/
public class PassphraseDialogActivity extends FragmentActivity {
@@ -71,11 +76,13 @@ public class PassphraseDialogActivity extends FragmentActivity {
public static final String EXTRA_REQUIRED_INPUT = "required_input";
public static final String EXTRA_SUBKEY_ID = "secret_key_id";
+ public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
// special extra for OpenPgpService
public static final String EXTRA_SERVICE_INTENT = "data";
+ private long mSubKeyId;
- private static final int REQUEST_CODE_ENTER_PATTERN = 2;
+ private CryptoInputParcel mCryptoInputParcel;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -90,20 +97,52 @@ public class PassphraseDialogActivity extends FragmentActivity {
);
}
+ mCryptoInputParcel = getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT);
+
+ if (mCryptoInputParcel == null) {
+ // not all usages of PassphraseActivity are from CryptoInputOperation
+ // NOTE: This CryptoInputParcel cannot be used for signing operations without setting
+ // signature time
+ mCryptoInputParcel = new CryptoInputParcel();
+ }
+
// this activity itself has no content view (see manifest)
- long keyId;
if (getIntent().hasExtra(EXTRA_SUBKEY_ID)) {
- keyId = getIntent().getLongExtra(EXTRA_SUBKEY_ID, 0);
+ mSubKeyId = getIntent().getLongExtra(EXTRA_SUBKEY_ID, 0);
} else {
RequiredInputParcel requiredInput = getIntent().getParcelableExtra(EXTRA_REQUIRED_INPUT);
switch (requiredInput.mType) {
case PASSPHRASE_SYMMETRIC: {
- keyId = Constants.key.symmetric;
+ mSubKeyId = Constants.key.symmetric;
break;
}
case PASSPHRASE: {
- keyId = requiredInput.getSubKeyId();
+
+ // handle empty passphrases by directly returning an empty crypto input parcel
+ try {
+ CanonicalizedSecretKeyRing pubRing =
+ new ProviderHelper(this).getCanonicalizedSecretKeyRing(
+ requiredInput.getMasterKeyId());
+ // use empty passphrase for empty passphrase
+ if (pubRing.getSecretKey(requiredInput.getSubKeyId()).getSecretKeyType() ==
+ SecretKeyType.PASSPHRASE_EMPTY) {
+ // also return passphrase back to activity
+ Intent returnIntent = new Intent();
+ mCryptoInputParcel.mPassphrase = new Passphrase("");
+ returnIntent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel);
+ setResult(RESULT_OK, returnIntent);
+ finish();
+ return;
+ }
+ } catch (NotFoundException e) {
+ Log.e(Constants.TAG, "Key not found?!", e);
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ mSubKeyId = requiredInput.getSubKeyId();
break;
}
default: {
@@ -112,64 +151,35 @@ public class PassphraseDialogActivity extends FragmentActivity {
}
}
- Intent serviceIntent = getIntent().getParcelableExtra(EXTRA_SERVICE_INTENT);
-
- show(this, keyId, serviceIntent);
}
@Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case REQUEST_CODE_ENTER_PATTERN: {
- /*
- * NOTE that there are 4 possible result codes!!!
- */
- switch (resultCode) {
- case RESULT_OK:
- // The user passed
- break;
- case RESULT_CANCELED:
- // The user cancelled the task
- break;
-// case LockPatternActivity.RESULT_FAILED:
-// // The user failed to enter the pattern
-// break;
-// case LockPatternActivity.RESULT_FORGOT_PATTERN:
-// // The user forgot the pattern and invoked your recovery Activity.
-// break;
- }
+ protected void onResumeFragments() {
+ super.onResumeFragments();
- /*
- * In any case, there's always a key EXTRA_RETRY_COUNT, which holds
- * the number of tries that the user did.
- */
-// int retryCount = data.getIntExtra(
-// LockPatternActivity.EXTRA_RETRY_COUNT, 0);
+ /* Show passphrase dialog to cache a new passphrase the user enters for using it later for
+ * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks
+ * for a symmetric passphrase
+ */
- break;
- }
- }
+ Intent serviceIntent = getIntent().getParcelableExtra(EXTRA_SERVICE_INTENT);
+
+ PassphraseDialogFragment frag = new PassphraseDialogFragment();
+ Bundle args = new Bundle();
+ args.putLong(EXTRA_SUBKEY_ID, mSubKeyId);
+ args.putParcelable(EXTRA_SERVICE_INTENT, serviceIntent);
+ frag.setArguments(args);
+ frag.show(getSupportFragmentManager(), "passphraseDialog");
}
- /**
- * Shows passphrase dialog to cache a new passphrase the user enters for using it later for
- * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks
- * for a symmetric passphrase
- */
- public static void show(final FragmentActivity context, final long keyId, final Intent serviceIntent) {
- DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
- public void run() {
- // do NOT check if the key even needs a passphrase. that's not our job here.
- PassphraseDialogFragment frag = new PassphraseDialogFragment();
- Bundle args = new Bundle();
- args.putLong(EXTRA_SUBKEY_ID, keyId);
- args.putParcelable(EXTRA_SERVICE_INTENT, serviceIntent);
-
- frag.setArguments(args);
-
- frag.show(context.getSupportFragmentManager(), "passphraseDialog");
- }
- });
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ DialogFragment dialog = (DialogFragment) getSupportFragmentManager().findFragmentByTag("passphraseDialog");
+ if (dialog != null) {
+ dialog.dismiss();
+ }
}
public static class PassphraseDialogFragment extends DialogFragment implements TextView.OnEditorActionListener {
@@ -183,17 +193,12 @@ public class PassphraseDialogActivity extends FragmentActivity {
private Intent mServiceIntent;
- /**
- * Creates dialog
- */
+ @NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity();
- // if the dialog is displayed from the application class, design is missing
- // hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay
- ContextThemeWrapper theme = new ContextThemeWrapper(activity,
- R.style.Theme_AppCompat_Light_Dialog);
+ ContextThemeWrapper theme = ThemeChanger.getDialogThemeWrapper(activity);
mSubKeyId = getArguments().getLong(EXTRA_SUBKEY_ID);
mServiceIntent = getArguments().getParcelable(EXTRA_SERVICE_INTENT);
@@ -246,12 +251,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
userId = null;
}
- /* Get key type for message */
- // find a master key id for our key
- long masterKeyId = new ProviderHelper(activity).getMasterKeyId(mSubKeyId);
- CachedPublicKeyRing keyRing = new ProviderHelper(activity).getCachedPublicKeyRing(masterKeyId);
- // get the type of key (from the database)
- keyType = keyRing.getSecretKeyType(mSubKeyId);
+ keyType = mSecretRing.getSecretKey(mSubKeyId).getSecretKeyType();
switch (keyType) {
case PASSPHRASE:
message = getString(R.string.passphrase_for, userId);
@@ -284,51 +284,42 @@ public class PassphraseDialogActivity extends FragmentActivity {
mPassphraseText.setText(message);
- if (keyType == CanonicalizedSecretKey.SecretKeyType.PATTERN) {
- // start pattern dialog and show progress circle here...
-// Intent patternActivity = new Intent(getActivity(), LockPatternActivity.class);
-// patternActivity.putExtra(LockPatternActivity.EXTRA_PATTERN, "123");
-// startActivityForResult(patternActivity, REQUEST_CODE_ENTER_PATTERN);
- mInput.setVisibility(View.INVISIBLE);
- mProgress.setVisibility(View.VISIBLE);
- } else {
- // Hack to open keyboard.
- // This is the only method that I found to work across all Android versions
- // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/
- // Notes: * onCreateView can't be used because we want to add buttons to the dialog
- // * opening in onActivityCreated does not work on Android 4.4
- mPassphraseEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- mPassphraseEditText.post(new Runnable() {
- @Override
- public void run() {
- if (getActivity() == null || mPassphraseEditText == null) {
- return;
- }
- InputMethodManager imm = (InputMethodManager) getActivity()
- .getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT);
+ // Hack to open keyboard.
+ // This is the only method that I found to work across all Android versions
+ // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/
+ // Notes: * onCreateView can't be used because we want to add buttons to the dialog
+ // * opening in onActivityCreated does not work on Android 4.4
+ mPassphraseEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mPassphraseEditText.post(new Runnable() {
+ @Override
+ public void run() {
+ if (getActivity() == null || mPassphraseEditText == null) {
+ return;
}
- });
- }
- });
- mPassphraseEditText.requestFocus();
-
- mPassphraseEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE);
- mPassphraseEditText.setOnEditorActionListener(this);
-
- if (keyType == CanonicalizedSecretKey.SecretKeyType.DIVERT_TO_CARD && Preferences.getPreferences(activity).useNumKeypadForYubiKeyPin()) {
- mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_VARIATION_PASSWORD);
- } else if (keyType == CanonicalizedSecretKey.SecretKeyType.PIN) {
- mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_VARIATION_PASSWORD);
- } else {
- mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ InputMethodManager imm = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT);
+ }
+ });
}
+ });
+ mPassphraseEditText.requestFocus();
+
+ mPassphraseEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE);
+ mPassphraseEditText.setOnEditorActionListener(this);
+ if ((keyType == CanonicalizedSecretKey.SecretKeyType.DIVERT_TO_CARD && Preferences.getPreferences(activity).useNumKeypadForYubiKeyPin())
+ || keyType == CanonicalizedSecretKey.SecretKeyType.PIN) {
+ mPassphraseEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
mPassphraseEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());
+ } else {
+ mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
}
+ mPassphraseEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());
+
AlertDialog dialog = alert.create();
dialog.setButton(DialogInterface.BUTTON_POSITIVE,
activity.getString(R.string.btn_unlock), (DialogInterface.OnClickListener) null);
@@ -347,11 +338,16 @@ public class PassphraseDialogActivity extends FragmentActivity {
public void onClick(View v) {
final Passphrase passphrase = new Passphrase(mPassphraseEditText);
+ CryptoInputParcel cryptoInputParcel =
+ ((PassphraseDialogActivity) getActivity()).mCryptoInputParcel;
+
// Early breakout if we are dealing with a symmetric key
if (mSecretRing == null) {
- PassphraseCacheService.addCachedPassphrase(getActivity(),
- Constants.key.symmetric, Constants.key.symmetric, passphrase,
- getString(R.string.passp_cache_notif_pwd));
+ if (cryptoInputParcel.mCachePassphrase) {
+ PassphraseCacheService.addCachedPassphrase(getActivity(),
+ Constants.key.symmetric, Constants.key.symmetric, passphrase,
+ getString(R.string.passp_cache_notif_pwd));
+ }
finishCaching(passphrase);
return;
@@ -404,15 +400,24 @@ public class PassphraseDialogActivity extends FragmentActivity {
return;
}
- // cache the new passphrase
- Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
+ // cache the new passphrase as specified in CryptoInputParcel
+ Log.d(Constants.TAG, "Everything okay!");
- try {
- PassphraseCacheService.addCachedPassphrase(getActivity(),
- mSecretRing.getMasterKeyId(), mSubKeyId, passphrase,
- mSecretRing.getPrimaryUserIdWithFallback());
- } catch (PgpKeyNotFoundException e) {
- Log.e(Constants.TAG, "adding of a passphrase failed", e);
+ CryptoInputParcel cryptoInputParcel
+ = ((PassphraseDialogActivity) getActivity()).mCryptoInputParcel;
+
+ if (cryptoInputParcel.mCachePassphrase) {
+ Log.d(Constants.TAG, "Caching entered passphrase");
+
+ try {
+ PassphraseCacheService.addCachedPassphrase(getActivity(),
+ mSecretRing.getMasterKeyId(), mSubKeyId, passphrase,
+ mSecretRing.getPrimaryUserIdWithFallback());
+ } catch (PgpKeyNotFoundException e) {
+ Log.e(Constants.TAG, "adding of a passphrase failed", e);
+ }
+ } else {
+ Log.d(Constants.TAG, "Not caching entered passphrase!");
}
finishCaching(passphrase);
@@ -428,9 +433,12 @@ public class PassphraseDialogActivity extends FragmentActivity {
return;
}
- CryptoInputParcel inputParcel = new CryptoInputParcel(null, passphrase);
+ CryptoInputParcel inputParcel =
+ ((PassphraseDialogActivity) getActivity()).mCryptoInputParcel;
+ inputParcel.mPassphrase = passphrase;
if (mServiceIntent != null) {
- CryptoInputParcelCacheService.addCryptoInputParcel(getActivity(), mServiceIntent, inputParcel);
+ CryptoInputParcelCacheService.addCryptoInputParcel(getActivity(), mServiceIntent,
+ inputParcel);
getActivity().setResult(RESULT_OK, mServiceIntent);
} else {
// also return passphrase back to activity
@@ -449,20 +457,16 @@ public class PassphraseDialogActivity extends FragmentActivity {
// note we need no synchronization here, this variable is only accessed in the ui thread
mIsCancelled = true;
+
+ getActivity().setResult(RESULT_CANCELED);
+ getActivity().finish();
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
- if (getActivity() == null) {
- return;
- }
-
hideKeyboard();
-
- getActivity().setResult(RESULT_CANCELED);
- getActivity().finish();
}
private void hideKeyboard() {
@@ -476,11 +480,9 @@ public class PassphraseDialogActivity extends FragmentActivity {
inputManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
- /**
- * Associate the "done" button on the soft keyboard with the okay button in the view
- */
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ // Associate the "done" button on the soft keyboard with the okay button in the view
if (EditorInfo.IME_ACTION_DONE == actionId) {
AlertDialog dialog = ((AlertDialog) getDialog());
Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java
deleted file mode 100644
index 2e838535d..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java
+++ /dev/null
@@ -1,575 +0,0 @@
-/*
- * 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.annotation.TargetApi;
-import android.app.AlertDialog;
-import android.app.PendingIntent;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.nfc.FormatException;
-import android.nfc.NdefMessage;
-import android.nfc.NdefRecord;
-import android.nfc.NfcAdapter;
-import android.nfc.Tag;
-import android.nfc.tech.Ndef;
-import android.os.Build;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.FragmentTransaction;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import org.sufficientlysecure.keychain.R;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.security.SecureRandom;
-import java.util.Arrays;
-import java.util.List;
-
-@TargetApi(Build.VERSION_CODES.HONEYCOMB)
-public class PassphraseWizardActivity extends FragmentActivity {
-//public class PassphraseWizardActivity extends FragmentActivity implements LockPatternView.OnPatternListener {
- //create or authenticate
- public String selectedAction;
- //for lockpattern
- public static char[] pattern;
- private static String passphrase = "";
- //nfc string
- private static byte[] output = new byte[8];
-
- public static final String CREATE_METHOD = "create";
- public static final String AUTHENTICATION = "authenticate";
-
- NfcAdapter adapter;
- PendingIntent pendingIntent;
- IntentFilter writeTagFilters[];
- boolean writeMode;
- Tag myTag;
- boolean writeNFC = false;
- boolean readNFC = false;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (getActionBar() != null) {
- getActionBar().setTitle(R.string.unlock_method);
- }
-
- selectedAction = getIntent().getAction();
- if (savedInstanceState == null) {
- SelectMethods selectMethods = new SelectMethods();
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- transaction.add(R.id.fragmentContainer, selectMethods).commit();
- }
- setContentView(R.layout.passphrase_wizard);
-
- adapter = NfcAdapter.getDefaultAdapter(this);
- if (adapter != null) {
- pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, PassphraseWizardActivity.class).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
- IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
- tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
- writeTagFilters = new IntentFilter[]{tagDetected};
- }
- }
-
- public void noPassphrase(View view) {
- passphrase = "";
- Toast.makeText(this, R.string.no_passphrase_set, Toast.LENGTH_SHORT).show();
- this.finish();
- }
-
- public void passphrase(View view) {
- Passphrase passphrase = new Passphrase();
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- transaction.replace(R.id.fragmentContainer, passphrase).addToBackStack(null).commit();
- }
-
- public void startLockpattern(View view) {
- if (getActionBar() != null) {
- getActionBar().setTitle(R.string.draw_lockpattern);
- }
-// LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction);
-// LockPatternFragment lpf = LockPatternFragment.newInstance("asd");
-
-// FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-// transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit();
- }
-
- public void cancel(View view) {
- this.finish();
- }
-
- public void savePassphrase(View view) {
- EditText passphrase = (EditText) findViewById(R.id.passphrase);
- passphrase.setError(null);
- String pw = passphrase.getText().toString();
- //check and save passphrase
- if (selectedAction.equals(CREATE_METHOD)) {
- EditText passphraseAgain = (EditText) findViewById(R.id.passphraseAgain);
- passphraseAgain.setError(null);
- String pwAgain = passphraseAgain.getText().toString();
-
- if (!TextUtils.isEmpty(pw)) {
- if (!TextUtils.isEmpty(pwAgain)) {
- if (pw.equals(pwAgain)) {
- PassphraseWizardActivity.passphrase = pw;
- Toast.makeText(this, getString(R.string.passphrase_saved), Toast.LENGTH_SHORT).show();
- this.finish();
- } else {
- passphrase.setError(getString(R.string.passphrase_invalid));
- passphrase.requestFocus();
- }
- } else {
- passphraseAgain.setError(getString(R.string.missing_passphrase));
- passphraseAgain.requestFocus();
- }
- } else {
- passphrase.setError(getString(R.string.missing_passphrase));
- passphrase.requestFocus();
- }
- }
- //check for right passphrase
- if (selectedAction.equals(AUTHENTICATION)) {
- if (pw.equals(PassphraseWizardActivity.passphrase)) {
- Toast.makeText(this, getString(R.string.unlocked), Toast.LENGTH_SHORT).show();
- this.finish();
- } else {
- passphrase.setError(getString(R.string.passphrase_invalid));
- passphrase.requestFocus();
- }
- }
- }
-
- public void NFC(View view) {
- if (adapter != null) {
- if (getActionBar() != null) {
- getActionBar().setTitle(R.string.nfc_title);
- }
- NFCFragment nfc = new NFCFragment();
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- transaction.replace(R.id.fragmentContainer, nfc).addToBackStack(null).commit();
-
- //if you want to create a new method or just authenticate
- if (CREATE_METHOD.equals(selectedAction)) {
- writeNFC = true;
- } else if (AUTHENTICATION.equals(selectedAction)) {
- readNFC = true;
- }
-
- if (!adapter.isEnabled()) {
- showAlertDialog(getString(R.string.enable_nfc), true);
- }
- } else {
- showAlertDialog(getString(R.string.no_nfc_support), false);
- }
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
- myTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
-
- if (writeNFC && CREATE_METHOD.equals(selectedAction)) {
- //write new password on NFC tag
- try {
- if (myTag != null) {
- write(myTag);
- writeNFC = false; //just write once
- Toast.makeText(this, R.string.nfc_write_succesful, Toast.LENGTH_SHORT).show();
- //advance to lockpattern
-// LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction);
-// FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-// transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit();
- }
- } catch (IOException | FormatException e) {
- e.printStackTrace();
- }
-
- } else if (readNFC && AUTHENTICATION.equals(selectedAction)) {
- //read pw from NFC tag
- try {
- if (myTag != null) {
- //if tag detected, read tag
- String pwtag = read(myTag);
- if (output != null && pwtag.equals(output.toString())) {
-
- //passwort matches, go to next view
- Toast.makeText(this, R.string.passphrases_match + "!", Toast.LENGTH_SHORT).show();
-
-// LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction);
-// FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-// transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit();
- readNFC = false; //just once
- } else {
- //passwort doesnt match
- TextView nfc = (TextView) findViewById(R.id.nfcText);
- nfc.setText(R.string.nfc_wrong_tag);
- }
- }
- } catch (IOException | FormatException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- private void write(Tag tag) throws IOException, FormatException {
- //generate new random key and write them on the tag
- SecureRandom sr = new SecureRandom();
- sr.nextBytes(output);
- NdefRecord[] records = {createRecord(output.toString())};
- NdefMessage message = new NdefMessage(records);
- Ndef ndef = Ndef.get(tag);
- ndef.connect();
- ndef.writeNdefMessage(message);
- ndef.close();
- }
-
- private String read(Tag tag) throws IOException, FormatException {
- //read string from tag
- String password = null;
- Ndef ndef = Ndef.get(tag);
- ndef.connect();
- NdefMessage ndefMessage = ndef.getCachedNdefMessage();
-
- NdefRecord[] records = ndefMessage.getRecords();
- for (NdefRecord ndefRecord : records) {
- if (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
- try {
- password = readText(ndefRecord);
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- }
- }
- ndef.close();
- return password;
- }
-
- private String readText(NdefRecord record) throws UnsupportedEncodingException {
- //low-level method for reading nfc
- byte[] payload = record.getPayload();
- String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";
- int languageCodeLength = payload[0] & 0063;
- return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
- }
-
- private NdefRecord createRecord(String text) throws UnsupportedEncodingException {
- //low-level method for writing nfc
- String lang = "en";
- byte[] textBytes = text.getBytes();
- byte[] langBytes = lang.getBytes("US-ASCII");
- int langLength = langBytes.length;
- int textLength = textBytes.length;
- byte[] payload = new byte[1 + langLength + textLength];
-
- // set status byte (see NDEF spec for actual bits)
- payload[0] = (byte) langLength;
- // copy langbytes and textbytes into payload
- System.arraycopy(langBytes, 0, payload, 1, langLength);
- System.arraycopy(textBytes, 0, payload, 1 + langLength, textLength);
- return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], payload);
- }
-
- public void showAlertDialog(String message, boolean nfc) {
- //This method shows an AlertDialog
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
- alert.setTitle("Information").setMessage(message).setPositiveButton("Ok",
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- }
- }
- );
- if (nfc) {
-
- alert.setNeutralButton(R.string.nfc_settings,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialogInterface, int i) {
- startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
- }
- }
- );
- }
- alert.show();
- }
-
- @Override
- public void onPause() {
- //pause this app and free nfc intent
- super.onPause();
- if (adapter != null) {
- WriteModeOff();
- }
- }
-
- @Override
- public void onResume() {
- //resume this app and get nfc intent
- super.onResume();
- if (adapter != null) {
- WriteModeOn();
- }
- }
-
- private void WriteModeOn() {
- //enable nfc for this view
- writeMode = true;
- adapter.enableForegroundDispatch(this, pendingIntent, writeTagFilters, null);
- }
-
- private void WriteModeOff() {
- //disable nfc for this view
- writeMode = false;
- adapter.disableForegroundDispatch(this);
- }
-
- public static class SelectMethods extends Fragment {
-// private OnFragmentInteractionListener mListener;
-
- /**
- * Use this factory method to create a new instance of
- * this fragment using the provided parameters.
- */
- public SelectMethods() {
-
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- if (getActivity().getActionBar() != null) {
- getActivity().getActionBar().setTitle(R.string.unlock_method);
- }
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- // Inflate the layout for this fragment
- return inflater.inflate(R.layout.passphrase_wizard_fragment_select_methods, container, false);
- }
-
-// @Override
-// public void onAttach(Activity activity) {
-// super.onAttach(activity);
-// try {
-// mListener = (OnFragmentInteractionListener) activity;
-// } catch (ClassCastException e) {
-// throw new ClassCastException(activity.toString()
-// + " must implement OnFragmentInteractionListener");
-// }
-// }
-//
-// @Override
-// public void onDetach() {
-// super.onDetach();
-// mListener = null;
-// }
-
- /**
- * This interface must be implemented by activities that contain this
- * fragment to allow an interaction in this fragment to be communicated
- * to the activity and potentially other fragments contained in that
- * activity.
- * <p/>
- * See the Android Training lesson <a href=
- * "http://developer.android.com/training/basics/fragments/communicating.html"
- * >Communicating with Other Fragments</a> for more information.
- */
-// public static interface OnFragmentInteractionListener {
-// public void onFragmentInteraction(Uri uri);
-// }
-
- }
-
-
- // /**
-// * A simple {@link android.support.v4.app.Fragment} subclass.
-// * Activities that contain this fragment must implement the
-// * {@link com.haibison.android.lockpattern.Passphrase.OnFragmentInteractionListener} interface
-// * to handle interaction events.
-// */
- public static class Passphrase extends Fragment {
-
-// private OnFragmentInteractionListener mListener;
-
- public Passphrase() {
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- // Inflate the layout for this fragment
- View view = inflater.inflate(R.layout.passphrase_wizard_fragment_passphrase, container, false);
- EditText passphraseAgain = (EditText) view.findViewById(R.id.passphraseAgain);
- TextView passphraseText = (TextView) view.findViewById(R.id.passphraseText);
- TextView passphraseTextAgain = (TextView) view.findViewById(R.id.passphraseTextAgain);
- String selectedAction = getActivity().getIntent().getAction();
- if (selectedAction.equals(AUTHENTICATION)) {
- passphraseAgain.setVisibility(View.GONE);
- passphraseTextAgain.setVisibility(View.GONE);
- passphraseText.setText(R.string.enter_passphrase);
-// getActivity().getActionBar().setTitle(R.string.enter_passphrase);
- } else if (selectedAction.equals(CREATE_METHOD)) {
- passphraseAgain.setVisibility(View.VISIBLE);
- passphraseTextAgain.setVisibility(View.VISIBLE);
- passphraseText.setText(R.string.passphrase);
-// getActivity().getActionBar().setTitle(R.string.set_passphrase);
- }
- return view;
- }
-
-// @Override
-// public void onAttach(Activity activity) {
-// super.onAttach(activity);
-// try {
-// mListener = (OnFragmentInteractionListener) activity;
-// } catch (ClassCastException e) {
-// throw new ClassCastException(activity.toString()
-// + " must implement OnFragmentInteractionListener");
-// }
-// }
-//
-// @Override
-// public void onDetach() {
-// super.onDetach();
-// mListener = null;
-// }
-
-// /**
-// * This interface must be implemented by activities that contain this
-// * fragment to allow an interaction in this fragment to be communicated
-// * to the activity and potentially other fragments contained in that
-// * activity.
-// * <p/>
-// * See the Android Training lesson <a href=
-// * "http://developer.android.com/training/basics/fragments/communicating.html"
-// * >Communicating with Other Fragments</a> for more information.
-// */
-// public interface OnFragmentInteractionListener {
-// public void onFragmentInteraction(Uri uri);
-// }
- }
-
-
- /**
- * A simple {@link android.support.v4.app.Fragment} subclass.
- * Activities that contain this fragment must implement the
- * interface
- * to handle interaction events.
- * Use the method to
- * create an instance of this fragment.
- */
- public static class NFCFragment extends Fragment {
- // TODO: Rename parameter arguments, choose names that match
- // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
- private static final String ARG_PARAM1 = "param1";
- private static final String ARG_PARAM2 = "param2";
-
- // TODO: Rename and change types of parameters
- private String mParam1;
- private String mParam2;
-
-// private OnFragmentInteractionListener mListener;
-
- /**
- * Use this factory method to create a new instance of
- * this fragment using the provided parameters.
- *
- * @param param1 Parameter 1.
- * @param param2 Parameter 2.
- * @return A new instance of fragment SelectMethods.
- */
- // TODO: Rename and change types and number of parameters
- public static NFCFragment newInstance(String param1, String param2) {
- NFCFragment fragment = new NFCFragment();
- Bundle args = new Bundle();
- args.putString(ARG_PARAM1, param1);
- args.putString(ARG_PARAM2, param2);
- fragment.setArguments(args);
- return fragment;
- }
-
- public NFCFragment() {
- // Required empty public constructor
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (getArguments() != null) {
- mParam1 = getArguments().getString(ARG_PARAM1);
- mParam2 = getArguments().getString(ARG_PARAM2);
- }
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- // Inflate the layout for this fragment
- return inflater.inflate(R.layout.passphrase_wizard_fragment_nfc, container, false);
- }
-
-// // TODO: Rename method, update argument and hook method into UI event
-// public void onButtonPressed(Uri uri) {
-// if (mListener != null) {
-// mListener.onFragmentInteraction(uri);
-// }
-// }
-
-// @Override
-// public void onAttach(Activity activity) {
-// super.onAttach(activity);
-// try {
-// mListener = (OnFragmentInteractionListener) activity;
-// } catch (ClassCastException e) {
-// throw new ClassCastException(activity.toString()
-// + " must implement OnFragmentInteractionListener");
-// }
-// }
-
-
-// @Override
-// public void onDetach() {
-// super.onDetach();
-// mListener = null;
-// }
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java
index d4858ee5d..e54852f1b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java
@@ -85,11 +85,12 @@ public class QrCodeViewActivity extends BaseActivity {
ActivityCompat.finishAfterTransition(QrCodeViewActivity.this);
}
- String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
- String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
-
+ Uri uri = new Uri.Builder()
+ .scheme(Constants.FINGERPRINT_SCHEME)
+ .opaquePart(KeyFormattingUtils.convertFingerprintToHex(blob))
+ .build();
// create a minimal size qr code, we can keep this in ram no problem
- final Bitmap qrCode = QrCodeUtils.getQRCodeBitmap(qrCodeContent, 0);
+ final Bitmap qrCode = QrCodeUtils.getQRCodeBitmap(uri, 0);
mQrCode.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/RetryUploadDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/RetryUploadDialogActivity.java
new file mode 100644
index 000000000..2a00e8b70
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/RetryUploadDialogActivity.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentActivity;
+import android.view.ContextThemeWrapper;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
+import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
+
+public class RetryUploadDialogActivity extends FragmentActivity {
+
+ public static final String EXTRA_CRYPTO_INPUT = "extra_crypto_input";
+
+ public static final String RESULT_CRYPTO_INPUT = "result_crypto_input";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ UploadRetryDialogFragment.newInstance().show(getSupportFragmentManager(),
+ "uploadRetryDialog");
+ }
+
+ public static class UploadRetryDialogFragment extends DialogFragment {
+ public static UploadRetryDialogFragment newInstance() {
+ return new UploadRetryDialogFragment();
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+
+ ContextThemeWrapper theme = ThemeChanger.getDialogThemeWrapper(getActivity());
+
+ CustomAlertDialogBuilder dialogBuilder = new CustomAlertDialogBuilder(theme);
+ dialogBuilder.setTitle(R.string.retry_up_dialog_title);
+ dialogBuilder.setMessage(R.string.retry_up_dialog_message);
+
+ dialogBuilder.setNegativeButton(R.string.retry_up_dialog_btn_cancel,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ getActivity().setResult(RESULT_CANCELED);
+ getActivity().finish();
+ }
+ });
+
+ dialogBuilder.setPositiveButton(R.string.retry_up_dialog_btn_reupload,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = new Intent();
+ intent.putExtra(RESULT_CRYPTO_INPUT, getActivity()
+ .getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT));
+ getActivity().setResult(RESULT_OK, intent);
+ getActivity().finish();
+ }
+ });
+
+ return dialogBuilder.show();
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java
index aa3c36d11..534dbfd05 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java
@@ -18,15 +18,11 @@
package org.sufficientlysecure.keychain.ui;
import android.annotation.TargetApi;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.NumberPicker;
@@ -38,10 +34,10 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
+import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
@@ -53,7 +49,8 @@ import edu.cmu.cylab.starslinger.exchange.ExchangeActivity;
import edu.cmu.cylab.starslinger.exchange.ExchangeConfig;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
-public class SafeSlingerActivity extends BaseActivity {
+public class SafeSlingerActivity extends BaseActivity
+ implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
private static final int REQUEST_CODE_SAFE_SLINGER = 211;
@@ -62,6 +59,12 @@ public class SafeSlingerActivity extends BaseActivity {
private long mMasterKeyId;
private int mSelectedNumber = 2;
+ // for CryptoOperationHelper
+ private ArrayList<ParcelableKeyRing> mKeyList;
+ private String mKeyserver;
+ private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
+
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -79,7 +82,7 @@ public class SafeSlingerActivity extends BaseActivity {
});
ImageView buttonIcon = (ImageView) findViewById(R.id.safe_slinger_button_image);
- buttonIcon.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
+ buttonIcon.setColorFilter(FormattingUtils.getColorFromAttr(this, R.attr.colorTertiaryText),
PorterDuff.Mode.SRC_IN);
View button = findViewById(R.id.safe_slinger_button);
@@ -117,69 +120,17 @@ public class SafeSlingerActivity extends BaseActivity {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mOperationHelper != null) {
+ mOperationHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+
if (requestCode == REQUEST_CODE_SAFE_SLINGER) {
if (resultCode == ExchangeActivity.RESULT_EXCHANGE_CANCELED) {
return;
}
- final FragmentActivity activity = SafeSlingerActivity.this;
-
- // Message is received after importing is done in KeychainIntentService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- activity,
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL,
- true,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
- if (returnData == null) {
- return;
- }
- final ImportKeyResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null) {
- Log.e(Constants.TAG, "result == null");
- return;
- }
-
- if (!result.success()) {
-// result.createNotify(activity).show();
- // only return if no success...
- Intent data = new Intent();
- data.putExtras(returnData);
- setResult(RESULT_OK, data);
- finish();
- return;
- }
-
-// if (mExchangeMasterKeyId == null) {
-// return;
-// }
-
- Intent certifyIntent = new Intent(activity, CertifyKeyActivity.class);
- certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
- certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, result.getImportedMasterKeyIds());
- certifyIntent.putExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, mMasterKeyId);
- startActivityForResult(certifyIntent, 0);
-
-// mExchangeMasterKeyId = null;
- }
- }
- };
-
Log.d(Constants.TAG, "importKeys started");
- // Send all information needed to service to import key in other thread
- Intent intent = new Intent(activity, KeychainIntentService.class);
-
- intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING);
-
// instead of giving the entries by Intent extra, cache them into a
// file to prevent Java Binder problems on heavy imports
// read FileImportCache for more info.
@@ -190,25 +141,18 @@ public class SafeSlingerActivity extends BaseActivity {
// We parcel this iteratively into a file - anything we can
// display here, we should be able to import.
ParcelableFileCache<ParcelableKeyRing> cache =
- new ParcelableFileCache<>(activity, "key_import.pcl");
+ new ParcelableFileCache<>(this, "key_import.pcl");
cache.writeCache(it.size(), it.iterator());
- // fill values for this action
- Bundle bundle = new Bundle();
- intent.putExtra(KeychainIntentService.EXTRA_DATA, bundle);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(activity);
+ mOperationHelper =
+ new CryptoOperationHelper(1, this, this, R.string.progress_importing);
- // start service with intent
- activity.startService(intent);
+ mKeyList = null;
+ mKeyserver = null;
+ mOperationHelper.cryptoOperation();
} catch (IOException e) {
Log.e(Constants.TAG, "Problem writing cache file", e);
- Notify.create(activity, "Problem writing cache file!", Notify.Style.ERROR).show();
+ Notify.create(this, "Problem writing cache file!", Notify.Style.ERROR).show();
}
} else {
// give everything else down to KeyListActivity!
@@ -235,4 +179,39 @@ public class SafeSlingerActivity extends BaseActivity {
return list;
}
+ // CryptoOperationHelper.Callback functions
+
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(mKeyList, mKeyserver);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ImportKeyResult result) {
+ Intent certifyIntent = new Intent(this, CertifyKeyActivity.class);
+ certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
+ certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, result.getImportedMasterKeyIds());
+ certifyIntent.putExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, mMasterKeyId);
+ startActivityForResult(certifyIntent, 0);
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(ImportKeyResult result) {
+ Bundle returnData = new Bundle();
+ returnData.putParcelable(OperationResult.EXTRA_RESULT, result);
+ Intent data = new Intent();
+ data.putExtras(returnData);
+ setResult(RESULT_OK, data);
+ finish();
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
index 442bdf8f7..4077f1c84 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
*
* This program is free software: you can redistribute it and/or modify
@@ -17,42 +18,59 @@
package org.sufficientlysecure.keychain.ui;
-import android.annotation.TargetApi;
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.Activity;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.os.Build;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
+import android.preference.EditTextPreference;
+import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
+import android.preference.SwitchPreference;
+import android.provider.ContactsContract;
import android.support.v7.widget.Toolbar;
+import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
-import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.AppCompatPreferenceActivity;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
import org.sufficientlysecure.keychain.ui.widget.IntegerListPreference;
+import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
import java.util.List;
-public class SettingsActivity extends PreferenceActivity {
+public class SettingsActivity extends AppCompatPreferenceActivity {
public static final String ACTION_PREFS_CLOUD = "org.sufficientlysecure.keychain.ui.PREFS_CLOUD";
public static final String ACTION_PREFS_ADV = "org.sufficientlysecure.keychain.ui.PREFS_ADV";
+ public static final String ACTION_PREFS_PROXY = "org.sufficientlysecure.keychain.ui.PREFS_PROXY";
+ public static final String ACTION_PREFS_GUI = "org.sufficientlysecure.keychain.ui.PREFS_GUI";
public static final int REQUEST_CODE_KEYSERVER_PREF = 0x00007005;
private PreferenceScreen mKeyServerPreference = null;
private static Preferences sPreferences;
+ private ThemeChanger mThemeChanger;
@Override
protected void onCreate(Bundle savedInstanceState) {
sPreferences = Preferences.getPreferences(this);
+ mThemeChanger = new ThemeChanger(this);
+ mThemeChanger.setThemes(R.style.Theme_Keychain_Light, R.style.Theme_Keychain_Dark);
+ mThemeChanger.changeTheme();
super.onCreate(savedInstanceState);
setupToolbar();
@@ -76,14 +94,14 @@ public class SettingsActivity extends PreferenceActivity {
}
});
initializeSearchKeyserver(
- (CheckBoxPreference) findPreference(Constants.Pref.SEARCH_KEYSERVER)
+ (SwitchPreference) findPreference(Constants.Pref.SEARCH_KEYSERVER)
);
initializeSearchKeybase(
- (CheckBoxPreference) findPreference(Constants.Pref.SEARCH_KEYBASE)
+ (SwitchPreference) findPreference(Constants.Pref.SEARCH_KEYBASE)
);
} else if (action != null && action.equals(ACTION_PREFS_ADV)) {
- addPreferencesFromResource(R.xml.adv_preferences);
+ addPreferencesFromResource(R.xml.passphrase_preferences);
initializePassphraseCacheSubs(
(CheckBoxPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_SUBS));
@@ -91,28 +109,29 @@ public class SettingsActivity extends PreferenceActivity {
initializePassphraseCacheTtl(
(IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL));
- int[] valueIds = new int[]{
- CompressionAlgorithmTags.UNCOMPRESSED,
- CompressionAlgorithmTags.ZIP,
- CompressionAlgorithmTags.ZLIB,
- CompressionAlgorithmTags.BZIP2,
- };
- String[] entries = new String[]{
- getString(R.string.choice_none) + " (" + getString(R.string.compression_fast) + ")",
- "ZIP (" + getString(R.string.compression_fast) + ")",
- "ZLIB (" + getString(R.string.compression_fast) + ")",
- "BZIP2 (" + getString(R.string.compression_very_slow) + ")",};
- String[] values = new String[valueIds.length];
- for (int i = 0; i < values.length; ++i) {
- values[i] = "" + valueIds[i];
- }
-
initializeUseDefaultYubiKeyPin(
(CheckBoxPreference) findPreference(Constants.Pref.USE_DEFAULT_YUBIKEY_PIN));
initializeUseNumKeypadForYubiKeyPin(
(CheckBoxPreference) findPreference(Constants.Pref.USE_NUMKEYPAD_FOR_YUBIKEY_PIN));
+ } else if (action != null && action.equals(ACTION_PREFS_GUI)) {
+ addPreferencesFromResource(R.xml.gui_preferences);
+
+ initializeTheme((ListPreference) findPreference(Constants.Pref.THEME));
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ if (mThemeChanger.changeTheme()) {
+ Intent intent = getIntent();
+ finish();
+ overridePendingTransition(0, 0);
+ startActivity(intent);
+ overridePendingTransition(0, 0);
}
}
@@ -122,13 +141,14 @@ public class SettingsActivity extends PreferenceActivity {
private void setupToolbar() {
ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
LinearLayout content = (LinearLayout) root.getChildAt(0);
- LinearLayout toolbarContainer = (LinearLayout) View.inflate(this, R.layout.preference_toolbar_activity, null);
+ LinearLayout toolbarContainer = (LinearLayout) View.inflate(this, R.layout.preference_toolbar, null);
root.removeAllViews();
toolbarContainer.addView(content);
root.addView(toolbarContainer);
Toolbar toolbar = (Toolbar) toolbarContainer.findViewById(R.id.toolbar);
+
toolbar.setTitle(R.string.title_preferences);
toolbar.setNavigationIcon(getResources().getDrawable(R.drawable.ic_arrow_back_white_24dp));
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@@ -141,34 +161,13 @@ public class SettingsActivity extends PreferenceActivity {
}
@Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case REQUEST_CODE_KEYSERVER_PREF: {
- if (resultCode == RESULT_CANCELED || data == null) {
- return;
- }
- String servers[] = data
- .getStringArrayExtra(SettingsKeyServerActivity.EXTRA_KEY_SERVERS);
- sPreferences.setKeyServers(servers);
- mKeyServerPreference.setSummary(keyserverSummary(this));
- break;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
- break;
- }
- }
- }
-
- @Override
public void onBuildHeaders(List<Header> target) {
super.onBuildHeaders(target);
loadHeadersFromResource(R.xml.preference_headers, target);
}
/**
- * This fragment shows the Cloud Search preferences in android 3.0+
+ * This fragment shows the Cloud Search preferences
*/
public static class CloudSearchPrefsFragment extends PreferenceFragment {
@@ -196,10 +195,10 @@ public class SettingsActivity extends PreferenceActivity {
}
});
initializeSearchKeyserver(
- (CheckBoxPreference) findPreference(Constants.Pref.SEARCH_KEYSERVER)
+ (SwitchPreference) findPreference(Constants.Pref.SEARCH_KEYSERVER)
);
initializeSearchKeybase(
- (CheckBoxPreference) findPreference(Constants.Pref.SEARCH_KEYBASE)
+ (SwitchPreference) findPreference(Constants.Pref.SEARCH_KEYBASE)
);
}
@@ -207,12 +206,7 @@ public class SettingsActivity extends PreferenceActivity {
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CODE_KEYSERVER_PREF: {
- if (resultCode == RESULT_CANCELED || data == null) {
- return;
- }
- String servers[] = data
- .getStringArrayExtra(SettingsKeyServerActivity.EXTRA_KEY_SERVERS);
- sPreferences.setKeyServers(servers);
+ // update preference, in case it changed
mKeyServerPreference.setSummary(keyserverSummary(getActivity()));
break;
}
@@ -226,16 +220,16 @@ public class SettingsActivity extends PreferenceActivity {
}
/**
- * This fragment shows the advanced preferences in android 3.0+
+ * This fragment shows the PIN/password preferences
*/
- public static class AdvancedPrefsFragment extends PreferenceFragment {
+ public static class PassphrasePrefsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
- addPreferencesFromResource(R.xml.adv_preferences);
+ addPreferencesFromResource(R.xml.passphrase_preferences);
initializePassphraseCacheSubs(
(CheckBoxPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_SUBS));
@@ -243,25 +237,6 @@ public class SettingsActivity extends PreferenceActivity {
initializePassphraseCacheTtl(
(IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL));
- int[] valueIds = new int[]{
- CompressionAlgorithmTags.UNCOMPRESSED,
- CompressionAlgorithmTags.ZIP,
- CompressionAlgorithmTags.ZLIB,
- CompressionAlgorithmTags.BZIP2,
- };
-
- String[] entries = new String[]{
- getString(R.string.choice_none) + " (" + getString(R.string.compression_fast) + ")",
- "ZIP (" + getString(R.string.compression_fast) + ")",
- "ZLIB (" + getString(R.string.compression_fast) + ")",
- "BZIP2 (" + getString(R.string.compression_very_slow) + ")",
- };
-
- String[] values = new String[valueIds.length];
- for (int i = 0; i < values.length; ++i) {
- values[i] = "" + valueIds[i];
- }
-
initializeUseDefaultYubiKeyPin(
(CheckBoxPreference) findPreference(Constants.Pref.USE_DEFAULT_YUBIKEY_PIN));
@@ -270,10 +245,346 @@ public class SettingsActivity extends PreferenceActivity {
}
}
- @TargetApi(Build.VERSION_CODES.KITKAT)
+ public static class ProxyPrefsFragment extends PreferenceFragment {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ new Initializer(this).initialize();
+
+ }
+
+ public static class Initializer {
+ private SwitchPreference mUseTor;
+ private SwitchPreference mUseNormalProxy;
+ private EditTextPreference mProxyHost;
+ private EditTextPreference mProxyPort;
+ private ListPreference mProxyType;
+ private PreferenceActivity mActivity;
+ private PreferenceFragment mFragment;
+
+ public Initializer(PreferenceFragment fragment) {
+ mFragment = fragment;
+ }
+
+ public Initializer(PreferenceActivity activity) {
+ mActivity = activity;
+ }
+
+ public Preference automaticallyFindPreference(String key) {
+ if (mFragment != null) {
+ return mFragment.findPreference(key);
+ } else {
+ return mActivity.findPreference(key);
+ }
+ }
+
+ public void initialize() {
+ // makes android's preference framework write to our file instead of default
+ // This allows us to use the "persistent" attribute to simplify code
+ if (mFragment != null) {
+ Preferences.setPreferenceManagerFileAndMode(mFragment.getPreferenceManager());
+ // Load the preferences from an XML resource
+ mFragment.addPreferencesFromResource(R.xml.proxy_prefs);
+ } else {
+ Preferences.setPreferenceManagerFileAndMode(mActivity.getPreferenceManager());
+ // Load the preferences from an XML resource
+ mActivity.addPreferencesFromResource(R.xml.proxy_prefs);
+ }
+
+ mUseTor = (SwitchPreference) automaticallyFindPreference(Constants.Pref.USE_TOR_PROXY);
+ mUseNormalProxy = (SwitchPreference) automaticallyFindPreference(Constants.Pref.USE_NORMAL_PROXY);
+ mProxyHost = (EditTextPreference) automaticallyFindPreference(Constants.Pref.PROXY_HOST);
+ mProxyPort = (EditTextPreference) automaticallyFindPreference(Constants.Pref.PROXY_PORT);
+ mProxyType = (ListPreference) automaticallyFindPreference(Constants.Pref.PROXY_TYPE);
+ initializeUseTorPref();
+ initializeUseNormalProxyPref();
+ initializeEditTextPreferences();
+ initializeProxyTypePreference();
+
+ if (mUseTor.isChecked()) {
+ disableNormalProxyPrefs();
+ }
+ else if (mUseNormalProxy.isChecked()) {
+ disableUseTorPrefs();
+ } else {
+ disableNormalProxySettings();
+ }
+ }
+
+ private void initializeUseTorPref() {
+ mUseTor.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ Activity activity = mFragment != null ? mFragment.getActivity() : mActivity;
+ if ((Boolean) newValue) {
+ boolean installed = OrbotHelper.isOrbotInstalled(activity);
+ if (!installed) {
+ Log.d(Constants.TAG, "Prompting to install Tor");
+ OrbotHelper.getPreferenceInstallDialogFragment().show(activity.getFragmentManager(),
+ "installDialog");
+ // don't let the user check the box until he's installed orbot
+ return false;
+ } else {
+ disableNormalProxyPrefs();
+ // let the enable tor box be checked
+ return true;
+ }
+ } else {
+ // we're unchecking Tor, so enable other proxy
+ enableNormalProxyCheckbox();
+ return true;
+ }
+ }
+ });
+ }
+
+ private void initializeUseNormalProxyPref() {
+ mUseNormalProxy.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if ((Boolean) newValue) {
+ disableUseTorPrefs();
+ enableNormalProxySettings();
+ } else {
+ enableUseTorPrefs();
+ disableNormalProxySettings();
+ }
+ return true;
+ }
+ });
+ }
+
+ private void initializeEditTextPreferences() {
+ mProxyHost.setSummary(mProxyHost.getText());
+ mProxyPort.setSummary(mProxyPort.getText());
+
+ mProxyHost.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ Activity activity = mFragment != null ? mFragment.getActivity() : mActivity;
+ if (TextUtils.isEmpty((String) newValue)) {
+ Notify.create(
+ activity,
+ R.string.pref_proxy_host_err_invalid,
+ Notify.Style.ERROR
+ ).show();
+ return false;
+ } else {
+ mProxyHost.setSummary((CharSequence) newValue);
+ return true;
+ }
+ }
+ });
+
+ mProxyPort.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ Activity activity = mFragment != null ? mFragment.getActivity() : mActivity;
+ try {
+ int port = Integer.parseInt((String) newValue);
+ if (port < 0 || port > 65535) {
+ Notify.create(
+ activity,
+ R.string.pref_proxy_port_err_invalid,
+ Notify.Style.ERROR
+ ).show();
+ return false;
+ }
+ // no issues, save port
+ mProxyPort.setSummary("" + port);
+ return true;
+ } catch (NumberFormatException e) {
+ Notify.create(
+ activity,
+ R.string.pref_proxy_port_err_invalid,
+ Notify.Style.ERROR
+ ).show();
+ return false;
+ }
+ }
+ });
+ }
+
+ private void initializeProxyTypePreference() {
+ mProxyType.setSummary(mProxyType.getEntry());
+
+ mProxyType.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ CharSequence entry = mProxyType.getEntries()[mProxyType.findIndexOfValue((String) newValue)];
+ mProxyType.setSummary(entry);
+ return true;
+ }
+ });
+ }
+
+ private void disableNormalProxyPrefs() {
+ mUseNormalProxy.setChecked(false);
+ mUseNormalProxy.setEnabled(false);
+ disableNormalProxySettings();
+ }
+
+ private void enableNormalProxyCheckbox() {
+ mUseNormalProxy.setEnabled(true);
+ }
+
+ private void enableNormalProxySettings() {
+ mProxyHost.setEnabled(true);
+ mProxyPort.setEnabled(true);
+ mProxyType.setEnabled(true);
+ }
+
+ private void disableNormalProxySettings() {
+ mProxyHost.setEnabled(false);
+ mProxyPort.setEnabled(false);
+ mProxyType.setEnabled(false);
+ }
+
+ private void disableUseTorPrefs() {
+ mUseTor.setChecked(false);
+ mUseTor.setEnabled(false);
+ }
+
+ private void enableUseTorPrefs() {
+ mUseTor.setEnabled(true);
+ }
+ }
+ }
+
+ /**
+ * This fragment shows gui preferences.
+ */
+ public static class GuiPrefsFragment extends PreferenceFragment {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+
+ // Load the preferences from an XML resource
+ addPreferencesFromResource(R.xml.gui_preferences);
+
+ initializeTheme((ListPreference) findPreference(Constants.Pref.THEME));
+ }
+ }
+
+ /**
+ * This fragment shows the keyserver/contacts sync preferences
+ */
+ public static class SyncPrefsFragment extends PreferenceFragment {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Load the preferences from an XML resource
+ addPreferencesFromResource(R.xml.sync_preferences);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ // this needs to be done in onResume since the user can change sync values from Android
+ // settings and we need to reflect that change when the user navigates back
+ AccountManager manager = AccountManager.get(getActivity());
+ final Account account = manager.getAccountsByType(Constants.ACCOUNT_TYPE)[0];
+ // for keyserver sync
+ initializeSyncCheckBox(
+ (SwitchPreference) findPreference(Constants.Pref.SYNC_KEYSERVER),
+ account,
+ Constants.PROVIDER_AUTHORITY
+ );
+ // for contacts sync
+ initializeSyncCheckBox(
+ (SwitchPreference) findPreference(Constants.Pref.SYNC_CONTACTS),
+ account,
+ ContactsContract.AUTHORITY
+ );
+ }
+
+ private void initializeSyncCheckBox(final SwitchPreference syncCheckBox,
+ final Account account,
+ final String authority) {
+ boolean syncEnabled = ContentResolver.getSyncAutomatically(account, authority);
+ syncCheckBox.setChecked(syncEnabled);
+ setSummary(syncCheckBox, authority, syncEnabled);
+
+ syncCheckBox.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ boolean syncEnabled = (Boolean) newValue;
+ if (syncEnabled) {
+ ContentResolver.setSyncAutomatically(account, authority, true);
+ } else {
+ // disable syncs
+ ContentResolver.setSyncAutomatically(account, authority, false);
+ // cancel any ongoing/pending syncs
+ ContentResolver.cancelSync(account, authority);
+ }
+ setSummary(syncCheckBox, authority, syncEnabled);
+ return true;
+ }
+ });
+ }
+
+ private void setSummary(SwitchPreference syncCheckBox, String authority,
+ boolean checked) {
+ switch (authority) {
+ case Constants.PROVIDER_AUTHORITY: {
+ if (checked) {
+ syncCheckBox.setSummary(R.string.label_sync_settings_keyserver_summary_on);
+ } else {
+ syncCheckBox.setSummary(R.string.label_sync_settings_keyserver_summary_off);
+ }
+ break;
+ }
+ case ContactsContract.AUTHORITY: {
+ if (checked) {
+ syncCheckBox.setSummary(R.string.label_sync_settings_contacts_summary_on);
+ } else {
+ syncCheckBox.setSummary(R.string.label_sync_settings_contacts_summary_off);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * This fragment shows experimental features
+ */
+ public static class ExperimentalPrefsFragment extends PreferenceFragment {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Load the preferences from an XML resource
+ addPreferencesFromResource(R.xml.experimental_preferences);
+
+ initializeExperimentalEnableWordConfirm(
+ (SwitchPreference) findPreference(Constants.Pref.EXPERIMENTAL_ENABLE_WORD_CONFIRM));
+
+ initializeExperimentalEnableLinkedIdentities(
+ (SwitchPreference) findPreference(Constants.Pref.EXPERIMENTAL_ENABLE_LINKED_IDENTITIES));
+
+ initializeExperimentalEnableKeybase(
+ (SwitchPreference) findPreference(Constants.Pref.EXPERIMENTAL_ENABLE_KEYBASE));
+
+ initializeTheme((ListPreference) findPreference(Constants.Pref.THEME));
+
+ }
+ }
+
protected boolean isValidFragment(String fragmentName) {
- return AdvancedPrefsFragment.class.getName().equals(fragmentName)
+ return PassphrasePrefsFragment.class.getName().equals(fragmentName)
|| CloudSearchPrefsFragment.class.getName().equals(fragmentName)
+ || ProxyPrefsFragment.class.getName().equals(fragmentName)
+ || GuiPrefsFragment.class.getName().equals(fragmentName)
+ || SyncPrefsFragment.class.getName().equals(fragmentName)
+ || ExperimentalPrefsFragment.class.getName().equals(fragmentName)
|| super.isValidFragment(fragmentName);
}
@@ -302,7 +613,23 @@ public class SettingsActivity extends PreferenceActivity {
});
}
- private static void initializeSearchKeyserver(final CheckBoxPreference mSearchKeyserver) {
+ private static void initializeTheme(final ListPreference mTheme) {
+ mTheme.setValue(sPreferences.getTheme());
+ mTheme.setSummary(mTheme.getEntry());
+ mTheme.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ mTheme.setValue((String) newValue);
+ mTheme.setSummary(mTheme.getEntry());
+ sPreferences.setTheme((String) newValue);
+
+ ((SettingsActivity) mTheme.getContext()).recreate();
+
+ return false;
+ }
+ });
+ }
+
+ private static void initializeSearchKeyserver(final SwitchPreference mSearchKeyserver) {
Preferences.CloudSearchPrefs prefs = sPreferences.getCloudSearchPrefs();
mSearchKeyserver.setChecked(prefs.searchKeyserver);
mSearchKeyserver.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@@ -315,7 +642,7 @@ public class SettingsActivity extends PreferenceActivity {
});
}
- private static void initializeSearchKeybase(final CheckBoxPreference mSearchKeybase) {
+ private static void initializeSearchKeybase(final SwitchPreference mSearchKeybase) {
Preferences.CloudSearchPrefs prefs = sPreferences.getCloudSearchPrefs();
mSearchKeybase.setChecked(prefs.searchKeybase);
mSearchKeybase.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@@ -332,7 +659,8 @@ public class SettingsActivity extends PreferenceActivity {
String[] servers = sPreferences.getKeyServers();
String serverSummary = context.getResources().getQuantityString(
R.plurals.n_keyservers, servers.length, servers.length);
- return serverSummary + "; " + context.getString(R.string.label_preferred) + ": " + sPreferences.getPreferredKeyserver();
+ return serverSummary + "; " + context.getString(R.string.label_preferred) + ": " + sPreferences
+ .getPreferredKeyserver();
}
private static void initializeUseDefaultYubiKeyPin(final CheckBoxPreference mUseDefaultYubiKeyPin) {
@@ -356,4 +684,37 @@ public class SettingsActivity extends PreferenceActivity {
}
});
}
+
+ private static void initializeExperimentalEnableWordConfirm(final SwitchPreference mExperimentalEnableWordConfirm) {
+ mExperimentalEnableWordConfirm.setChecked(sPreferences.getExperimentalEnableWordConfirm());
+ mExperimentalEnableWordConfirm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ mExperimentalEnableWordConfirm.setChecked((Boolean) newValue);
+ sPreferences.setExperimentalEnableWordConfirm((Boolean) newValue);
+ return false;
+ }
+ });
+ }
+
+ private static void initializeExperimentalEnableLinkedIdentities(final SwitchPreference mExperimentalEnableLinkedIdentities) {
+ mExperimentalEnableLinkedIdentities.setChecked(sPreferences.getExperimentalEnableLinkedIdentities());
+ mExperimentalEnableLinkedIdentities.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ mExperimentalEnableLinkedIdentities.setChecked((Boolean) newValue);
+ sPreferences.setExperimentalEnableLinkedIdentities((Boolean) newValue);
+ return false;
+ }
+ });
+ }
+
+ private static void initializeExperimentalEnableKeybase(final SwitchPreference mExperimentalKeybase) {
+ mExperimentalKeybase.setChecked(sPreferences.getExperimentalEnableKeybase());
+ mExperimentalKeybase.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ mExperimentalKeybase.setChecked((Boolean) newValue);
+ sPreferences.setExperimentalEnableKeybase((Boolean) newValue);
+ return false;
+ }
+ });
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java
index 8f025c769..f61ada84f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java
@@ -17,89 +17,23 @@
package org.sufficientlysecure.keychain.ui;
-import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
-import org.sufficientlysecure.keychain.ui.dialog.AddKeyserverDialogFragment;
-import org.sufficientlysecure.keychain.ui.util.Notify;
-import org.sufficientlysecure.keychain.ui.widget.Editor;
-import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
-import org.sufficientlysecure.keychain.ui.widget.KeyServerEditor;
-import java.util.Vector;
-
-public class SettingsKeyServerActivity extends BaseActivity implements OnClickListener,
- EditorListener {
+public class SettingsKeyServerActivity extends BaseActivity {
public static final String EXTRA_KEY_SERVERS = "key_servers";
- private LayoutInflater mInflater;
- private ViewGroup mEditors;
- private View mAdd;
- private View mRotate;
- private TextView mTitle;
- private TextView mSummary;
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- // Inflate a "Done"/"Cancel" custom action bar view
- setFullScreenDialogDoneClose(R.string.btn_save,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- okClicked();
- }
- },
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- cancelClicked();
- }
- });
-
- mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- mTitle = (TextView) findViewById(R.id.title);
- mSummary = (TextView) findViewById(R.id.summary);
- mSummary.setText(getText(R.string.label_first_keyserver_is_used));
-
- mTitle.setText(R.string.label_keyservers);
-
- mEditors = (ViewGroup) findViewById(R.id.editors);
- mAdd = findViewById(R.id.add);
- mAdd.setOnClickListener(this);
-
- mRotate = findViewById(R.id.rotate);
- mRotate.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- Vector<String> servers = serverList();
- String first = servers.get(0);
- if (first != null) {
- servers.remove(0);
- servers.add(first);
- String[] dummy = {};
- makeServerList(servers.toArray(dummy));
- }
- }
- });
-
Intent intent = getIntent();
String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS);
- makeServerList(servers);
+ loadFragment(savedInstanceState, servers);
}
@Override
@@ -107,118 +41,22 @@ public class SettingsKeyServerActivity extends BaseActivity implements OnClickLi
setContentView(R.layout.key_server_preference);
}
- private void makeServerList(String[] servers) {
- if (servers != null) {
- mEditors.removeAllViews();
- for (String serv : servers) {
- KeyServerEditor view = (KeyServerEditor) mInflater.inflate(
- R.layout.key_server_editor, mEditors, false);
- view.setEditorListener(this);
- view.setValue(serv);
- mEditors.addView(view);
- }
+ private void loadFragment(Bundle savedInstanceState, String[] keyservers) {
+ // 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;
}
- }
-
- public void onDeleted(Editor editor, boolean wasNewItem) {
- // nothing to do
- }
- @Override
- public void onEdited() {
+ SettingsKeyserverFragment fragment = SettingsKeyserverFragment.newInstance(keyservers);
- }
-
- // button to add keyserver clicked
- public void onClick(View v) {
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- Bundle data = message.getData();
- switch (message.what) {
- case AddKeyserverDialogFragment.MESSAGE_OKAY: {
- boolean verified = data.getBoolean(AddKeyserverDialogFragment.MESSAGE_VERIFIED);
- if (verified) {
- Notify.create(SettingsKeyServerActivity.this,
- R.string.add_keyserver_verified, Notify.Style.OK).show();
- } else {
- Notify.create(SettingsKeyServerActivity.this,
- R.string.add_keyserver_without_verification,
- Notify.Style.WARN).show();
- }
- String keyserver = data.getString(AddKeyserverDialogFragment.MESSAGE_KEYSERVER);
- addKeyserver(keyserver);
- break;
- }
- case AddKeyserverDialogFragment.MESSAGE_VERIFICATION_FAILED: {
- AddKeyserverDialogFragment.FailureReason failureReason =
- (AddKeyserverDialogFragment.FailureReason) data.getSerializable(
- AddKeyserverDialogFragment.MESSAGE_FAILURE_REASON);
- switch (failureReason) {
- case CONNECTION_FAILED: {
- Notify.create(SettingsKeyServerActivity.this,
- R.string.add_keyserver_connection_failed,
- Notify.Style.ERROR).show();
- break;
- }
- case INVALID_URL: {
- Notify.create(SettingsKeyServerActivity.this,
- R.string.add_keyserver_invalid_url,
- Notify.Style.ERROR).show();
- break;
- }
- }
- break;
- }
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
- AddKeyserverDialogFragment dialogFragment = AddKeyserverDialogFragment
- .newInstance(messenger, R.string.add_keyserver_dialog_title);
- dialogFragment.show(getSupportFragmentManager(), "addKeyserverDialog");
- }
-
- public void addKeyserver(String keyserverUrl) {
- KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor,
- mEditors, false);
- view.setEditorListener(this);
- view.setValue(keyserverUrl);
- mEditors.addView(view);
- }
-
- private void cancelClicked() {
- setResult(RESULT_CANCELED, null);
- finish();
- }
-
- private Vector<String> serverList() {
- Vector<String> servers = new Vector<>();
- for (int i = 0; i < mEditors.getChildCount(); ++i) {
- KeyServerEditor editor = (KeyServerEditor) mEditors.getChildAt(i);
- String tmp = editor.getValue();
- if (tmp.length() > 0) {
- servers.add(tmp);
- }
- }
- return servers;
- }
-
- private void okClicked() {
- Intent data = new Intent();
- Vector<String> servers = new Vector<>();
- for (int i = 0; i < mEditors.getChildCount(); ++i) {
- KeyServerEditor editor = (KeyServerEditor) mEditors.getChildAt(i);
- String tmp = editor.getValue();
- if (tmp.length() > 0) {
- servers.add(tmp);
- }
- }
- String[] dummy = new String[0];
- data.putExtra(EXTRA_KEY_SERVERS, servers.toArray(dummy));
- setResult(RESULT_OK, data);
- finish();
+ // Add the fragment to the 'fragment_container' FrameLayout
+ // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.keyserver_settings_fragment_container, fragment)
+ .commitAllowingStateLoss();
+ // do it immediately!
+ getSupportFragmentManager().executePendingTransactions();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java
new file mode 100644
index 000000000..d8edbe4f8
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2012-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.graphics.Color;
+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.view.MotionEventCompat;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.dialog.AddEditKeyserverDialogFragment;
+import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperAdapter;
+import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperViewHolder;
+import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperDragCallback;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.recyclerview.RecyclerItemClickListener;
+import org.sufficientlysecure.keychain.util.Preferences;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class SettingsKeyserverFragment extends Fragment implements RecyclerItemClickListener.OnItemClickListener {
+
+ private static final String ARG_KEYSERVER_ARRAY = "arg_keyserver_array";
+ private ItemTouchHelper mItemTouchHelper;
+
+ private ArrayList<String> mKeyservers;
+ private KeyserverListAdapter mAdapter;
+
+ public static SettingsKeyserverFragment newInstance(String[] keyservers) {
+ Bundle args = new Bundle();
+ args.putStringArray(ARG_KEYSERVER_ARRAY, keyservers);
+
+ SettingsKeyserverFragment fragment = new SettingsKeyserverFragment();
+ fragment.setArguments(args);
+
+ return fragment;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
+ savedInstanceState) {
+
+ return inflater.inflate(R.layout.settings_keyserver_fragment, null);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ String keyservers[] = getArguments().getStringArray(ARG_KEYSERVER_ARRAY);
+ mKeyservers = new ArrayList<>(Arrays.asList(keyservers));
+
+ mAdapter = new KeyserverListAdapter(mKeyservers);
+
+ RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.keyserver_recycler_view);
+ // recyclerView.setHasFixedSize(true); // the size of the first item changes
+ recyclerView.setAdapter(mAdapter);
+ recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+
+ ItemTouchHelper.Callback callback = new ItemTouchHelperDragCallback(mAdapter);
+ mItemTouchHelper = new ItemTouchHelper(callback);
+ mItemTouchHelper.attachToRecyclerView(recyclerView);
+
+ // for clicks
+ recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(getActivity(), this));
+
+ // can't use item decoration because it doesn't move with drag and drop
+ // recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null));
+
+ // We have a menu item to show in action bar.
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
+ inflater.inflate(R.menu.keyserver_pref_menu, menu);
+
+ super.onCreateOptionsMenu(menu, inflater);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+
+ case R.id.menu_add_keyserver:
+ startAddKeyserverDialog();
+ return true;
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void startAddKeyserverDialog() {
+ // keyserver and position have no meaning
+ startEditKeyserverDialog(AddEditKeyserverDialogFragment.DialogAction.ADD, null, -1);
+ }
+
+ private void startEditKeyserverDialog(AddEditKeyserverDialogFragment.DialogAction action,
+ String keyserver, final int position) {
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ Bundle data = message.getData();
+ switch (message.what) {
+ case AddEditKeyserverDialogFragment.MESSAGE_OKAY: {
+ boolean deleted =
+ data.getBoolean(AddEditKeyserverDialogFragment.MESSAGE_KEYSERVER_DELETED
+ , false);
+ if (deleted) {
+ Notify.create(getActivity(),
+ getActivity().getString(
+ R.string.keyserver_preference_deleted, mKeyservers.get(position)),
+ Notify.Style.OK)
+ .show();
+ deleteKeyserver(position);
+ return;
+ }
+ boolean verified =
+ data.getBoolean(AddEditKeyserverDialogFragment.MESSAGE_VERIFIED);
+ if (verified) {
+ Notify.create(getActivity(),
+ R.string.add_keyserver_verified, Notify.Style.OK).show();
+ } else {
+ Notify.create(getActivity(),
+ R.string.add_keyserver_without_verification,
+ Notify.Style.WARN).show();
+ }
+ String keyserver = data.getString(
+ AddEditKeyserverDialogFragment.MESSAGE_KEYSERVER);
+
+ AddEditKeyserverDialogFragment.DialogAction dialogAction
+ = (AddEditKeyserverDialogFragment.DialogAction) data.getSerializable(
+ AddEditKeyserverDialogFragment.MESSAGE_DIALOG_ACTION);
+ switch (dialogAction) {
+ case ADD:
+ addKeyserver(keyserver);
+ break;
+ case EDIT:
+ editKeyserver(keyserver, position);
+ break;
+ }
+ break;
+ }
+ case AddEditKeyserverDialogFragment.MESSAGE_VERIFICATION_FAILED: {
+ AddEditKeyserverDialogFragment.FailureReason failureReason =
+ (AddEditKeyserverDialogFragment.FailureReason) data.getSerializable(
+ AddEditKeyserverDialogFragment.MESSAGE_FAILURE_REASON);
+ switch (failureReason) {
+ case CONNECTION_FAILED: {
+ Notify.create(getActivity(),
+ R.string.add_keyserver_connection_failed,
+ Notify.Style.ERROR).show();
+ break;
+ }
+ case INVALID_URL: {
+ Notify.create(getActivity(),
+ R.string.add_keyserver_invalid_url,
+ Notify.Style.ERROR).show();
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(returnHandler);
+ AddEditKeyserverDialogFragment dialogFragment = AddEditKeyserverDialogFragment
+ .newInstance(messenger, action, keyserver, position);
+ dialogFragment.show(getFragmentManager(), "addKeyserverDialog");
+ }
+
+ private void addKeyserver(String keyserver) {
+ mKeyservers.add(keyserver);
+ mAdapter.notifyItemInserted(mKeyservers.size() - 1);
+ saveKeyserverList();
+ }
+
+ private void editKeyserver(String newKeyserver, int position) {
+ mKeyservers.set(position, newKeyserver);
+ mAdapter.notifyItemChanged(position);
+ saveKeyserverList();
+ }
+
+ private void deleteKeyserver(int position) {
+ if (mKeyservers.size() == 1) {
+ Notify.create(getActivity(), R.string.keyserver_preference_cannot_delete_last,
+ Notify.Style.ERROR).show();
+ return;
+ }
+ mKeyservers.remove(position);
+ // we use this
+ mAdapter.notifyItemRemoved(position);
+ if (position == 0 && mKeyservers.size() > 0) {
+ // if we deleted the first item, we need the adapter to redraw the new first item
+ mAdapter.notifyItemChanged(0);
+ }
+ saveKeyserverList();
+ }
+
+ private void saveKeyserverList() {
+ String servers[] = mKeyservers.toArray(new String[mKeyservers.size()]);
+ Preferences.getPreferences(getActivity()).setKeyServers(servers);
+ }
+
+ @Override
+ public void onItemClick(View view, int position) {
+ startEditKeyserverDialog(AddEditKeyserverDialogFragment.DialogAction.EDIT,
+ mKeyservers.get(position), position);
+ }
+
+ public class KeyserverListAdapter extends RecyclerView.Adapter<KeyserverListAdapter.ViewHolder>
+ implements ItemTouchHelperAdapter {
+
+ private final List<String> mKeyservers;
+
+ public KeyserverListAdapter(List<String> keyservers) {
+ mKeyservers = keyservers;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.settings_keyserver_item, parent, false);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(final ViewHolder holder, int position) {
+ holder.keyserverUrl.setText(mKeyservers.get(position));
+
+ // Start a drag whenever the handle view it touched
+ holder.dragHandleView.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
+ mItemTouchHelper.startDrag(holder);
+ }
+ return false;
+ }
+ });
+
+ selectUnselectKeyserver(holder, position);
+ }
+
+ private void selectUnselectKeyserver(ViewHolder holder, int position) {
+
+ if (position == 0) {
+ holder.showAsSelectedKeyserver();
+ } else {
+ holder.showAsUnselectedKeyserver();
+ }
+ }
+
+ @Override
+ public void onItemMove(RecyclerView.ViewHolder source, RecyclerView.ViewHolder target,
+ int fromPosition, int toPosition) {
+ Collections.swap(mKeyservers, fromPosition, toPosition);
+ saveKeyserverList();
+ selectUnselectKeyserver((ViewHolder) target, fromPosition);
+ // we don't want source to change color while dragging, therefore we just set
+ // isSelectedKeyserver instead of selectUnselectKeyserver
+ ((ViewHolder) source).isSelectedKeyserver = toPosition == 0;
+
+ notifyItemMoved(fromPosition, toPosition);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mKeyservers.size();
+ }
+
+ public class ViewHolder extends RecyclerView.ViewHolder implements
+ ItemTouchHelperViewHolder {
+
+ public final ViewGroup outerLayout;
+ public final TextView selectedServerLabel;
+ public final TextView keyserverUrl;
+ public final ImageView dragHandleView;
+
+ private boolean isSelectedKeyserver = false;
+
+ public ViewHolder(View itemView) {
+ super(itemView);
+ outerLayout = (ViewGroup) itemView.findViewById(R.id.outer_layout);
+ selectedServerLabel = (TextView) itemView.findViewById(
+ R.id.selected_keyserver_title);
+ keyserverUrl = (TextView) itemView.findViewById(R.id.keyserver_tv);
+ dragHandleView = (ImageView) itemView.findViewById(R.id.drag_handle);
+
+ itemView.setClickable(true);
+ }
+
+ public void showAsSelectedKeyserver() {
+ isSelectedKeyserver = true;
+ selectedServerLabel.setVisibility(View.VISIBLE);
+ outerLayout.setBackgroundColor(getResources().getColor(R.color.android_green_dark));
+ }
+
+ public void showAsUnselectedKeyserver() {
+ isSelectedKeyserver = false;
+ selectedServerLabel.setVisibility(View.GONE);
+ outerLayout.setBackgroundColor(Color.WHITE);
+ }
+
+ @Override
+ public void onItemSelected() {
+ selectedServerLabel.setVisibility(View.GONE);
+ itemView.setBackgroundColor(Color.LTGRAY);
+ }
+
+ @Override
+ public void onItemClear() {
+ if (isSelectedKeyserver) {
+ showAsSelectedKeyserver();
+ } else {
+ showAsUnselectedKeyserver();
+ }
+ }
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
index 5c8e6bb5d..0415128a2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
@@ -17,40 +17,42 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.support.v4.app.NavUtils;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
-import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
/**
* Sends the selected public key to a keyserver
*/
-public class UploadKeyActivity extends BaseActivity {
+public class UploadKeyActivity extends BaseActivity
+ implements CryptoOperationHelper.Callback<ExportKeyringParcel, ExportResult> {
private View mUploadButton;
private Spinner mKeyServerSpinner;
private Uri mDataUri;
+ // CryptoOperationHelper.Callback vars
+ private String mKeyserver;
+ private Uri mUnifiedKeyringUri;
+ private CryptoOperationHelper<ExportKeyringParcel, ExportResult> mUploadOpHelper;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -90,52 +92,23 @@ public class UploadKeyActivity extends BaseActivity {
setContentView(R.layout.upload_key_activity);
}
- private void uploadKey() {
- // Send all information needed to service to upload key in other thread
- Intent intent = new Intent(this, KeychainIntentService.class);
-
- intent.setAction(KeychainIntentService.ACTION_UPLOAD_KEYRING);
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mUploadOpHelper != null) {
+ mUploadOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
- // set data uri as path to keyring
+ private void uploadKey() {
Uri blobUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
- intent.setData(blobUri);
-
- // fill values for this action
- Bundle data = new Bundle();
+ mUnifiedKeyringUri = blobUri;
String server = (String) mKeyServerSpinner.getSelectedItem();
- data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, server);
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after uploading is done in KeychainIntentService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- this,
- getString(R.string.progress_uploading),
- ProgressDialog.STYLE_HORIZONTAL,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
-
- Toast.makeText(UploadKeyActivity.this, R.string.msg_crt_upload_success,
- Toast.LENGTH_SHORT).show();
- finish();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(this);
+ mKeyserver = server;
- // start service with intent
- startService(intent);
+ mUploadOpHelper = new CryptoOperationHelper(1, this, this, R.string.progress_uploading);
+ mUploadOpHelper.cryptoOperation();
}
@Override
@@ -150,4 +123,29 @@ public class UploadKeyActivity extends BaseActivity {
}
return super.onOptionsItemSelected(item);
}
+
+ @Override
+ public ExportKeyringParcel createOperationInput() {
+ return new ExportKeyringParcel(mKeyserver, mUnifiedKeyringUri);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ExportResult result) {
+ result.createNotify(this).show();
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(ExportResult result) {
+ result.createNotify(this).show();
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index 7ae4f1be3..fd50ed5ef 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -31,9 +31,10 @@ import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
import android.provider.ContactsContract;
+import android.support.design.widget.AppBarLayout;
+import android.support.design.widget.CollapsingToolbarLayout;
+import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.LoaderManager;
@@ -47,16 +48,17 @@ import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
+import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
-import com.getbase.floatingactionbutton.FloatingActionButton;
+
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.operations.results.CertifyResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
@@ -65,13 +67,10 @@ import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
-import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
@@ -85,37 +84,45 @@ import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.NfcHelper;
import org.sufficientlysecure.keychain.util.Preferences;
+import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.HashMap;
public class ViewKeyActivity extends BaseNfcActivity implements
- LoaderManager.LoaderCallbacks<Cursor> {
+ LoaderManager.LoaderCallbacks<Cursor>,
+ CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
public static final String EXTRA_NFC_USER_ID = "nfc_user_id";
public static final String EXTRA_NFC_AID = "nfc_aid";
public static final String EXTRA_NFC_FINGERPRINTS = "nfc_fingerprints";
static final int REQUEST_QR_FINGERPRINT = 1;
- static final int REQUEST_DELETE = 2;
- static final int REQUEST_EXPORT = 3;
+ static final int REQUEST_BACKUP = 2;
+ static final int REQUEST_CERTIFY = 3;
+ static final int REQUEST_DELETE = 4;
+
public static final String EXTRA_DISPLAY_RESULT = "display_result";
- ExportHelper mExportHelper;
ProviderHelper mProviderHelper;
protected Uri mDataUri;
- private TextView mName;
+ // For CryptoOperationHelper.Callback
+ private String mKeyserver;
+ private ArrayList<ParcelableKeyRing> mKeyList;
+ private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
+
private TextView mStatusText;
private ImageView mStatusImage;
- private RelativeLayout mBigToolbar;
+ private AppBarLayout mAppBarLayout;
+ private CollapsingToolbarLayout mCollapsingToolbarLayout;
private ImageButton mActionEncryptFile;
private ImageButton mActionEncryptText;
private ImageButton mActionNfc;
private FloatingActionButton mFab;
private ImageView mPhoto;
+ private FrameLayout mPhotoLayout;
private ImageView mQrCode;
private CardView mQrCodeLayout;
@@ -132,6 +139,8 @@ public class ViewKeyActivity extends BaseNfcActivity implements
private boolean mIsRevoked = false;
private boolean mIsExpired = false;
+ private boolean mShowYubikeyAfterCreation = false;
+
private MenuItem mRefreshItem;
private boolean mIsRefreshing;
private Animation mRotate, mRotateSpin;
@@ -139,26 +148,31 @@ public class ViewKeyActivity extends BaseNfcActivity implements
private String mFingerprint;
private long mMasterKeyId;
+ private byte[] mNfcFingerprints;
+ private String mNfcUserId;
+ private byte[] mNfcAid;
+
@SuppressLint("InflateParams")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mExportHelper = new ExportHelper(this);
mProviderHelper = new ProviderHelper(this);
+ mOperationHelper = new CryptoOperationHelper<>(1, this, this, null);
setTitle(null);
- mName = (TextView) findViewById(R.id.view_key_name);
mStatusText = (TextView) findViewById(R.id.view_key_status);
mStatusImage = (ImageView) findViewById(R.id.view_key_status_image);
- mBigToolbar = (RelativeLayout) findViewById(R.id.toolbar_big);
+ mAppBarLayout = (AppBarLayout) findViewById(R.id.app_bar_layout);
+ mCollapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
mActionEncryptFile = (ImageButton) findViewById(R.id.view_key_action_encrypt_files);
mActionEncryptText = (ImageButton) findViewById(R.id.view_key_action_encrypt_text);
mActionNfc = (ImageButton) findViewById(R.id.view_key_action_nfc);
mFab = (FloatingActionButton) findViewById(R.id.fab);
mPhoto = (ImageView) findViewById(R.id.view_key_photo);
+ mPhotoLayout = (FrameLayout) findViewById(R.id.view_key_photo_layout);
mQrCode = (ImageView) findViewById(R.id.view_key_qr_code);
mQrCodeLayout = (CardView) findViewById(R.id.view_key_qr_code_layout);
@@ -287,14 +301,17 @@ public class ViewKeyActivity extends BaseNfcActivity implements
.replace(R.id.view_key_fragment, frag)
.commit();
- if (getIntent().hasExtra(EXTRA_NFC_AID)) {
- Intent intent = getIntent();
- byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_NFC_FINGERPRINTS);
- String nfcUserId = intent.getStringExtra(EXTRA_NFC_USER_ID);
- byte[] nfcAid = intent.getByteArrayExtra(EXTRA_NFC_AID);
- showYubiKeyFragment(nfcFingerprints, nfcUserId, nfcAid);
+ if (Preferences.getPreferences(this).getExperimentalEnableKeybase()) {
+ final ViewKeyKeybaseFragment keybaseFrag = ViewKeyKeybaseFragment.newInstance(mDataUri);
+ manager.beginTransaction()
+ .replace(R.id.view_key_keybase_fragment, keybaseFrag)
+ .commit();
}
+ // need to postpone loading of the yubikey fragment until after mMasterKeyId
+ // is available, but we mark here that this should be done
+ mShowYubikeyAfterCreation = true;
+
}
@Override
@@ -320,31 +337,11 @@ public class ViewKeyActivity extends BaseNfcActivity implements
return true;
}
case R.id.menu_key_view_export_file: {
- try {
- if (PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId, mMasterKeyId) != null) {
- exportToFile(mDataUri, mExportHelper, mProviderHelper);
- return true;
- }
-
- startPassphraseActivity(REQUEST_EXPORT);
- } catch (PassphraseCacheService.KeyNotFoundException e) {
- // This happens when the master key is stripped
- exportToFile(mDataUri, mExportHelper, mProviderHelper);
- }
+ startPassphraseActivity(REQUEST_BACKUP);
return true;
}
case R.id.menu_key_view_delete: {
- try {
- if (PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId, mMasterKeyId) != null) {
- deleteKey();
- return true;
- }
-
- startPassphraseActivity(REQUEST_DELETE);
- } catch (PassphraseCacheService.KeyNotFoundException e) {
- // This happens when the master key is stripped
- deleteKey();
- }
+ deleteKey();
return true;
}
case R.id.menu_key_view_advanced: {
@@ -372,7 +369,11 @@ public class ViewKeyActivity extends BaseNfcActivity implements
return true;
}
case R.id.menu_key_view_certify_fingerprint: {
- certifyFingeprint(mDataUri);
+ certifyFingeprint(mDataUri, false);
+ return true;
+ }
+ case R.id.menu_key_view_certify_fingerprint_word: {
+ certifyFingeprint(mDataUri, true);
return true;
}
}
@@ -384,11 +385,17 @@ public class ViewKeyActivity extends BaseNfcActivity implements
MenuItem editKey = menu.findItem(R.id.menu_key_view_edit);
editKey.setVisible(mIsSecret);
+ MenuItem exportKey = menu.findItem(R.id.menu_key_view_export_file);
+ exportKey.setVisible(mIsSecret);
+
MenuItem addLinked = menu.findItem(R.id.menu_key_view_add_linked_identity);
addLinked.setVisible(mIsSecret);
MenuItem certifyFingerprint = menu.findItem(R.id.menu_key_view_certify_fingerprint);
certifyFingerprint.setVisible(!mIsSecret && !mIsVerified && !mIsExpired && !mIsRevoked);
+ MenuItem certifyFingerprintWord = menu.findItem(R.id.menu_key_view_certify_fingerprint_word);
+ certifyFingerprintWord.setVisible(!mIsSecret && !mIsVerified && !mIsExpired && !mIsRevoked
+ && Preferences.getPreferences(this).getExperimentalEnableWordConfirm());
return true;
}
@@ -400,40 +407,19 @@ public class ViewKeyActivity extends BaseNfcActivity implements
startActivityForResult(scanQrCode, REQUEST_QR_FINGERPRINT);
}
- private void certifyFingeprint(Uri dataUri) {
+ private void certifyFingeprint(Uri dataUri, boolean enableWordConfirm) {
Intent intent = new Intent(this, CertifyFingerprintActivity.class);
intent.setData(dataUri);
+ intent.putExtra(CertifyFingerprintActivity.EXTRA_ENABLE_WORD_CONFIRM, enableWordConfirm);
- startCertifyIntent(intent);
+ startActivityForResult(intent, REQUEST_CERTIFY);
}
private void certifyImmediate() {
Intent intent = new Intent(this, CertifyKeyActivity.class);
- intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[] {mMasterKeyId});
+ intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{mMasterKeyId});
- startCertifyIntent(intent);
- }
-
- private void startCertifyIntent(Intent intent) {
- // Message is received after signing is done in KeychainIntentService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(this) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- Bundle data = message.getData();
- CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);
-
- result.createNotify(ViewKeyActivity.this).show();
- }
- }
- };
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- startActivityForResult(intent, 0);
+ startActivityForResult(intent, REQUEST_CERTIFY);
}
private void showQrCodeDialog() {
@@ -458,95 +444,108 @@ public class ViewKeyActivity extends BaseNfcActivity implements
startActivityForResult(intent, requestCode);
}
- private void exportToFile(Uri dataUri, ExportHelper exportHelper, ProviderHelper providerHelper) {
- try {
- Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri);
-
- HashMap<String, Object> data = providerHelper.getGenericData(
- baseUri,
- new String[] {KeychainContract.Keys.MASTER_KEY_ID, KeychainContract.KeyRings.HAS_SECRET},
- new int[] {ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_INTEGER});
-
- exportHelper.showExportKeysDialog(
- new long[] {(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)},
- Constants.Path.APP_DIR_FILE, ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) != 0)
- );
- } catch (ProviderHelper.NotFoundException e) {
- Notify.create(this, R.string.error_key_not_found, Notify.Style.ERROR).show();
- Log.e(Constants.TAG, "Key not found", e);
- }
+ private void backupToFile() {
+ new ExportHelper(this).showExportKeysDialog(
+ mMasterKeyId, new File(Constants.Path.APP_DIR,
+ KeyFormattingUtils.convertKeyIdToHex(mMasterKeyId) + ".sec.asc"), true);
}
private void deleteKey() {
- // Message is received after key is deleted
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- setResult(RESULT_CANCELED);
- finish();
- }
- }
- };
+ Intent deleteIntent = new Intent(this, DeleteKeyDialogActivity.class);
+
+ deleteIntent.putExtra(DeleteKeyDialogActivity.EXTRA_DELETE_MASTER_KEY_IDS,
+ new long[]{mMasterKeyId});
+ deleteIntent.putExtra(DeleteKeyDialogActivity.EXTRA_HAS_SECRET, mIsSecret);
+ if (mIsSecret) {
+ // for upload in case key is secret
+ deleteIntent.putExtra(DeleteKeyDialogActivity.EXTRA_KEYSERVER,
+ Preferences.getPreferences(this).getPreferredKeyserver());
+ }
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
- DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
- new long[] {mMasterKeyId});
- deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog");
+ startActivityForResult(deleteIntent, REQUEST_DELETE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == REQUEST_QR_FINGERPRINT && resultCode == Activity.RESULT_OK) {
+ if (mOperationHelper.handleActivityResult(requestCode, resultCode, data)) {
+ return;
+ }
- // If there is an EXTRA_RESULT, that's an error. Just show it.
- if (data.hasExtra(OperationResult.EXTRA_RESULT)) {
- OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
- result.createNotify(this).show();
+ switch (requestCode) {
+ case REQUEST_QR_FINGERPRINT: {
+
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
+
+ // If there is an EXTRA_RESULT, that's an error. Just show it.
+ if (data.hasExtra(OperationResult.EXTRA_RESULT)) {
+ OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
+ result.createNotify(this).show();
+ return;
+ }
+
+ String fp = data.getStringExtra(ImportKeysProxyActivity.EXTRA_FINGERPRINT);
+ if (fp == null) {
+ Notify.create(this, R.string.error_scan_fp, Notify.LENGTH_LONG, Style.ERROR).show();
+ return;
+ }
+ if (mFingerprint.equalsIgnoreCase(fp)) {
+ certifyImmediate();
+ } else {
+ Notify.create(this, R.string.error_scan_match, Notify.LENGTH_LONG, Style.ERROR).show();
+ }
return;
}
- String fp = data.getStringExtra(ImportKeysProxyActivity.EXTRA_FINGERPRINT);
- if (fp == null) {
- Notify.create(this, "Error scanning fingerprint!",
- Notify.LENGTH_LONG, Notify.Style.ERROR).show();
+ case REQUEST_BACKUP: {
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
+
+ backupToFile();
return;
}
- if (mFingerprint.equalsIgnoreCase(fp)) {
- certifyImmediate();
- } else {
- Notify.create(this, "Fingerprints did not match!",
- Notify.LENGTH_LONG, Notify.Style.ERROR).show();
+
+ case REQUEST_CERTIFY: {
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
+
+ if (data.hasExtra(OperationResult.EXTRA_RESULT)) {
+ OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
+ result.createNotify(this).show();
+ }
+ return;
}
- return;
- }
+ case REQUEST_DELETE: {
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
- if (requestCode == REQUEST_DELETE && resultCode == Activity.RESULT_OK) {
- deleteKey();
+ setResult(RESULT_OK, data);
+ finish();
+ return;
+ }
}
- if (requestCode == REQUEST_EXPORT && resultCode == Activity.RESULT_OK) {
- exportToFile(mDataUri, mExportHelper, mProviderHelper);
- }
+ super.onActivityResult(requestCode, resultCode, data);
- if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
- OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
- result.createNotify(this).show();
- } else {
- super.onActivityResult(requestCode, resultCode, data);
- }
}
@Override
- protected void onNfcPerform() throws IOException {
+ protected void doNfcInBackground() throws IOException {
- final byte[] nfcFingerprints = nfcGetFingerprints();
- final String nfcUserId = nfcGetUserId();
- final byte[] nfcAid = nfcGetAid();
+ mNfcFingerprints = nfcGetFingerprints();
+ mNfcUserId = nfcGetUserId();
+ mNfcAid = nfcGetAid();
+ }
+
+ @Override
+ protected void onNfcPostExecute() throws IOException {
- long yubiKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(nfcFingerprints);
+ long yubiKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints);
try {
@@ -557,7 +556,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
// if the master key of that key matches this one, just show the yubikey dialog
if (KeyFormattingUtils.convertFingerprintToHex(candidateFp).equals(mFingerprint)) {
- showYubiKeyFragment(nfcFingerprints, nfcUserId, nfcAid);
+ showYubiKeyFragment(mNfcFingerprints, mNfcUserId, mNfcAid);
return;
}
@@ -570,14 +569,13 @@ public class ViewKeyActivity extends BaseNfcActivity implements
Intent intent = new Intent(
ViewKeyActivity.this, ViewKeyActivity.class);
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid);
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId);
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints);
startActivity(intent);
finish();
}
}, R.string.snack_yubikey_view).show();
-
// and if it's not found, offer import
} catch (PgpKeyNotFoundException e) {
Notify.create(this, R.string.snack_yubi_other, Notify.LENGTH_LONG,
@@ -586,28 +584,36 @@ public class ViewKeyActivity extends BaseNfcActivity implements
public void onAction() {
Intent intent = new Intent(
ViewKeyActivity.this, CreateKeyActivity.class);
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid);
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId);
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints);
startActivity(intent);
finish();
}
}, R.string.snack_yubikey_import).show();
}
-
}
- public void showYubiKeyFragment(byte[] nfcFingerprints, String nfcUserId, byte[] nfcAid) {
- ViewKeyYubiKeyFragment frag = ViewKeyYubiKeyFragment.newInstance(
- nfcFingerprints, nfcUserId, nfcAid);
+ public void showYubiKeyFragment(
+ final byte[] nfcFingerprints, final String nfcUserId, final byte[] nfcAid) {
- FragmentManager manager = getSupportFragmentManager();
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ ViewKeyYubiKeyFragment frag = ViewKeyYubiKeyFragment.newInstance(
+ mMasterKeyId, nfcFingerprints, nfcUserId, nfcAid);
+
+ FragmentManager manager = getSupportFragmentManager();
+
+ manager.popBackStack("yubikey", FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ manager.beginTransaction()
+ .addToBackStack("yubikey")
+ .replace(R.id.view_key_fragment, frag)
+ // if this is called while the activity wasn't resumed, just forget it happened
+ .commitAllowingStateLoss();
+ }
+ });
- manager.popBackStack("yubikey", FragmentManager.POP_BACK_STACK_INCLUSIVE);
- manager.beginTransaction()
- .addToBackStack("yubikey")
- .replace(R.id.view_key_fragment, frag)
- .commit();
}
private void encrypt(Uri dataUri, boolean text) {
@@ -620,7 +626,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
long keyId = new ProviderHelper(this)
.getCachedPublicKeyRing(dataUri)
.extractOrGetMasterKeyId();
- long[] encryptionKeyIds = new long[] {keyId};
+ long[] encryptionKeyIds = new long[]{keyId};
Intent intent;
if (text) {
intent = new Intent(this, EncryptTextActivity.class);
@@ -638,76 +644,6 @@ public class ViewKeyActivity extends BaseNfcActivity implements
}
}
- private void updateFromKeyserver(Uri dataUri, ProviderHelper providerHelper)
- throws ProviderHelper.NotFoundException {
-
- mIsRefreshing = true;
- mRefreshItem.setEnabled(false);
- mRefreshItem.setActionView(mRefresh);
- mRefresh.startAnimation(mRotate);
-
- byte[] blob = (byte[]) providerHelper.getGenericData(
- KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri),
- KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
- String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
-
- ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
- ArrayList<ParcelableKeyRing> entries = new ArrayList<>();
- entries.add(keyEntry);
-
- // Message is received after importing is done in KeychainIntentService
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(this) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- mIsRefreshing = false;
-
- if (returnData == null) {
- finish();
- return;
- }
- final ImportKeyResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- result.createNotify(ViewKeyActivity.this).show();
- }
- }
- };
-
- // fill values for this action
- Bundle data = new Bundle();
-
- // search config
- {
- Preferences prefs = Preferences.getPreferences(this);
- Preferences.CloudSearchPrefs cloudPrefs =
- new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
- data.putString(KeychainIntentService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
- }
-
- data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, entries);
-
- // Send all information needed to service to query keys in other thread
- Intent intent = new Intent(this, KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- serviceHandler.showProgressDialog(this);
-
- // start service with intent
- startService(intent);
-
- }
-
private void editKey(Uri dataUri) {
Intent editIntent = new Intent(this, EditKeyActivity.class);
editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri));
@@ -735,9 +671,12 @@ public class ViewKeyActivity extends BaseNfcActivity implements
AsyncTask<Void, Void, Bitmap> loadTask =
new AsyncTask<Void, Void, Bitmap>() {
protected Bitmap doInBackground(Void... unused) {
- String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
+ Uri uri = new Uri.Builder()
+ .scheme(Constants.FINGERPRINT_SCHEME)
+ .opaquePart(fingerprint)
+ .build();
// render with minimal size
- return QrCodeUtils.getQRCodeBitmap(qrCodeContent, 0);
+ return QrCodeUtils.getQRCodeBitmap(uri, 0);
}
protected void onPostExecute(Bitmap qrCode) {
@@ -761,7 +700,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
// These are the rows that we will retrieve.
- static final String[] PROJECTION = new String[] {
+ static final String[] PROJECTION = new String[]{
KeychainContract.KeyRings._ID,
KeychainContract.KeyRings.MASTER_KEY_ID,
KeychainContract.KeyRings.USER_ID,
@@ -797,6 +736,25 @@ public class ViewKeyActivity extends BaseNfcActivity implements
int mPreviousColor = 0;
+ /**
+ * Calculate a reasonable color for the status bar based on the given toolbar color.
+ * Style guides want the toolbar color to be a "700" on the Android scale and the status
+ * bar should be the same color at "500", this is roughly 17 / 20th of the value in each
+ * channel.
+ * http://www.google.com/design/spec/style/color.html#color-color-palette
+ */
+ static public int getStatusBarBackgroundColor(int color) {
+ int r = (color >> 16) & 0xff;
+ int g = (color >> 8) & 0xff;
+ int b = color & 0xff;
+
+ r = r * 17 / 20;
+ g = g * 17 / 20;
+ b = b * 17 / 20;
+
+ return (0xff << 24) | (r << 16) | (g << 8) | b;
+ }
+
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
/* TODO better error handling? May cause problems when a key is deleted,
@@ -809,172 +767,181 @@ public class ViewKeyActivity extends BaseNfcActivity implements
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
switch (loader.getId()) {
-
case LOADER_ID_UNIFIED: {
- if (!data.moveToFirst()) {
- return;
- }
-
- // get name, email, and comment from USER_ID
- KeyRing.UserId mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID));
- if (mainUserId.name != null) {
- mName.setText(mainUserId.name);
- } else {
- mName.setText(R.string.user_id_no_name);
- }
-
- mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
- mFingerprint = KeyFormattingUtils.convertFingerprintToHex(
- data.getBlob(INDEX_FINGERPRINT));
-
- mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0;
- mHasEncrypt = data.getInt(INDEX_HAS_ENCRYPT) != 0;
- mIsRevoked = data.getInt(INDEX_IS_REVOKED) > 0;
- mIsExpired = data.getInt(INDEX_IS_EXPIRED) != 0;
- mIsVerified = data.getInt(INDEX_VERIFIED) > 0;
-
- // if the refresh animation isn't playing
- if (!mRotate.hasStarted() && !mRotateSpin.hasStarted()) {
- // re-create options menu based on mIsSecret, mIsVerified
- supportInvalidateOptionsMenu();
- // this is done at the end of the animation otherwise
- }
-
- AsyncTask<Long, Void, Bitmap> photoTask =
- new AsyncTask<Long, Void, Bitmap>() {
- protected Bitmap doInBackground(Long... mMasterKeyId) {
- return ContactHelper.loadPhotoByMasterKeyId(getContentResolver(), mMasterKeyId[0], true);
- }
-
- protected void onPostExecute(Bitmap photo) {
- mPhoto.setImageBitmap(photo);
- mPhoto.setVisibility(View.VISIBLE);
- }
- };
-
- // Note: order is important
- int color;
- if (mIsRevoked) {
- mStatusText.setText(R.string.view_key_revoked);
- mStatusImage.setVisibility(View.VISIBLE);
- KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText,
- State.REVOKED, R.color.icons, true);
- color = getResources().getColor(R.color.android_red_light);
-
- mActionEncryptFile.setVisibility(View.GONE);
- mActionEncryptText.setVisibility(View.GONE);
- mActionNfc.setVisibility(View.GONE);
- mFab.setVisibility(View.GONE);
- mQrCodeLayout.setVisibility(View.GONE);
- } else if (mIsExpired) {
- if (mIsSecret) {
- mStatusText.setText(R.string.view_key_expired_secret);
+ if (data.moveToFirst()) {
+ // get name, email, and comment from USER_ID
+ KeyRing.UserId mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID));
+ if (mainUserId.name != null) {
+ mCollapsingToolbarLayout.setTitle(mainUserId.name);
} else {
- mStatusText.setText(R.string.view_key_expired);
- }
- mStatusImage.setVisibility(View.VISIBLE);
- KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText,
- State.EXPIRED, R.color.icons, true);
- color = getResources().getColor(R.color.android_red_light);
-
- mActionEncryptFile.setVisibility(View.GONE);
- mActionEncryptText.setVisibility(View.GONE);
- mActionNfc.setVisibility(View.GONE);
- mFab.setVisibility(View.GONE);
- mQrCodeLayout.setVisibility(View.GONE);
- } else if (mIsSecret) {
- mStatusText.setText(R.string.view_key_my_key);
- mStatusImage.setVisibility(View.GONE);
- color = getResources().getColor(R.color.primary);
- // reload qr code only if the fingerprint changed
- if (!mFingerprint.equals(mQrCodeLoaded)) {
- loadQrCode(mFingerprint);
- }
- photoTask.execute(mMasterKeyId);
- mQrCodeLayout.setVisibility(View.VISIBLE);
-
- // and place leftOf qr code
- RelativeLayout.LayoutParams nameParams = (RelativeLayout.LayoutParams)
- mName.getLayoutParams();
- // remove right margin
- nameParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0);
- if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- nameParams.setMarginEnd(0);
+ mCollapsingToolbarLayout.setTitle(getString(R.string.user_id_no_name));
}
- nameParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout);
- mName.setLayoutParams(nameParams);
-
- RelativeLayout.LayoutParams statusParams = (RelativeLayout.LayoutParams)
- mStatusText.getLayoutParams();
- statusParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0);
- if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- statusParams.setMarginEnd(0);
- }
- statusParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout);
- mStatusText.setLayoutParams(statusParams);
- mActionEncryptFile.setVisibility(View.VISIBLE);
- mActionEncryptText.setVisibility(View.VISIBLE);
+ mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
+ mFingerprint = KeyFormattingUtils.convertFingerprintToHex(data.getBlob(INDEX_FINGERPRINT));
+
+ // if it wasn't shown yet, display yubikey fragment
+ if (mShowYubikeyAfterCreation && getIntent().hasExtra(EXTRA_NFC_AID)) {
+ mShowYubikeyAfterCreation = false;
+ Intent intent = getIntent();
+ byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_NFC_FINGERPRINTS);
+ String nfcUserId = intent.getStringExtra(EXTRA_NFC_USER_ID);
+ byte[] nfcAid = intent.getByteArrayExtra(EXTRA_NFC_AID);
+ showYubiKeyFragment(nfcFingerprints, nfcUserId, nfcAid);
+ }
- // invokeBeam is available from API 21
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- mActionNfc.setVisibility(View.VISIBLE);
- } else {
- mActionNfc.setVisibility(View.GONE);
+ mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0;
+ mHasEncrypt = data.getInt(INDEX_HAS_ENCRYPT) != 0;
+ mIsRevoked = data.getInt(INDEX_IS_REVOKED) > 0;
+ mIsExpired = data.getInt(INDEX_IS_EXPIRED) != 0;
+ mIsVerified = data.getInt(INDEX_VERIFIED) > 0;
+
+ // if the refresh animation isn't playing
+ if (!mRotate.hasStarted() && !mRotateSpin.hasStarted()) {
+ // re-create options menu based on mIsSecret, mIsVerified
+ supportInvalidateOptionsMenu();
+ // this is done at the end of the animation otherwise
}
- mFab.setVisibility(View.VISIBLE);
- mFab.setIconDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp));
- } else {
- mActionEncryptFile.setVisibility(View.VISIBLE);
- mActionEncryptText.setVisibility(View.VISIBLE);
- mQrCodeLayout.setVisibility(View.GONE);
- mActionNfc.setVisibility(View.GONE);
- if (mIsVerified) {
- mStatusText.setText(R.string.view_key_verified);
+ AsyncTask<Long, Void, Bitmap> photoTask =
+ new AsyncTask<Long, Void, Bitmap>() {
+ protected Bitmap doInBackground(Long... mMasterKeyId) {
+ return ContactHelper.loadPhotoByMasterKeyId(getContentResolver(),
+ mMasterKeyId[0], true);
+ }
+
+ protected void onPostExecute(Bitmap photo) {
+ if (photo == null) {
+ return;
+ }
+
+ mPhoto.setImageBitmap(photo);
+ mPhotoLayout.setVisibility(View.VISIBLE);
+ }
+ };
+
+ // Note: order is important
+ int color;
+ if (mIsRevoked) {
+ mStatusText.setText(R.string.view_key_revoked);
mStatusImage.setVisibility(View.VISIBLE);
KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText,
- State.VERIFIED, R.color.icons, true);
- color = getResources().getColor(R.color.primary);
- photoTask.execute(mMasterKeyId);
+ State.REVOKED, R.color.icons, true);
+ color = getResources().getColor(R.color.key_flag_red);
+ mActionEncryptFile.setVisibility(View.INVISIBLE);
+ mActionEncryptText.setVisibility(View.INVISIBLE);
+ mActionNfc.setVisibility(View.INVISIBLE);
mFab.setVisibility(View.GONE);
- } else {
- mStatusText.setText(R.string.view_key_unverified);
+ mQrCodeLayout.setVisibility(View.GONE);
+ } else if (mIsExpired) {
+ if (mIsSecret) {
+ mStatusText.setText(R.string.view_key_expired_secret);
+ } else {
+ mStatusText.setText(R.string.view_key_expired);
+ }
mStatusImage.setVisibility(View.VISIBLE);
KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText,
- State.UNVERIFIED, R.color.icons, true);
- color = getResources().getColor(R.color.android_orange_light);
+ State.EXPIRED, R.color.icons, true);
+ color = getResources().getColor(R.color.key_flag_red);
+ mActionEncryptFile.setVisibility(View.INVISIBLE);
+ mActionEncryptText.setVisibility(View.INVISIBLE);
+ mActionNfc.setVisibility(View.INVISIBLE);
+ mFab.setVisibility(View.GONE);
+ mQrCodeLayout.setVisibility(View.GONE);
+ } else if (mIsSecret) {
+ mStatusText.setText(R.string.view_key_my_key);
+ mStatusImage.setVisibility(View.GONE);
+ color = getResources().getColor(R.color.key_flag_green);
+ // reload qr code only if the fingerprint changed
+ if (!mFingerprint.equals(mQrCodeLoaded)) {
+ loadQrCode(mFingerprint);
+ }
+ photoTask.execute(mMasterKeyId);
+ mQrCodeLayout.setVisibility(View.VISIBLE);
+
+ // and place leftOf qr code
+// RelativeLayout.LayoutParams nameParams = (RelativeLayout.LayoutParams)
+// mName.getLayoutParams();
+// // remove right margin
+// nameParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0);
+// if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+// nameParams.setMarginEnd(0);
+// }
+// nameParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout);
+// mName.setLayoutParams(nameParams);
+
+ RelativeLayout.LayoutParams statusParams = (RelativeLayout.LayoutParams)
+ mStatusText.getLayoutParams();
+ statusParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0);
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ statusParams.setMarginEnd(0);
+ }
+ statusParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout);
+ mStatusText.setLayoutParams(statusParams);
+
+ mActionEncryptFile.setVisibility(View.VISIBLE);
+ mActionEncryptText.setVisibility(View.VISIBLE);
+
+ // invokeBeam is available from API 21
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ mActionNfc.setVisibility(View.VISIBLE);
+ } else {
+ mActionNfc.setVisibility(View.GONE);
+ }
mFab.setVisibility(View.VISIBLE);
- }
- }
+ // noinspection deprecation (no getDrawable with theme at current minApi level 15!)
+ mFab.setImageDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp));
+ } else {
+ mActionEncryptFile.setVisibility(View.VISIBLE);
+ mActionEncryptText.setVisibility(View.VISIBLE);
+ mQrCodeLayout.setVisibility(View.GONE);
+ mActionNfc.setVisibility(View.GONE);
- if (mPreviousColor == 0 || mPreviousColor == color) {
- mStatusBar.setBackgroundColor(color);
- mBigToolbar.setBackgroundColor(color);
- mPreviousColor = color;
- } else {
- ObjectAnimator colorFade1 =
- ObjectAnimator.ofObject(mStatusBar, "backgroundColor",
- new ArgbEvaluator(), mPreviousColor, color);
- ObjectAnimator colorFade2 =
- ObjectAnimator.ofObject(mBigToolbar, "backgroundColor",
- new ArgbEvaluator(), mPreviousColor, color);
-
- colorFade1.setDuration(1200);
- colorFade2.setDuration(1200);
- colorFade1.start();
- colorFade2.start();
- mPreviousColor = color;
- }
+ if (mIsVerified) {
+ mStatusText.setText(R.string.view_key_verified);
+ mStatusImage.setVisibility(View.VISIBLE);
+ KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText,
+ State.VERIFIED, R.color.icons, true);
+ color = getResources().getColor(R.color.key_flag_green);
+ photoTask.execute(mMasterKeyId);
+
+ mFab.setVisibility(View.GONE);
+ } else {
+ mStatusText.setText(R.string.view_key_unverified);
+ mStatusImage.setVisibility(View.VISIBLE);
+ KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText,
+ State.UNVERIFIED, R.color.icons, true);
+ color = getResources().getColor(R.color.key_flag_orange);
+
+ mFab.setVisibility(View.VISIBLE);
+ }
+ }
- //noinspection deprecation
- mStatusImage.setAlpha(80);
+ if (mPreviousColor == 0 || mPreviousColor == color) {
+ mAppBarLayout.setBackgroundColor(color);
+ mCollapsingToolbarLayout.setContentScrimColor(color);
+ mCollapsingToolbarLayout.setStatusBarScrimColor(getStatusBarBackgroundColor(color));
+ mPreviousColor = color;
+ } else {
+ ObjectAnimator colorFade =
+ ObjectAnimator.ofObject(mAppBarLayout, "backgroundColor",
+ new ArgbEvaluator(), mPreviousColor, color);
+ mCollapsingToolbarLayout.setContentScrimColor(color);
+ mCollapsingToolbarLayout.setStatusBarScrimColor(getStatusBarBackgroundColor(color));
+
+ colorFade.setDuration(1200);
+ colorFade.start();
+ mPreviousColor = color;
+ }
- break;
+ //noinspection deprecation
+ mStatusImage.setAlpha(80);
+ break;
+ }
}
}
}
@@ -984,4 +951,64 @@ public class ViewKeyActivity extends BaseNfcActivity implements
}
+ // CryptoOperationHelper.Callback functions
+
+
+ private void updateFromKeyserver(Uri dataUri, ProviderHelper providerHelper)
+ throws ProviderHelper.NotFoundException {
+
+ mIsRefreshing = true;
+ mRefreshItem.setEnabled(false);
+ mRefreshItem.setActionView(mRefresh);
+ mRefresh.startAnimation(mRotate);
+
+ byte[] blob = (byte[]) providerHelper.getGenericData(
+ KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri),
+ KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
+ String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
+
+ ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
+ ArrayList<ParcelableKeyRing> entries = new ArrayList<>();
+ entries.add(keyEntry);
+ mKeyList = entries;
+
+ // search config
+ {
+ Preferences prefs = Preferences.getPreferences(this);
+ Preferences.CloudSearchPrefs cloudPrefs =
+ new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
+ mKeyserver = cloudPrefs.keyserver;
+ }
+
+ mOperationHelper.cryptoOperation();
+ }
+
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(mKeyList, mKeyserver);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ImportKeyResult result) {
+ mIsRefreshing = false;
+ result.createNotify(this).show();
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+ mIsRefreshing = false;
+ }
+
+ @Override
+ public void onCryptoOperationError(ImportKeyResult result) {
+ mIsRefreshing = false;
+ result.createNotify(this).show();
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return true;
+ }
+
}
+
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java
index 9e8a12c8a..edd9feec9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java
@@ -53,13 +53,14 @@ public class ViewKeyAdvActivity extends BaseActivity implements
protected Uri mDataUri;
public static final String EXTRA_SELECTED_TAB = "selected_tab";
- public static final int TAB_MAIN = 0;
- public static final int TAB_SHARE = 1;
+ public static final int TAB_SHARE = 0;
+ public static final int TAB_IDENTITIES = 1;
+ public static final int TAB_SUBKEYS = 2;
+ public static final int TAB_CERTS = 3;
// view
private ViewPager mViewPager;
private PagerSlidingTabStrip mSlidingTabLayout;
- private PagerTabStripAdapter mTabsAdapter;
private static final int LOADER_ID_UNIFIED = 0;
@@ -80,11 +81,8 @@ public class ViewKeyAdvActivity extends BaseActivity implements
mViewPager = (ViewPager) findViewById(R.id.pager);
mSlidingTabLayout = (PagerSlidingTabStrip) findViewById(R.id.sliding_tab_layout);
- int switchToTab = TAB_MAIN;
Intent intent = getIntent();
- if (intent.getExtras() != null && intent.getExtras().containsKey(EXTRA_SELECTED_TAB)) {
- switchToTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
- }
+ int switchToTab = intent.getIntExtra(EXTRA_SELECTED_TAB, TAB_SHARE);
mDataUri = getIntent().getData();
if (mDataUri == null) {
@@ -102,8 +100,6 @@ public class ViewKeyAdvActivity extends BaseActivity implements
}
}
- Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
-
// Prepare the loaders. Either re-connect with an existing ones,
// or start new ones.
getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
@@ -120,34 +116,29 @@ public class ViewKeyAdvActivity extends BaseActivity implements
}
private void initTabs(Uri dataUri) {
- mTabsAdapter = new PagerTabStripAdapter(this);
- mViewPager.setAdapter(mTabsAdapter);
+ PagerTabStripAdapter adapter = new PagerTabStripAdapter(this);
+ mViewPager.setAdapter(adapter);
Bundle shareBundle = new Bundle();
shareBundle.putParcelable(ViewKeyAdvUserIdsFragment.ARG_DATA_URI, dataUri);
- mTabsAdapter.addTab(ViewKeyAdvShareFragment.class,
+ adapter.addTab(ViewKeyAdvShareFragment.class,
shareBundle, getString(R.string.key_view_tab_share));
Bundle userIdsBundle = new Bundle();
userIdsBundle.putParcelable(ViewKeyAdvUserIdsFragment.ARG_DATA_URI, dataUri);
- mTabsAdapter.addTab(ViewKeyAdvUserIdsFragment.class,
+ adapter.addTab(ViewKeyAdvUserIdsFragment.class,
userIdsBundle, getString(R.string.section_user_ids));
Bundle keysBundle = new Bundle();
keysBundle.putParcelable(ViewKeyAdvSubkeysFragment.ARG_DATA_URI, dataUri);
- mTabsAdapter.addTab(ViewKeyAdvSubkeysFragment.class,
+ adapter.addTab(ViewKeyAdvSubkeysFragment.class,
keysBundle, getString(R.string.key_view_tab_keys));
Bundle certsBundle = new Bundle();
certsBundle.putParcelable(ViewKeyAdvCertsFragment.ARG_DATA_URI, dataUri);
- mTabsAdapter.addTab(ViewKeyAdvCertsFragment.class,
+ adapter.addTab(ViewKeyAdvCertsFragment.class,
certsBundle, getString(R.string.key_view_tab_certs));
- Bundle trustBundle = new Bundle();
- trustBundle.putParcelable(ViewKeyTrustFragment.ARG_DATA_URI, dataUri);
- mTabsAdapter.addTab(ViewKeyTrustFragment.class,
- trustBundle, getString(R.string.key_view_tab_keybase));
-
// update layout after operations
mSlidingTabLayout.setViewPager(mViewPager);
}
@@ -185,11 +176,8 @@ public class ViewKeyAdvActivity extends BaseActivity implements
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- /* TODO better error handling? May cause problems when a key is deleted,
- * because the notification triggers faster than the activity closes.
- */
// Avoid NullPointerExceptions...
- if (data.getCount() == 0) {
+ if (data == null || data.getCount() == 0) {
return;
}
// Swap the new cursor in. (The framework will take care of closing the
@@ -217,18 +205,18 @@ public class ViewKeyAdvActivity extends BaseActivity implements
// Note: order is important
int color;
if (isRevoked || isExpired) {
- color = getResources().getColor(R.color.android_red_light);
+ color = getResources().getColor(R.color.key_flag_red);
} else if (isSecret) {
- color = getResources().getColor(R.color.primary);
+ color = getResources().getColor(R.color.android_green_light);
} else {
if (isVerified) {
- color = getResources().getColor(R.color.primary);
+ color = getResources().getColor(R.color.android_green_light);
} else {
- color = getResources().getColor(R.color.android_orange_light);
+ color = getResources().getColor(R.color.key_flag_orange);
}
}
mToolbar.setBackgroundColor(color);
- mStatusBar.setBackgroundColor(color);
+ mStatusBar.setBackgroundColor(ViewKeyActivity.getStatusBarBackgroundColor(color));
mSlidingTabLayout.setBackgroundColor(color);
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java
index fde0f62fd..4a46896bc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java
@@ -17,7 +17,17 @@
package org.sufficientlysecure.keychain.ui;
+
+import java.io.BufferedWriter;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+import android.app.Activity;
import android.app.ActivityOptions;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
@@ -42,72 +52,51 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
-import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
+import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
+import org.sufficientlysecure.keychain.util.ExportHelper;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.NfcHelper;
-import java.io.BufferedWriter;
-import java.io.OutputStreamWriter;
-import java.io.IOException;
-import java.io.FileNotFoundException;
-
public class ViewKeyAdvShareFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
public static final String ARG_DATA_URI = "uri";
- private TextView mFingerprint;
private ImageView mQrCode;
private CardView mQrCodeLayout;
- private View mFingerprintShareButton;
- private View mFingerprintClipboardButton;
- private View mKeyShareButton;
- private View mKeyClipboardButton;
- private View mKeyNfcButton;
- private ImageButton mKeySafeSlingerButton;
- private View mKeyUploadButton;
-
- ProviderHelper mProviderHelper;
+ private TextView mFingerprintView;
+
NfcHelper mNfcHelper;
private static final int LOADER_ID_UNIFIED = 0;
private Uri mDataUri;
+ private byte[] mFingerprint;
+ private long mMasterKeyId;
+
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
View view = inflater.inflate(R.layout.view_key_adv_share_fragment, getContainer());
- mProviderHelper = new ProviderHelper(ViewKeyAdvShareFragment.this.getActivity());
- mNfcHelper = new NfcHelper(getActivity(), mProviderHelper);
+ ProviderHelper providerHelper = new ProviderHelper(ViewKeyAdvShareFragment.this.getActivity());
+ mNfcHelper = new NfcHelper(getActivity(), providerHelper);
- mFingerprint = (TextView) view.findViewById(R.id.view_key_fingerprint);
+ mFingerprintView = (TextView) view.findViewById(R.id.view_key_fingerprint);
mQrCode = (ImageView) view.findViewById(R.id.view_key_qr_code);
mQrCodeLayout = (CardView) view.findViewById(R.id.view_key_qr_code_layout);
- mFingerprintShareButton = view.findViewById(R.id.view_key_action_fingerprint_share);
- mFingerprintClipboardButton = view.findViewById(R.id.view_key_action_fingerprint_clipboard);
- mKeyShareButton = view.findViewById(R.id.view_key_action_key_share);
- mKeyNfcButton = view.findViewById(R.id.view_key_action_key_nfc);
- mKeyClipboardButton = view.findViewById(R.id.view_key_action_key_clipboard);
- mKeySafeSlingerButton = (ImageButton) view.findViewById(R.id.view_key_action_key_safeslinger);
- mKeyUploadButton = view.findViewById(R.id.view_key_action_upload);
-
- mKeySafeSlingerButton.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
- PorterDuff.Mode.SRC_IN);
-
mQrCodeLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -115,45 +104,67 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
}
});
- mFingerprintShareButton.setOnClickListener(new View.OnClickListener() {
+ View vFingerprintShareButton = view.findViewById(R.id.view_key_action_fingerprint_share);
+ View vFingerprintClipboardButton = view.findViewById(R.id.view_key_action_fingerprint_clipboard);
+ View vKeyShareButton = view.findViewById(R.id.view_key_action_key_share);
+ View vKeySafeButton = view.findViewById(R.id.view_key_action_key_export);
+ View vKeyNfcButton = view.findViewById(R.id.view_key_action_key_nfc);
+ View vKeyClipboardButton = view.findViewById(R.id.view_key_action_key_clipboard);
+ ImageButton vKeySafeSlingerButton = (ImageButton) view.findViewById(R.id.view_key_action_key_safeslinger);
+ View vKeyUploadButton = view.findViewById(R.id.view_key_action_upload);
+ vKeySafeSlingerButton.setColorFilter(FormattingUtils.getColorFromAttr(getActivity(), R.attr.colorTertiaryText),
+ PorterDuff.Mode.SRC_IN);
+
+ vFingerprintShareButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- share(mDataUri, mProviderHelper, true, false);
+ share(true, false);
}
});
- mFingerprintClipboardButton.setOnClickListener(new View.OnClickListener() {
+ vFingerprintClipboardButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- share(mDataUri, mProviderHelper, true, true);
+ share(true, true);
}
});
- mKeyShareButton.setOnClickListener(new View.OnClickListener() {
+ vKeyShareButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- share(mDataUri, mProviderHelper, false, false);
+ share(false, false);
}
});
- mKeyClipboardButton.setOnClickListener(new View.OnClickListener() {
+ vKeySafeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- share(mDataUri, mProviderHelper, false, true);
+ exportToFile();
}
});
-
- mKeyNfcButton.setOnClickListener(new View.OnClickListener() {
+ vKeyClipboardButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- mNfcHelper.invokeNfcBeam();
+ share(false, true);
}
});
- mKeySafeSlingerButton.setOnClickListener(new View.OnClickListener() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ vKeyNfcButton.setVisibility(View.VISIBLE);
+ vKeyNfcButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mNfcHelper.invokeNfcBeam();
+ }
+ });
+ } else {
+ vKeyNfcButton.setVisibility(View.GONE);
+ }
+
+ vKeySafeSlingerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startSafeSlinger(mDataUri);
}
});
- mKeyUploadButton.setOnClickListener(new View.OnClickListener() {
+ vKeyUploadButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
uploadToKeyserver();
@@ -163,6 +174,11 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
return root;
}
+ private void exportToFile() {
+ new ExportHelper(getActivity()).showExportKeysDialog(
+ mMasterKeyId, Constants.Path.APP_DIR_FILE, false);
+ }
+
private void startSafeSlinger(Uri dataUri) {
long keyId = 0;
try {
@@ -177,97 +193,87 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
startActivityForResult(safeSlingerIntent, 0);
}
- private void share(Uri dataUri, ProviderHelper providerHelper, boolean fingerprintOnly,
- boolean toClipboard) {
+ private void share(boolean fingerprintOnly, boolean toClipboard) {
+ Activity activity = getActivity();
+ if (activity == null || mFingerprint == null) {
+ return;
+ }
+ ProviderHelper providerHelper = new ProviderHelper(activity);
+
try {
String content;
- byte[] fingerprintData = (byte[]) providerHelper.getGenericData(
- KeyRings.buildUnifiedKeyRingUri(dataUri),
- Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
if (fingerprintOnly) {
- String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fingerprintData);
+ String fingerprint = KeyFormattingUtils.convertFingerprintToHex(mFingerprint);
if (!toClipboard) {
content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
} else {
content = fingerprint;
}
} else {
- Uri uri = KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri);
- // get public keyring as ascii armored string
- content = providerHelper.getKeyRingAsArmoredString(uri);
+ content = providerHelper.getKeyRingAsArmoredString(
+ KeychainContract.KeyRingData.buildPublicKeyRingUri(mDataUri));
}
if (toClipboard) {
- ClipboardReflection.copyToClipboard(getActivity(), content);
- String message;
- if (fingerprintOnly) {
- message = getResources().getString(R.string.fingerprint_copied_to_clipboard);
- } else {
- message = getResources().getString(R.string.key_copied_to_clipboard);
- }
- Notify.create(getActivity(), message, Notify.Style.OK).show();
- } else {
- // Android will fail with android.os.TransactionTooLargeException if key is too big
- // see http://www.lonestarprod.com/?p=34
- if (content.length() >= 86389) {
- Notify.create(getActivity(), R.string.key_too_big_for_sharing,
- Notify.Style.ERROR).show();
+ ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
+ if (clipMan == null) {
+ Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR);
return;
}
- // let user choose application
- Intent sendIntent = new Intent(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_TEXT, content);
- sendIntent.setType("text/plain");
+ ClipData clip = ClipData.newPlainText(Constants.CLIPBOARD_LABEL, content);
+ clipMan.setPrimaryClip(clip);
- String title;
- if (fingerprintOnly) {
- title = getResources().getString(R.string.title_share_fingerprint_with);
- } else {
- title = getResources().getString(R.string.title_share_key);
- }
- Intent shareChooser = Intent.createChooser(sendIntent, title);
-
- // Bluetooth Share will convert text/plain sent via EXTRA_TEXT to HTML
- // Add replacement extra to send a text/plain file instead.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- try {
- String primaryUserId = UncachedKeyRing.decodeFromData(content.getBytes()).
- getPublicKey().getPrimaryUserIdWithFallback();
-
- TemporaryStorageProvider shareFileProv = new TemporaryStorageProvider();
- Uri contentUri = TemporaryStorageProvider.createFile(getActivity(),
- primaryUserId + Constants.FILE_EXTENSION_ASC);
-
- BufferedWriter contentWriter = new BufferedWriter(new OutputStreamWriter(
- new ParcelFileDescriptor.AutoCloseOutputStream(
- shareFileProv.openFile(contentUri, "w"))));
- contentWriter.write(content);
- contentWriter.close();
-
- // create replacement extras inside try{}:
- // if file creation fails, just don't add the replacements
- Bundle replacements = new Bundle();
- shareChooser.putExtra(Intent.EXTRA_REPLACEMENT_EXTRAS, replacements);
-
- Bundle bluetoothExtra = new Bundle(sendIntent.getExtras());
- replacements.putBundle("com.android.bluetooth", bluetoothExtra);
-
- bluetoothExtra.putParcelable(Intent.EXTRA_STREAM, contentUri);
- } catch (FileNotFoundException e) {
- Log.e(Constants.TAG, "error creating temporary Bluetooth key share file!", e);
- Notify.create(getActivity(), R.string.error_bluetooth_file, Notify.Style.ERROR).show();
- }
- }
+ Notify.create(activity, fingerprintOnly ? R.string.fingerprint_copied_to_clipboard
+ : R.string.key_copied_to_clipboard, Notify.Style.OK).show();
+ return;
+ }
+
+ // Android will fail with android.os.TransactionTooLargeException if key is too big
+ // see http://www.lonestarprod.com/?p=34
+ if (content.length() >= 86389) {
+ Notify.create(activity, R.string.key_too_big_for_sharing, Notify.Style.ERROR).show();
+ return;
+ }
- startActivity(shareChooser);
+ // let user choose application
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_TEXT, content);
+ sendIntent.setType("text/plain");
+
+ // Bluetooth Share will convert text/plain sent via EXTRA_TEXT to HTML
+ // Add replacement extra to send a text/plain file instead.
+ try {
+ TemporaryStorageProvider shareFileProv = new TemporaryStorageProvider();
+ Uri contentUri = TemporaryStorageProvider.createFile(activity,
+ KeyFormattingUtils.convertFingerprintToHex(mFingerprint) + Constants.FILE_EXTENSION_ASC);
+
+ BufferedWriter contentWriter = new BufferedWriter(new OutputStreamWriter(
+ new ParcelFileDescriptor.AutoCloseOutputStream(
+ shareFileProv.openFile(contentUri, "w"))));
+ contentWriter.write(content);
+ contentWriter.close();
+
+ sendIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
+ } catch (FileNotFoundException e) {
+ Log.e(Constants.TAG, "error creating temporary Bluetooth key share file!", e);
+ // no need for a snackbar because one sharing option doesn't work
+ // Notify.create(getActivity(), R.string.error_temp_file, Notify.Style.ERROR).show();
}
+
+
+ String title = getString(fingerprintOnly
+ ? R.string.title_share_fingerprint_with : R.string.title_share_key);
+ Intent shareChooser = Intent.createChooser(sendIntent, title);
+
+ startActivity(shareChooser);
+
} catch (PgpGeneralException | IOException e) {
Log.e(Constants.TAG, "error processing key!", e);
- Notify.create(getActivity(), R.string.error_key_processing, Notify.Style.ERROR).show();
+ Notify.create(activity, R.string.error_key_processing, Notify.Style.ERROR).show();
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
- Notify.create(getActivity(), R.string.error_key_not_found, Notify.Style.ERROR).show();
+ Notify.create(activity, R.string.error_key_not_found, Notify.Style.ERROR).show();
}
}
@@ -288,8 +294,8 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
}
@Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
if (dataUri == null) {
@@ -304,8 +310,6 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
private void loadData(Uri dataUri) {
mDataUri = dataUri;
- Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
-
// Prepare the loaders. Either re-connect with an existing ones,
// or start new ones.
getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
@@ -315,19 +319,10 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
}
static final String[] UNIFIED_PROJECTION = new String[] {
- KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET,
- KeyRings.USER_ID, KeyRings.FINGERPRINT,
- KeyRings.ALGORITHM, KeyRings.KEY_SIZE, KeyRings.CREATION, KeyRings.IS_EXPIRED,
-
+ KeyRings._ID, KeyRings.FINGERPRINT
};
- static final int INDEX_UNIFIED_MASTER_KEY_ID = 1;
- static final int INDEX_UNIFIED_HAS_ANY_SECRET = 2;
- static final int INDEX_UNIFIED_USER_ID = 3;
- static final int INDEX_UNIFIED_FINGERPRINT = 4;
- static final int INDEX_UNIFIED_ALGORITHM = 5;
- static final int INDEX_UNIFIED_KEY_SIZE = 6;
- static final int INDEX_UNIFIED_CREATION = 7;
- static final int INDEX_UNIFIED_ID_EXPIRED = 8;
+
+ static final int INDEX_UNIFIED_FINGERPRINT = 1;
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
setContentShown(false);
@@ -343,11 +338,8 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- /* TODO better error handling? May cause problems when a key is deleted,
- * because the notification triggers faster than the activity closes.
- */
// Avoid NullPointerExceptions...
- if (data.getCount() == 0) {
+ if (data == null || data.getCount() == 0) {
return;
}
// Swap the new cursor in. (The framework will take care of closing the
@@ -357,10 +349,7 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
if (data.moveToFirst()) {
byte[] fingerprintBlob = data.getBlob(INDEX_UNIFIED_FINGERPRINT);
- String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fingerprintBlob);
- mFingerprint.setText(KeyFormattingUtils.colorizeFingerprint(fingerprint));
-
- loadQrCode(fingerprint);
+ setFingerprint(fingerprintBlob);
break;
}
@@ -375,20 +364,26 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
* We need to make sure we are no longer using it.
*/
public void onLoaderReset(Loader<Cursor> loader) {
+ mFingerprint = null;
}
- /**
- * Load QR Code asynchronously and with a fade in animation
- *
- * @param fingerprint
- */
- private void loadQrCode(final String fingerprint) {
+ /** Load QR Code asynchronously and with a fade in animation */
+ private void setFingerprint(byte[] fingerprintBlob) {
+ mFingerprint = fingerprintBlob;
+ mMasterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(fingerprintBlob);
+
+ final String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fingerprintBlob);
+ mFingerprintView.setText(KeyFormattingUtils.colorizeFingerprint(fingerprint));
+
AsyncTask<Void, Void, Bitmap> loadTask =
new AsyncTask<Void, Void, Bitmap>() {
protected Bitmap doInBackground(Void... unused) {
- String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
+ Uri uri = new Uri.Builder()
+ .scheme(Constants.FINGERPRINT_SCHEME)
+ .opaquePart(fingerprint)
+ .build();
// render with minimal size
- return QrCodeUtils.getQRCodeBitmap(qrCodeContent, 0);
+ return QrCodeUtils.getQRCodeBitmap(uri, 0);
}
protected void onPostExecute(Bitmap qrCode) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java
index c01a94286..89e5d741f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java
@@ -378,7 +378,7 @@ public class ViewKeyFragment extends LoaderFragment implements
* because the notification triggers faster than the activity closes.
*/
// Avoid NullPointerExceptions...
- if (data.getCount() == 0) {
+ if (data == null || data.getCount() == 0) {
return;
}
// Swap the new cursor in. (The framework will take care of closing the
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java
index d5870d8c5..266633061 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java
@@ -17,15 +17,12 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -48,24 +45,26 @@ import com.textuality.keybase.lib.User;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.KeybaseVerificationResult;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ParcelableProxy;
+import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
-public class ViewKeyTrustFragment extends LoaderFragment implements
- LoaderManager.LoaderCallbacks<Cursor> {
+public class ViewKeyKeybaseFragment extends LoaderFragment implements
+ LoaderManager.LoaderCallbacks<Cursor>,
+ CryptoOperationHelper.Callback<KeybaseVerificationParcel, KeybaseVerificationResult> {
public static final String ARG_DATA_URI = "uri";
- private View mStartSearch;
- private TextView mTrustReadout;
private TextView mReportHeader;
private TableLayout mProofListing;
private LayoutInflater mInflater;
@@ -77,15 +76,33 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
// for retrieving the key we’re working on
private Uri mDataUri;
+ private Proof mProof;
+
+ // for CryptoOperationHelper,Callback
+ private String mKeybaseProof;
+ private String mKeybaseFingerprint;
+ private CryptoOperationHelper<KeybaseVerificationParcel, KeybaseVerificationResult>
+ mKeybaseOpHelper;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static ViewKeyKeybaseFragment newInstance(Uri dataUri) {
+ ViewKeyKeybaseFragment frag = new ViewKeyKeybaseFragment();
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_DATA_URI, dataUri);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
View view = inflater.inflate(R.layout.view_key_adv_keybase_fragment, getContainer());
mInflater = inflater;
- mTrustReadout = (TextView) view.findViewById(R.id.view_key_trust_readout);
- mStartSearch = view.findViewById(R.id.view_key_trust_search_cloud);
- mStartSearch.setEnabled(false);
mReportHeader = (TextView) view.findViewById(R.id.view_key_trust_cloud_narrative);
mProofListing = (TableLayout) view.findViewById(R.id.view_key_proof_list);
mProofVerifyHeader = view.findViewById(R.id.view_key_proof_verify_header);
@@ -148,58 +165,45 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
}
boolean nothingSpecial = true;
- StringBuilder message = new StringBuilder();
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
if (data.moveToFirst()) {
- if (data.getInt(INDEX_UNIFIED_HAS_ANY_SECRET) != 0) {
- message.append(getString(R.string.key_trust_it_is_yours)).append("\n");
- nothingSpecial = false;
- } else if (data.getInt(INDEX_VERIFIED) != 0) {
- message.append(getString(R.string.key_trust_already_verified)).append("\n");
- nothingSpecial = false;
- }
+ final byte[] fp = data.getBlob(INDEX_TRUST_FINGERPRINT);
+ final String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fp);
- // If this key is revoked, don’t trust it!
- if (data.getInt(INDEX_TRUST_IS_REVOKED) != 0) {
- message.append(getString(R.string.key_trust_revoked)).
- append(getString(R.string.key_trust_old_keys));
+ startSearch(fingerprint);
+ }
- nothingSpecial = false;
- } else {
- if (data.getInt(INDEX_TRUST_IS_EXPIRED) != 0) {
+ setContentShown(true);
+ }
- // if expired, don’t trust it!
- message.append(getString(R.string.key_trust_expired)).
- append(getString(R.string.key_trust_old_keys));
+ private void startSearch(final String fingerprint) {
+ final Preferences.ProxyPrefs proxyPrefs =
+ Preferences.getPreferences(getActivity()).getProxyPrefs();
- nothingSpecial = false;
- }
+ OrbotHelper.DialogActions dialogActions = new OrbotHelper.DialogActions() {
+ @Override
+ public void onOrbotStarted() {
+ new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint);
}
- if (nothingSpecial) {
- message.append(getString(R.string.key_trust_maybe));
+ @Override
+ public void onNeutralButton() {
+ new DescribeKey(ParcelableProxy.getForNoProxy())
+ .execute(fingerprint);
}
- final byte[] fp = data.getBlob(INDEX_TRUST_FINGERPRINT);
- final String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fp);
- if (fingerprint != null) {
+ @Override
+ public void onCancel() {
- mStartSearch.setEnabled(true);
- mStartSearch.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mStartSearch.setEnabled(false);
- new DescribeKey().execute(fingerprint);
- }
- });
}
- }
+ };
- mTrustReadout.setText(message);
- setContentShown(true);
+ if (OrbotHelper.putOrbotInRequiredState(dialogActions, getActivity())) {
+ new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint);
+ }
}
/**
@@ -223,6 +227,11 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
// look for evidence from keybase in the background, make tabular version of result
//
private class DescribeKey extends AsyncTask<String, Void, ResultPage> {
+ ParcelableProxy mParcelableProxy;
+
+ public DescribeKey(ParcelableProxy parcelableProxy) {
+ mParcelableProxy = parcelableProxy;
+ }
@Override
protected ResultPage doInBackground(String... args) {
@@ -231,7 +240,7 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
final ArrayList<CharSequence> proofList = new ArrayList<CharSequence>();
final Hashtable<Integer, ArrayList<Proof>> proofs = new Hashtable<Integer, ArrayList<Proof>>();
try {
- User keybaseUser = User.findByFingerprint(fingerprint);
+ User keybaseUser = User.findByFingerprint(fingerprint, mParcelableProxy.getProxy());
for (Proof proof : keybaseUser.getProofs()) {
Integer proofType = proof.getType();
appendIfOK(proofs, proofType, proof);
@@ -243,8 +252,6 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
Proof[] proofsFor = proofs.get(proofType).toArray(x);
if (proofsFor.length > 0) {
SpannableStringBuilder ssb = new SpannableStringBuilder();
- ssb.append(getProofNarrative(proofType)).append(" ");
-
int i = 0;
while (i < proofsFor.length - 1) {
appendProofLinks(ssb, fingerprint, proofsFor[i]);
@@ -252,7 +259,7 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
i++;
}
appendProofLinks(ssb, fingerprint, proofsFor[i]);
- proofList.add(ssb);
+ proofList.add(formatSpannableString(ssb, getProofNarrative(proofType)));
}
}
@@ -262,6 +269,20 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
return new ResultPage(getString(R.string.key_trust_results_prefix), proofList);
}
+ private SpannableStringBuilder formatSpannableString(SpannableStringBuilder proofLinks, String proofType) {
+ //Formatting SpannableStringBuilder with String.format() causes the links to stop working.
+ //This method is to insert the links while reserving the links
+
+ SpannableStringBuilder ssb = new SpannableStringBuilder();
+ ssb.append(proofType);
+ if (proofType.contains("%s")) {
+ int i = proofType.indexOf("%s");
+ ssb.replace(i, i + 2, proofLinks);
+ } else ssb.append(proofLinks);
+
+ return ssb;
+ }
+
private SpannableStringBuilder appendProofLinks(SpannableStringBuilder ssb, final String fingerprint, final Proof proof) throws KeybaseException {
int startAt = ssb.length();
String handle = proof.getHandle();
@@ -291,7 +312,6 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
result.mHeader = getActivity().getString(R.string.key_trust_no_cloud_evidence);
}
- mStartSearch.setVisibility(View.GONE);
mReportHeader.setVisibility(View.VISIBLE);
mProofListing.setVisibility(View.VISIBLE);
mReportHeader.setText(result.mHeader);
@@ -306,22 +326,35 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
text.setMovementMethod(LinkMovementMethod.getInstance());
mProofListing.addView(row);
}
-
- // mSearchReport.loadDataWithBaseURL("file:///android_res/drawable/", s, "text/html", "UTF-8", null);
}
}
private String getProofNarrative(int proofType) {
int stringIndex;
switch (proofType) {
- case Proof.PROOF_TYPE_TWITTER: stringIndex = R.string.keybase_narrative_twitter; break;
- case Proof.PROOF_TYPE_GITHUB: stringIndex = R.string.keybase_narrative_github; break;
- case Proof.PROOF_TYPE_DNS: stringIndex = R.string.keybase_narrative_dns; break;
- case Proof.PROOF_TYPE_WEB_SITE: stringIndex = R.string.keybase_narrative_web_site; break;
- case Proof.PROOF_TYPE_HACKERNEWS: stringIndex = R.string.keybase_narrative_hackernews; break;
- case Proof.PROOF_TYPE_COINBASE: stringIndex = R.string.keybase_narrative_coinbase; break;
- case Proof.PROOF_TYPE_REDDIT: stringIndex = R.string.keybase_narrative_reddit; break;
- default: stringIndex = R.string.keybase_narrative_unknown;
+ case Proof.PROOF_TYPE_TWITTER:
+ stringIndex = R.string.keybase_narrative_twitter;
+ break;
+ case Proof.PROOF_TYPE_GITHUB:
+ stringIndex = R.string.keybase_narrative_github;
+ break;
+ case Proof.PROOF_TYPE_DNS:
+ stringIndex = R.string.keybase_narrative_dns;
+ break;
+ case Proof.PROOF_TYPE_WEB_SITE:
+ stringIndex = R.string.keybase_narrative_web_site;
+ break;
+ case Proof.PROOF_TYPE_HACKERNEWS:
+ stringIndex = R.string.keybase_narrative_hackernews;
+ break;
+ case Proof.PROOF_TYPE_COINBASE:
+ stringIndex = R.string.keybase_narrative_coinbase;
+ break;
+ case Proof.PROOF_TYPE_REDDIT:
+ stringIndex = R.string.keybase_narrative_reddit;
+ break;
+ default:
+ stringIndex = R.string.keybase_narrative_unknown;
}
return getActivity().getString(stringIndex);
}
@@ -338,125 +371,150 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
// which proofs do we have working verifiers for?
private boolean haveProofFor(int proofType) {
switch (proofType) {
- case Proof.PROOF_TYPE_TWITTER: return true;
- case Proof.PROOF_TYPE_GITHUB: return true;
- case Proof.PROOF_TYPE_DNS: return true;
- case Proof.PROOF_TYPE_WEB_SITE: return true;
- case Proof.PROOF_TYPE_HACKERNEWS: return true;
- case Proof.PROOF_TYPE_COINBASE: return true;
- case Proof.PROOF_TYPE_REDDIT: return true;
- default: return false;
+ case Proof.PROOF_TYPE_TWITTER:
+ return true;
+ case Proof.PROOF_TYPE_GITHUB:
+ return true;
+ case Proof.PROOF_TYPE_DNS:
+ return true;
+ case Proof.PROOF_TYPE_WEB_SITE:
+ return true;
+ case Proof.PROOF_TYPE_HACKERNEWS:
+ return true;
+ case Proof.PROOF_TYPE_COINBASE:
+ return true;
+ case Proof.PROOF_TYPE_REDDIT:
+ return true;
+ default:
+ return false;
}
}
private void verify(final Proof proof, final String fingerprint) {
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
- Bundle data = new Bundle();
- intent.setAction(KeychainIntentService.ACTION_VERIFY_KEYBASE_PROOF);
- data.putString(KeychainIntentService.KEYBASE_PROOF, proof.toString());
- data.putString(KeychainIntentService.KEYBASE_REQUIRED_FINGERPRINT, fingerprint);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+ mProof = proof;
+ mKeybaseProof = proof.toString();
+ mKeybaseFingerprint = fingerprint;
mProofVerifyDetail.setVisibility(View.GONE);
- // Create a new Messenger for the communication back after proof work is done
- //
- ServiceProgressHandler handler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_verifying_signature),
- ProgressDialog.STYLE_HORIZONTAL,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- Bundle returnData = message.getData();
- String msg = returnData.getString(ServiceProgressHandler.DATA_MESSAGE);
- SpannableStringBuilder ssb = new SpannableStringBuilder();
-
- if ((msg != null) && msg.equals("OK")) {
-
- //yay
- String proofUrl = returnData.getString(ServiceProgressHandler.KEYBASE_PROOF_URL);
- String presenceUrl = returnData.getString(ServiceProgressHandler.KEYBASE_PRESENCE_URL);
- String presenceLabel = returnData.getString(ServiceProgressHandler.KEYBASE_PRESENCE_LABEL);
-
- String proofLabel;
- switch (proof.getType()) {
- case Proof.PROOF_TYPE_TWITTER:
- proofLabel = getString(R.string.keybase_twitter_proof);
- break;
- case Proof.PROOF_TYPE_DNS:
- proofLabel = getString(R.string.keybase_dns_proof);
- break;
- case Proof.PROOF_TYPE_WEB_SITE:
- proofLabel = getString(R.string.keybase_web_site_proof);
- break;
- case Proof.PROOF_TYPE_GITHUB:
- proofLabel = getString(R.string.keybase_github_proof);
- break;
- case Proof.PROOF_TYPE_REDDIT:
- proofLabel = getString(R.string.keybase_reddit_proof);
- break;
- default:
- proofLabel = getString(R.string.keybase_a_post);
- break;
- }
+ mKeybaseOpHelper = new CryptoOperationHelper<>(1, this, this,
+ R.string.progress_verifying_signature);
+ mKeybaseOpHelper.cryptoOperation();
+ }
- ssb.append(getString(R.string.keybase_proof_succeeded));
- StyleSpan bold = new StyleSpan(Typeface.BOLD);
- ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- ssb.append("\n\n");
- int length = ssb.length();
- ssb.append(proofLabel);
- if (proofUrl != null) {
- URLSpan postLink = new URLSpan(proofUrl);
- ssb.setSpan(postLink, length, length + proofLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- if (Proof.PROOF_TYPE_DNS == proof.getType()) {
- ssb.append(" ").append(getString(R.string.keybase_for_the_domain)).append(" ");
- } else {
- ssb.append(" ").append(getString(R.string.keybase_fetched_from)).append(" ");
- }
- length = ssb.length();
- URLSpan presenceLink = new URLSpan(presenceUrl);
- ssb.append(presenceLabel);
- ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- if (Proof.PROOF_TYPE_REDDIT == proof.getType()) {
- ssb.append(", ").
- append(getString(R.string.keybase_reddit_attribution)).
- append(" “").append(proof.getHandle()).append("”, ");
- }
- ssb.append(" ").append(getString(R.string.keybase_contained_signature));
- } else {
- // verification failed!
- msg = returnData.getString(ServiceProgressHandler.DATA_ERROR);
- ssb.append(getString(R.string.keybase_proof_failure));
- if (msg == null) {
- msg = getString(R.string.keybase_unknown_proof_failure);
- }
- StyleSpan bold = new StyleSpan(Typeface.BOLD);
- ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- ssb.append("\n\n").append(msg);
- }
- mProofVerifyHeader.setVisibility(View.VISIBLE);
- mProofVerifyDetail.setVisibility(View.VISIBLE);
- mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance());
- mProofVerifyDetail.setText(ssb);
- }
- }
- };
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (mKeybaseOpHelper != null) {
+ mKeybaseOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+ }
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(handler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+ // CryptoOperationHelper.Callback methods
+ @Override
+ public KeybaseVerificationParcel createOperationInput() {
+ return new KeybaseVerificationParcel(mKeybaseProof, mKeybaseFingerprint);
+ }
- // show progress dialog
- handler.showProgressDialog(getActivity());
+ @Override
+ public void onCryptoOperationSuccess(KeybaseVerificationResult result) {
+
+ result.createNotify(getActivity()).show();
+
+ String proofUrl = result.mProofUrl;
+ String presenceUrl = result.mPresenceUrl;
+ String presenceLabel = result.mPresenceLabel;
+
+ Proof proof = mProof; // TODO: should ideally be contained in result
+
+ String proofLabel;
+ switch (proof.getType()) {
+ case Proof.PROOF_TYPE_TWITTER:
+ proofLabel = getString(R.string.keybase_twitter_proof);
+ break;
+ case Proof.PROOF_TYPE_DNS:
+ proofLabel = getString(R.string.keybase_dns_proof);
+ break;
+ case Proof.PROOF_TYPE_WEB_SITE:
+ proofLabel = getString(R.string.keybase_web_site_proof);
+ break;
+ case Proof.PROOF_TYPE_GITHUB:
+ proofLabel = getString(R.string.keybase_github_proof);
+ break;
+ case Proof.PROOF_TYPE_REDDIT:
+ proofLabel = getString(R.string.keybase_reddit_proof);
+ break;
+ default:
+ proofLabel = getString(R.string.keybase_a_post);
+ break;
+ }
+
+ SpannableStringBuilder ssb = new SpannableStringBuilder();
+
+ ssb.append(getString(R.string.keybase_proof_succeeded));
+ StyleSpan bold = new StyleSpan(Typeface.BOLD);
+ ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ssb.append("\n\n");
+ int length = ssb.length();
+ ssb.append(proofLabel);
+ if (proofUrl != null) {
+ URLSpan postLink = new URLSpan(proofUrl);
+ ssb.setSpan(postLink, length, length + proofLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ if (Proof.PROOF_TYPE_DNS == proof.getType()) {
+ ssb.append(" ").append(getString(R.string.keybase_for_the_domain)).append(" ");
+ } else {
+ ssb.append(" ").append(getString(R.string.keybase_fetched_from)).append(" ");
+ }
+ length = ssb.length();
+ URLSpan presenceLink = new URLSpan(presenceUrl);
+ ssb.append(presenceLabel);
+ ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned
+ .SPAN_EXCLUSIVE_EXCLUSIVE);
+ if (Proof.PROOF_TYPE_REDDIT == proof.getType()) {
+ ssb.append(", ").
+ append(getString(R.string.keybase_reddit_attribution)).
+ append(" “").append(proof.getHandle()).append("”, ");
+ }
+ ssb.append(" ").append(getString(R.string.keybase_contained_signature));
+
+ displaySpannableResult(ssb);
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(KeybaseVerificationResult result) {
+
+ result.createNotify(getActivity()).show();
+
+ SpannableStringBuilder ssb = new SpannableStringBuilder();
+
+ ssb.append(getString(R.string.keybase_proof_failure));
+ String msg = getString(result.getLog().getLast().mType.mMsgId);
+ if (msg == null) {
+ msg = getString(R.string.keybase_unknown_proof_failure);
+ }
+ StyleSpan bold = new StyleSpan(Typeface.BOLD);
+ ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ssb.append("\n\n").append(msg);
+
+ displaySpannableResult(ssb);
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
- // start service with intent
- getActivity().startService(intent);
+ private void displaySpannableResult(SpannableStringBuilder ssb) {
+ mProofVerifyHeader.setVisibility(View.VISIBLE);
+ mProofVerifyDetail.setVisibility(View.VISIBLE);
+ mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance());
+ mProofVerifyDetail.setText(ssb);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java
index 812874456..f980f297b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java
@@ -18,15 +18,12 @@
package org.sufficientlysecure.keychain.ui;
+
import java.nio.ByteBuffer;
import java.util.Arrays;
-import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -39,31 +36,38 @@ import android.widget.TextView;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
+import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-public class ViewKeyYubiKeyFragment extends Fragment
+
+public class ViewKeyYubiKeyFragment
+ extends QueueingCryptoOperationFragment<PromoteKeyringParcel, PromoteKeyResult>
implements LoaderCallbacks<Cursor> {
+ public static final String ARG_MASTER_KEY_ID = "master_key_id";
public static final String ARG_FINGERPRINT = "fingerprint";
public static final String ARG_USER_ID = "user_id";
public static final String ARG_CARD_AID = "aid";
+
private byte[][] mFingerprints;
private String mUserId;
private byte[] mCardAid;
private long mMasterKeyId;
+ private long[] mSubKeyIds;
+
private Button vButton;
private TextView vStatus;
- public static ViewKeyYubiKeyFragment newInstance(byte[] fingerprints, String userId, byte[] aid) {
+ public static ViewKeyYubiKeyFragment newInstance(long masterKeyId,
+ byte[] fingerprints, String userId, byte[] aid) {
ViewKeyYubiKeyFragment frag = new ViewKeyYubiKeyFragment();
Bundle args = new Bundle();
+ args.putLong(ARG_MASTER_KEY_ID, masterKeyId);
args.putByteArray(ARG_FINGERPRINT, fingerprints);
args.putString(ARG_USER_ID, userId);
args.putByteArray(ARG_CARD_AID, aid);
@@ -72,13 +76,17 @@ public class ViewKeyYubiKeyFragment extends Fragment
return frag;
}
+ public ViewKeyYubiKeyFragment() {
+ super(null);
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
ByteBuffer buf = ByteBuffer.wrap(args.getByteArray(ARG_FINGERPRINT));
- mFingerprints = new byte[buf.remaining()/40][];
+ mFingerprints = new byte[buf.remaining()/20][];
for (int i = 0; i < mFingerprints.length; i++) {
mFingerprints[i] = new byte[20];
buf.get(mFingerprints[i]);
@@ -86,7 +94,7 @@ public class ViewKeyYubiKeyFragment extends Fragment
mUserId = args.getString(ARG_USER_ID);
mCardAid = args.getByteArray(ARG_CARD_AID);
- mMasterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mFingerprints[0]);
+ mMasterKeyId = args.getLong(ARG_MASTER_KEY_ID);
getLoaderManager().initLoader(0, null, this);
@@ -105,7 +113,7 @@ public class ViewKeyYubiKeyFragment extends Fragment
if (!mUserId.isEmpty()) {
vUserId.setText(getString(R.string.yubikey_key_holder, mUserId));
} else {
- vUserId.setText(getString(R.string.yubikey_key_holder_unset));
+ vUserId.setText(getString(R.string.yubikey_key_holder_not_set));
}
vButton = (Button) view.findViewById(R.id.button_bind);
@@ -122,44 +130,15 @@ public class ViewKeyYubiKeyFragment extends Fragment
}
public void promoteToSecretKey() {
+ long[] subKeyIds = new long[mFingerprints.length];
+ for (int i = 0; i < subKeyIds.length; i++) {
+ subKeyIds[i] = KeyFormattingUtils.getKeyIdFromFingerprint(mFingerprints[i]);
+ }
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- PromoteKeyResult result =
- returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
-
- result.createNotify(getActivity()).show();
- }
-
- }
- };
-
- // Send all information needed to service to decrypt in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
-
- // fill values for this action
-
- intent.setAction(KeychainIntentService.ACTION_PROMOTE_KEYRING);
-
- Bundle data = new Bundle();
- data.putLong(KeychainIntentService.PROMOTE_MASTER_KEY_ID, mMasterKeyId);
- data.putByteArray(KeychainIntentService.PROMOTE_CARD_AID, mCardAid);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // start service with intent
- getActivity().startService(intent);
+ // mMasterKeyId and mCardAid are already set
+ mSubKeyIds = subKeyIds;
+ cryptoOperation();
}
public static final String[] PROJECTION = new String[]{
@@ -169,8 +148,8 @@ public class ViewKeyYubiKeyFragment extends Fragment
Keys.HAS_SECRET,
Keys.FINGERPRINT
};
- private static final int INDEX_KEY_ID = 1;
- private static final int INDEX_RANK = 2;
+ // private static final int INDEX_KEY_ID = 1;
+ // private static final int INDEX_RANK = 2;
private static final int INDEX_HAS_SECRET = 3;
private static final int INDEX_FINGERPRINT = 4;
@@ -216,7 +195,7 @@ public class ViewKeyYubiKeyFragment extends Fragment
}
- public Integer naiveIndexOf(byte[][] haystack, byte[] needle) {
+ static private Integer naiveIndexOf(byte[][] haystack, byte[] needle) {
for (int i = 0; i < haystack.length; i++) {
if (Arrays.equals(needle, haystack[i])) {
return i;
@@ -229,4 +208,15 @@ public class ViewKeyYubiKeyFragment extends Fragment
public void onLoaderReset(Loader<Cursor> loader) {
}
+
+ @Override
+ public PromoteKeyringParcel createOperationInput() {
+ return new PromoteKeyringParcel(mMasterKeyId, mCardAid, mSubKeyIds);
+ }
+
+ @Override
+ public void onQueuedOperationSuccess(PromoteKeyResult result) {
+ result.createNotify(getActivity()).show();
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
index db88de676..0be7e8f76 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
@@ -33,6 +33,7 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.operations.ImportOperation;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Highlighter;
@@ -92,8 +93,8 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
}
/** This method returns a list of all selected entries, with public keys sorted
- * before secret keys, see ImportExportOperation for specifics.
- * @see org.sufficientlysecure.keychain.operations.ImportExportOperation
+ * before secret keys, see ImportOperation for specifics.
+ * @see ImportOperation
*/
public ArrayList<ImportKeysListEntry> getSelectedEntries() {
ArrayList<ImportKeysListEntry> result = new ArrayList<>();
@@ -176,9 +177,9 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
}
if (entry.isRevoked()) {
- KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, State.REVOKED, R.color.bg_gray);
+ KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, State.REVOKED, R.color.key_flag_gray);
} else if (entry.isExpired()) {
- KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, State.EXPIRED, R.color.bg_gray);
+ KeyFormattingUtils.setStatusImage(getContext(), holder.status, null, State.EXPIRED, R.color.key_flag_gray);
}
if (entry.isRevoked() || entry.isExpired()) {
@@ -187,9 +188,9 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
// no more space for algorithm display
holder.algorithm.setVisibility(View.GONE);
- holder.mainUserId.setTextColor(getContext().getResources().getColor(R.color.bg_gray));
- holder.mainUserIdRest.setTextColor(getContext().getResources().getColor(R.color.bg_gray));
- holder.keyId.setTextColor(getContext().getResources().getColor(R.color.bg_gray));
+ holder.mainUserId.setTextColor(getContext().getResources().getColor(R.color.key_flag_gray));
+ holder.mainUserIdRest.setTextColor(getContext().getResources().getColor(R.color.key_flag_gray));
+ holder.keyId.setTextColor(getContext().getResources().getColor(R.color.key_flag_gray));
} else {
holder.status.setVisibility(View.GONE);
holder.algorithm.setVisibility(View.VISIBLE);
@@ -197,11 +198,11 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
if (entry.isSecretKey()) {
holder.mainUserId.setTextColor(Color.RED);
} else {
- holder.mainUserId.setTextColor(Color.BLACK);
+ holder.mainUserId.setTextColor(FormattingUtils.getColorFromAttr(mActivity, R.attr.colorText));
}
- holder.mainUserIdRest.setTextColor(Color.BLACK);
- holder.keyId.setTextColor(Color.BLACK);
+ holder.mainUserIdRest.setTextColor(FormattingUtils.getColorFromAttr(mActivity, R.attr.colorText));
+ holder.keyId.setTextColor(FormattingUtils.getColorFromAttr(mActivity, R.attr.colorText));
}
if (entry.getUserIds().size() == 1) {
@@ -241,9 +242,9 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
uidView.setPadding(0, 0, FormattingUtils.dpToPx(getContext(), 8), 0);
if (entry.isRevoked() || entry.isExpired()) {
- uidView.setTextColor(getContext().getResources().getColor(R.color.bg_gray));
+ uidView.setTextColor(getContext().getResources().getColor(R.color.key_flag_gray));
} else {
- uidView.setTextColor(getContext().getResources().getColor(R.color.black));
+ uidView.setTextColor(FormattingUtils.getColorFromAttr(getContext(), R.attr.colorText));
}
holder.userIdsList.addView(uidView);
@@ -257,9 +258,9 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
emailView.setText(highlighter.highlight(email));
if (entry.isRevoked() || entry.isExpired()) {
- emailView.setTextColor(getContext().getResources().getColor(R.color.bg_gray));
+ emailView.setTextColor(getContext().getResources().getColor(R.color.key_flag_gray));
} else {
- emailView.setTextColor(getContext().getResources().getColor(R.color.black));
+ emailView.setTextColor(FormattingUtils.getColorFromAttr(getContext(), R.attr.colorText));
}
holder.userIdsList.addView(emailView);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java
index 4781864dd..e77c92923 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
+import android.support.annotation.Nullable;
import android.support.v4.content.AsyncTaskLoader;
import org.sufficientlysecure.keychain.Constants;
@@ -26,8 +27,12 @@ import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.operations.results.GetKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
import java.util.ArrayList;
@@ -38,15 +43,27 @@ public class ImportKeysListCloudLoader
Preferences.CloudSearchPrefs mCloudPrefs;
String mServerQuery;
+ private ParcelableProxy mParcelableProxy;
private ArrayList<ImportKeysListEntry> mEntryList = new ArrayList<>();
private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper;
- public ImportKeysListCloudLoader(Context context, String serverQuery, Preferences.CloudSearchPrefs cloudPrefs) {
+ /**
+ * Searches a keyserver as specified in cloudPrefs, using an explicit proxy if passed
+ *
+ * @param serverQuery string to search on servers for. If is a fingerprint,
+ * will enforce fingerprint check
+ * @param cloudPrefs contains keyserver to search on, whether to search on the keyserver,
+ * and whether to search keybase.io
+ * @param parcelableProxy explicit proxy to use. If null, will retrieve from preferences
+ */
+ public ImportKeysListCloudLoader(Context context, String serverQuery, Preferences.CloudSearchPrefs cloudPrefs,
+ @Nullable ParcelableProxy parcelableProxy) {
super(context);
mContext = context;
mServerQuery = serverQuery;
mCloudPrefs = cloudPrefs;
+ mParcelableProxy = parcelableProxy;
}
@Override
@@ -95,9 +112,32 @@ public class ImportKeysListCloudLoader
* Query keyserver
*/
private void queryServer(boolean enforceFingerprint) {
+ ParcelableProxy parcelableProxy;
+
+ if (mParcelableProxy == null) {
+ // no explicit proxy specified, fetch from preferences
+ if (OrbotHelper.isOrbotInRequiredState(mContext)) {
+ parcelableProxy = Preferences.getPreferences(mContext).getProxyPrefs()
+ .parcelableProxy;
+ } else {
+ // user needs to enable/install orbot
+ mEntryList.clear();
+ GetKeyResult pendingResult = new GetKeyResult(null,
+ RequiredInputParcel.createOrbotRequiredOperation(),
+ new CryptoInputParcel());
+ mEntryListWrapper = new AsyncTaskResultWrapper<>(mEntryList, pendingResult);
+ return;
+ }
+ } else {
+ parcelableProxy = mParcelableProxy;
+ }
+
try {
- ArrayList<ImportKeysListEntry> searchResult
- = CloudSearch.search(mServerQuery, mCloudPrefs);
+ ArrayList<ImportKeysListEntry> searchResult = CloudSearch.search(
+ mServerQuery,
+ mCloudPrefs,
+ parcelableProxy.getProxy()
+ );
mEntryList.clear();
// add result to data
@@ -109,7 +149,7 @@ public class ImportKeysListCloudLoader
ImportKeysListEntry uniqueEntry = searchResult.get(0);
/*
* set fingerprint explicitly after query
- * to enforce a check when the key is imported by KeychainIntentService
+ * to enforce a check when the key is imported by KeychainService
*/
uniqueEntry.setFingerprintHex(fingerprint);
uniqueEntry.setSelected(true);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java
index 3dbae09b6..56d273c7c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java
@@ -18,6 +18,11 @@
package org.sufficientlysecure.keychain.ui.adapter;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
import java.util.Date;
import android.content.Context;
@@ -27,6 +32,7 @@ import android.support.v4.widget.CursorAdapter;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
@@ -38,6 +44,7 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.ui.util.Highlighter;
+import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
@@ -45,6 +52,7 @@ public class KeyAdapter extends CursorAdapter {
protected String mQuery;
protected LayoutInflater mInflater;
+ protected Context mContext;
// These are the rows that we will retrieve.
public static final String[] PROJECTION = new String[]{
@@ -56,7 +64,6 @@ public class KeyAdapter extends CursorAdapter {
KeyRings.VERIFIED,
KeyRings.HAS_ANY_SECRET,
KeyRings.HAS_DUPLICATE_USER_ID,
- KeyRings.HAS_ENCRYPT,
KeyRings.FINGERPRINT,
KeyRings.CREATION,
};
@@ -68,13 +75,13 @@ public class KeyAdapter extends CursorAdapter {
public static final int INDEX_VERIFIED = 5;
public static final int INDEX_HAS_ANY_SECRET = 6;
public static final int INDEX_HAS_DUPLICATE_USER_ID = 7;
- public static final int INDEX_HAS_ENCRYPT = 8;
- public static final int INDEX_FINGERPRINT = 9;
- public static final int INDEX_CREATION = 10;
+ public static final int INDEX_FINGERPRINT = 8;
+ public static final int INDEX_CREATION = 9;
public KeyAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
+ mContext = context;
mInflater = LayoutInflater.from(context);
}
@@ -83,6 +90,9 @@ public class KeyAdapter extends CursorAdapter {
}
public static class KeyItemViewHolder {
+ public View mView;
+ public View mLayoutDummy;
+ public View mLayoutData;
public Long mMasterKeyId;
public TextView mMainUserId;
public TextView mMainUserIdRest;
@@ -91,7 +101,12 @@ public class KeyAdapter extends CursorAdapter {
public View mSlinger;
public ImageButton mSlingerButton;
+ public KeyItem mDisplayedItem;
+
public KeyItemViewHolder(View view) {
+ mView = view;
+ mLayoutData = view.findViewById(R.id.key_list_item_data);
+ mLayoutDummy = view.findViewById(R.id.key_list_item_dummy);
mMainUserId = (TextView) view.findViewById(R.id.key_list_item_name);
mMainUserIdRest = (TextView) view.findViewById(R.id.key_list_item_email);
mStatus = (ImageView) view.findViewById(R.id.key_list_item_status_icon);
@@ -100,11 +115,15 @@ public class KeyAdapter extends CursorAdapter {
mCreationDate = (TextView) view.findViewById(R.id.key_list_item_creation);
}
- public void setData(Context context, Cursor cursor, Highlighter highlighter) {
+ public void setData(Context context, KeyItem item, Highlighter highlighter, boolean enabled) {
+
+ mLayoutData.setVisibility(View.VISIBLE);
+ mLayoutDummy.setVisibility(View.GONE);
+
+ mDisplayedItem = item;
{ // set name and stuff, common to both key types
- String userId = cursor.getString(INDEX_USER_ID);
- KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId);
+ KeyRing.UserId userIdSplit = item.mUserId;
if (userIdSplit.name != null) {
mMainUserId.setText(highlighter.highlight(userIdSplit.name));
} else {
@@ -118,43 +137,42 @@ public class KeyAdapter extends CursorAdapter {
}
}
+ // sort of a hack: if this item isn't enabled, we make it clickable
+ // to intercept its click events. either way, no listener!
+ mView.setClickable(!enabled);
+
{ // set edit button and status, specific by key type
- long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
- boolean isSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) != 0;
- boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
- boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0;
- boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0;
- boolean hasDuplicate = cursor.getInt(INDEX_HAS_DUPLICATE_USER_ID) != 0;
+ mMasterKeyId = item.mKeyId;
- mMasterKeyId = masterKeyId;
+ int textColor;
// Note: order is important!
- if (isRevoked) {
+ if (item.mIsRevoked) {
KeyFormattingUtils
- .setStatusImage(context, mStatus, null, State.REVOKED, R.color.bg_gray);
+ .setStatusImage(context, mStatus, null, State.REVOKED, R.color.key_flag_gray);
mStatus.setVisibility(View.VISIBLE);
mSlinger.setVisibility(View.GONE);
- mMainUserId.setTextColor(context.getResources().getColor(R.color.bg_gray));
- mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.bg_gray));
- } else if (isExpired) {
- KeyFormattingUtils.setStatusImage(context, mStatus, null, State.EXPIRED, R.color.bg_gray);
+ textColor = context.getResources().getColor(R.color.key_flag_gray);
+ } else if (item.mIsExpired) {
+ KeyFormattingUtils.setStatusImage(context, mStatus, null, State.EXPIRED, R.color.key_flag_gray);
mStatus.setVisibility(View.VISIBLE);
mSlinger.setVisibility(View.GONE);
- mMainUserId.setTextColor(context.getResources().getColor(R.color.bg_gray));
- mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.bg_gray));
- } else if (isSecret) {
+ textColor = context.getResources().getColor(R.color.key_flag_gray);
+ } else if (item.mIsSecret) {
mStatus.setVisibility(View.GONE);
if (mSlingerButton.hasOnClickListeners()) {
+ mSlingerButton.setColorFilter(
+ FormattingUtils.getColorFromAttr(context, R.attr.colorTertiaryText),
+ PorterDuff.Mode.SRC_IN);
mSlinger.setVisibility(View.VISIBLE);
} else {
mSlinger.setVisibility(View.GONE);
}
- mMainUserId.setTextColor(context.getResources().getColor(R.color.black));
- mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.black));
+ textColor = FormattingUtils.getColorFromAttr(context, R.attr.colorText);
} else {
// this is a public key - show if it's verified
- if (isVerified) {
+ if (item.mIsVerified) {
KeyFormattingUtils.setStatusImage(context, mStatus, State.VERIFIED);
mStatus.setVisibility(View.VISIBLE);
} else {
@@ -162,19 +180,26 @@ public class KeyAdapter extends CursorAdapter {
mStatus.setVisibility(View.VISIBLE);
}
mSlinger.setVisibility(View.GONE);
- mMainUserId.setTextColor(context.getResources().getColor(R.color.black));
- mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.black));
+ textColor = FormattingUtils.getColorFromAttr(context, R.attr.colorText);
}
- if (hasDuplicate) {
+ if (!enabled) {
+ textColor = context.getResources().getColor(R.color.key_flag_gray);
+ }
+
+ mMainUserId.setTextColor(textColor);
+ mMainUserIdRest.setTextColor(textColor);
+
+ if (item.mHasDuplicate) {
String dateTime = DateUtils.formatDateTime(context,
- cursor.getLong(INDEX_CREATION) * 1000,
+ item.mCreation.getTime(),
DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_SHOW_TIME
| DateUtils.FORMAT_SHOW_YEAR
| DateUtils.FORMAT_ABBREV_MONTH);
mCreationDate.setText(context.getString(R.string.label_key_created,
- dateTime));
+ dateTime));
+ mCreationDate.setTextColor(textColor);
mCreationDate.setVisibility(View.VISIBLE);
} else {
mCreationDate.setVisibility(View.GONE);
@@ -184,6 +209,24 @@ public class KeyAdapter extends CursorAdapter {
}
+ /** Shows the "you have no keys yet" dummy view, and sets an OnClickListener. */
+ public void setDummy(OnClickListener listener) {
+
+ // just reset everything to display the dummy layout
+ mLayoutDummy.setVisibility(View.VISIBLE);
+ mLayoutData.setVisibility(View.GONE);
+ mSlinger.setVisibility(View.GONE);
+ mStatus.setVisibility(View.GONE);
+ mView.setClickable(false);
+
+ mLayoutDummy.setOnClickListener(listener);
+
+ }
+
+ }
+
+ public boolean isEnabled(Cursor cursor) {
+ return true;
}
@Override
@@ -191,16 +234,17 @@ public class KeyAdapter extends CursorAdapter {
View view = mInflater.inflate(R.layout.key_list_item, parent, false);
KeyItemViewHolder holder = new KeyItemViewHolder(view);
view.setTag(holder);
- holder.mSlingerButton.setColorFilter(context.getResources().getColor(R.color.tertiary_text_light),
- PorterDuff.Mode.SRC_IN);
return view;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
Highlighter highlighter = new Highlighter(context, mQuery);
+ KeyItem item = new KeyItem(cursor);
+ boolean isEnabled = isEnabled(cursor);
+
KeyItemViewHolder h = (KeyItemViewHolder) view.getTag();
- h.setData(context, cursor, highlighter);
+ h.setData(context, item, highlighter, isEnabled);
}
public boolean isSecretAvailable(int id) {
@@ -230,14 +274,16 @@ public class KeyAdapter extends CursorAdapter {
@Override
public long getItemId(int position) {
+ Cursor cursor = getCursor();
// prevent a crash on rapid cursor changes
- if (getCursor().isClosed()) {
+ if (cursor != null && getCursor().isClosed()) {
return 0L;
}
return super.getItemId(position);
}
- public static class KeyItem {
+ // must be serializable for TokenCompleTextView state
+ public static class KeyItem implements Serializable {
public final String mUserIdFull;
public final KeyRing.UserId mUserId;
@@ -245,6 +291,7 @@ public class KeyAdapter extends CursorAdapter {
public final boolean mHasDuplicate;
public final Date mCreation;
public final String mFingerprint;
+ public final boolean mIsSecret, mIsRevoked, mIsExpired, mIsVerified;
private KeyItem(Cursor cursor) {
String userId = cursor.getString(INDEX_USER_ID);
@@ -255,6 +302,10 @@ public class KeyAdapter extends CursorAdapter {
mCreation = new Date(cursor.getLong(INDEX_CREATION) * 1000);
mFingerprint = KeyFormattingUtils.convertFingerprintToHex(
cursor.getBlob(INDEX_FINGERPRINT));
+ mIsSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) != 0;
+ mIsRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
+ mIsExpired = cursor.getInt(INDEX_IS_EXPIRED) > 0;
+ mIsVerified = cursor.getInt(INDEX_VERIFIED) > 0;
}
public KeyItem(CanonicalizedPublicKeyRing ring) {
@@ -267,6 +318,12 @@ public class KeyAdapter extends CursorAdapter {
mCreation = key.getCreationTime();
mFingerprint = KeyFormattingUtils.convertFingerprintToHex(
ring.getFingerprint());
+ mIsRevoked = key.isRevoked();
+ mIsExpired = key.isExpired();
+
+ // these two are actually "don't know"s
+ mIsSecret = false;
+ mIsVerified = false;
}
public String getReadableName() {
@@ -279,4 +336,11 @@ public class KeyAdapter extends CursorAdapter {
}
+ public static String[] getProjectionWith(String[] projection) {
+ List<String> list = new ArrayList<>();
+ list.addAll(Arrays.asList(PROJECTION));
+ list.addAll(Arrays.asList(projection));
+ return list.toArray(new String[list.size()]);
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeySelectableAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeySelectableAdapter.java
new file mode 100644
index 000000000..471a20411
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeySelectableAdapter.java
@@ -0,0 +1,87 @@
+package org.sufficientlysecure.keychain.ui.adapter;
+
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.support.v7.internal.widget.AdapterViewCompat;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.CheckBox;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.util.Log;
+
+
+public class KeySelectableAdapter extends KeyAdapter implements OnItemClickListener {
+
+ HashSet<Long> mSelectedItems = new HashSet<>();
+
+ public KeySelectableAdapter(Context context, Cursor c, int flags, Set<Long> initialChecked) {
+ super(context, c, flags);
+ if (initialChecked != null) {
+ mSelectedItems.addAll(initialChecked);
+ }
+ }
+
+ public static class KeySelectableItemViewHolder extends KeyItemViewHolder {
+
+ public CheckBox mCheckbox;
+
+ public KeySelectableItemViewHolder(View view) {
+ super(view);
+ mCheckbox = (CheckBox) view.findViewById(R.id.selected);
+ }
+
+ public void setCheckedState(boolean checked) {
+ mCheckbox.setChecked(checked);
+ }
+
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ View view = mInflater.inflate(R.layout.key_list_selectable_item, parent, false);
+ KeySelectableItemViewHolder holder = new KeySelectableItemViewHolder(view);
+ view.setTag(holder);
+ return view;
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ super.bindView(view, context, cursor);
+
+ KeySelectableItemViewHolder h = (KeySelectableItemViewHolder) view.getTag();
+ h.setCheckedState(mSelectedItems.contains(h.mDisplayedItem.mKeyId));
+
+ }
+
+ public void setCheckedStates(Set<Long> checked) {
+ mSelectedItems.clear();
+ mSelectedItems.addAll(checked);
+ notifyDataSetChanged();
+ }
+
+ public Set<Long> getSelectedMasterKeyIds() {
+ return Collections.unmodifiableSet(mSelectedItems);
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ Log.d(Constants.TAG, "clicked id: " + id);
+ long masterKeyId = getMasterKeyId(position);
+ if (mSelectedItems.contains(masterKeyId)) {
+ mSelectedItems.remove(masterKeyId);
+ } else {
+ mSelectedItems.add(masterKeyId);
+ }
+ notifyDataSetChanged();
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java
index 5218273a0..b91abf076 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java
@@ -40,20 +40,19 @@ public class MultiUserIdsAdapter extends CursorAdapter {
private LayoutInflater mInflater;
private final ArrayList<Boolean> mCheckStates;
- public MultiUserIdsAdapter(Context context, Cursor c, int flags) {
+ public MultiUserIdsAdapter(Context context, Cursor c, int flags, ArrayList<Boolean> preselectStates) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
- mCheckStates = new ArrayList<>();
+ mCheckStates = preselectStates == null ? new ArrayList<Boolean>() : preselectStates;
}
@Override
public Cursor swapCursor(Cursor newCursor) {
- mCheckStates.clear();
if (newCursor != null) {
int count = newCursor.getCount();
mCheckStates.ensureCapacity(count);
- // initialize to true (use case knowledge: we usually want to sign all uids)
- for (int i = 0; i < count; i++) {
+ // initialize new fields to true (use case knowledge: we usually want to sign all uids)
+ for (int i = mCheckStates.size(); i < count; i++) {
mCheckStates.add(true);
}
}
@@ -151,6 +150,10 @@ public class MultiUserIdsAdapter extends CursorAdapter {
}
+ public ArrayList<Boolean> getCheckStates() {
+ return mCheckStates;
+ }
+
public ArrayList<CertifyAction> getSelectedCertifyActions() {
LongSparseArray<CertifyAction> actions = new LongSparseArray<>();
for (int i = 0; i < mCheckStates.size(); i++) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
index 6bbf41a88..f01f25200 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
@@ -149,11 +149,11 @@ abstract public class SelectKeyCursorAdapter extends CursorAdapter {
boolean enabled;
if (cursor.getInt(mIndexIsRevoked) != 0) {
h.statusIcon.setVisibility(View.VISIBLE);
- KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, State.REVOKED, R.color.bg_gray);
+ KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, State.REVOKED, R.color.key_flag_gray);
enabled = false;
} else if (cursor.getInt(mIndexIsExpiry) != 0) {
h.statusIcon.setVisibility(View.VISIBLE);
- KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, State.EXPIRED, R.color.bg_gray);
+ KeyFormattingUtils.setStatusImage(mContext, h.statusIcon, null, State.EXPIRED, R.color.key_flag_gray);
enabled = false;
} else {
h.statusIcon.setVisibility(View.GONE);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
index 096dea51f..24f5f04a1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
@@ -116,6 +116,21 @@ public class SubkeysAdapter extends CursorAdapter {
}
}
+ public int getAlgorithm(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getInt(INDEX_ALGORITHM);
+ }
+
+ public int getKeySize(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getInt(INDEX_KEY_SIZE);
+ }
+
+ public SecretKeyType getSecretKeyType(int position) {
+ mCursor.moveToPosition(position);
+ return SecretKeyType.fromNum(mCursor.getInt(INDEX_HAS_SECRET));
+ }
+
@Override
public Cursor swapCursor(Cursor newCursor) {
hasAnySecret = false;
@@ -164,13 +179,23 @@ public class SubkeysAdapter extends CursorAdapter {
? mSaveKeyringParcel.getSubkeyChange(keyId)
: null;
- if (change != null && change.mDummyStrip) {
- algorithmStr.append(", ");
- final SpannableString boldStripped = new SpannableString(
- context.getString(R.string.key_stripped)
- );
- boldStripped.setSpan(new StyleSpan(Typeface.BOLD), 0, boldStripped.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- algorithmStr.append(boldStripped);
+ if (change != null && (change.mDummyStrip || change.mMoveKeyToCard)) {
+ if (change.mDummyStrip) {
+ algorithmStr.append(", ");
+ final SpannableString boldStripped = new SpannableString(
+ context.getString(R.string.key_stripped)
+ );
+ boldStripped.setSpan(new StyleSpan(Typeface.BOLD), 0, boldStripped.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ algorithmStr.append(boldStripped);
+ }
+ if (change.mMoveKeyToCard) {
+ algorithmStr.append(", ");
+ final SpannableString boldDivert = new SpannableString(
+ context.getString(R.string.key_divert)
+ );
+ boldDivert.setSpan(new StyleSpan(Typeface.BOLD), 0, boldDivert.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ algorithmStr.append(boldDivert);
+ }
} else {
switch (SecretKeyType.fromNum(cursor.getInt(INDEX_HAS_SECRET))) {
case GNU_DUMMY:
@@ -259,27 +284,27 @@ public class SubkeysAdapter extends CursorAdapter {
vStatus.setVisibility(View.VISIBLE);
vCertifyIcon.setColorFilter(
- mContext.getResources().getColor(R.color.bg_gray),
+ mContext.getResources().getColor(R.color.key_flag_gray),
PorterDuff.Mode.SRC_IN);
vSignIcon.setColorFilter(
- mContext.getResources().getColor(R.color.bg_gray),
+ mContext.getResources().getColor(R.color.key_flag_gray),
PorterDuff.Mode.SRC_IN);
vEncryptIcon.setColorFilter(
- mContext.getResources().getColor(R.color.bg_gray),
+ mContext.getResources().getColor(R.color.key_flag_gray),
PorterDuff.Mode.SRC_IN);
vAuthenticateIcon.setColorFilter(
- mContext.getResources().getColor(R.color.bg_gray),
+ mContext.getResources().getColor(R.color.key_flag_gray),
PorterDuff.Mode.SRC_IN);
if (isRevoked) {
vStatus.setImageResource(R.drawable.status_signature_revoked_cutout_24dp);
vStatus.setColorFilter(
- mContext.getResources().getColor(R.color.bg_gray),
+ mContext.getResources().getColor(R.color.key_flag_gray),
PorterDuff.Mode.SRC_IN);
} else if (isExpired) {
vStatus.setImageResource(R.drawable.status_signature_expired_cutout_24dp);
vStatus.setColorFilter(
- mContext.getResources().getColor(R.color.bg_gray),
+ mContext.getResources().getColor(R.color.key_flag_gray),
PorterDuff.Mode.SRC_IN);
}
} else {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
index c68c078ad..0f4312dad 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
@@ -128,7 +128,7 @@ public class UserIdsAdapter extends UserAttributesAdapter {
if (isRevoked) {
// set revocation icon (can this even be primary?)
- KeyFormattingUtils.setStatusImage(mContext, vVerified, null, State.REVOKED, R.color.bg_gray);
+ KeyFormattingUtils.setStatusImage(mContext, vVerified, null, State.REVOKED, R.color.key_flag_gray);
// disable revoked user ids
vName.setEnabled(false);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java
index 0e752881f..aa4e7d840 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui.base;
import android.app.Activity;
+import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
@@ -29,6 +30,8 @@ import android.view.ViewGroup;
import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService;
+import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
/**
* Setups Toolbar
@@ -36,15 +39,33 @@ import org.sufficientlysecure.keychain.R;
public abstract class BaseActivity extends AppCompatActivity {
protected Toolbar mToolbar;
protected View mStatusBar;
+ protected ThemeChanger mThemeChanger;
@Override
protected void onCreate(Bundle savedInstanceState) {
+ initTheme();
super.onCreate(savedInstanceState);
initLayout();
initToolbar();
}
- protected abstract void initLayout();
+ @Override
+ protected void onResume() {
+ super.onResume();
+ KeyserverSyncAdapterService.cancelUpdates(this);
+
+ if (mThemeChanger.changeTheme()) {
+ Intent intent = getIntent();
+ finish();
+ overridePendingTransition(0, 0);
+ startActivity(intent);
+ overridePendingTransition(0, 0);
+ }
+ }
+
+ protected void initLayout() {
+
+ }
protected void initToolbar() {
mToolbar = (Toolbar) findViewById(R.id.toolbar);
@@ -55,6 +76,15 @@ public abstract class BaseActivity extends AppCompatActivity {
mStatusBar = findViewById(R.id.status_bar);
}
+ /**
+ * Override if you want a different theme!
+ */
+ protected void initTheme() {
+ mThemeChanger = new ThemeChanger(this);
+ mThemeChanger.setThemes(R.style.Theme_Keychain_Light, R.style.Theme_Keychain_Dark);
+ mThemeChanger.changeTheme();
+ }
+
protected void setActionBarIcon(int iconRes) {
mToolbar.setNavigationIcon(iconRes);
}
@@ -85,9 +115,7 @@ public abstract class BaseActivity extends AppCompatActivity {
mToolbar.setNavigationOnClickListener(cancelOnClickListener);
}
- /**
- * Close button only
- */
+ /** Close button only */
protected void setFullScreenDialogClose(View.OnClickListener cancelOnClickListener, boolean white) {
if (white) {
setActionBarIcon(R.drawable.ic_close_white_24dp);
@@ -102,6 +130,17 @@ public abstract class BaseActivity extends AppCompatActivity {
setFullScreenDialogClose(cancelOnClickListener, true);
}
+ /** Close button only, with finish-action and given return status, white. */
+ protected void setFullScreenDialogClose(final int result, boolean white) {
+ setFullScreenDialogClose(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setResult(result);
+ finish();
+ }
+ }, white);
+ }
+
/**
* Inflate custom design with two buttons using drawables.
* This does not conform to the Material Design Guidelines, but we deviate here as this is used
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java
index 1d09b281f..972421abe 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java
@@ -1,6 +1,8 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2013-2014 Signe Rüsch
+ * Copyright (C) 2013-2014 Philipp Jakubeit
*
* 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,7 +21,9 @@
package org.sufficientlysecure.keychain.ui.base;
import java.io.IOException;
+import java.math.BigInteger;
import java.nio.ByteBuffer;
+import java.security.interfaces.RSAPrivateCrtKey;
import android.app.Activity;
import android.app.PendingIntent;
@@ -27,18 +31,25 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
+import android.nfc.TagLostException;
import android.nfc.tech.IsoDep;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.Toast;
import org.spongycastle.bcpg.HashAlgorithmTags;
+import org.spongycastle.util.Arrays;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService.KeyNotFoundException;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
@@ -54,21 +65,135 @@ import org.sufficientlysecure.keychain.util.Preferences;
public abstract class BaseNfcActivity extends BaseActivity {
- public static final int REQUEST_CODE_PASSPHRASE = 1;
+ public static final int REQUEST_CODE_PIN = 1;
+
+ public static final String EXTRA_TAG_HANDLING_ENABLED = "tag_handling_enabled";
protected Passphrase mPin;
+ protected Passphrase mAdminPin;
protected boolean mPw1ValidForMultipleSignatures;
protected boolean mPw1ValidatedForSignature;
protected boolean mPw1ValidatedForDecrypt; // Mode 82 does other things; consider renaming?
+ protected boolean mPw3Validated;
private NfcAdapter mNfcAdapter;
private IsoDep mIsoDep;
+ private boolean mTagHandlingEnabled;
private static final int TIMEOUT = 100000;
+ private byte[] mNfcFingerprints;
+ private String mNfcUserId;
+ private byte[] mNfcAid;
+
+ /**
+ * Override to change UI before NFC handling (UI thread)
+ */
+ protected void onNfcPreExecute() {
+ }
+
+ /**
+ * Override to implement NFC operations (background thread)
+ */
+ protected void doNfcInBackground() throws IOException {
+ mNfcFingerprints = nfcGetFingerprints();
+ mNfcUserId = nfcGetUserId();
+ mNfcAid = nfcGetAid();
+ }
+
+ /**
+ * Override to handle result of NFC operations (UI thread)
+ */
+ protected void onNfcPostExecute() throws IOException {
+
+ final long subKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints);
+
+ try {
+ CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId));
+ long masterKeyId = ring.getMasterKeyId();
+
+ Intent intent = new Intent(this, ViewKeyActivity.class);
+ intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints);
+ startActivity(intent);
+ } catch (PgpKeyNotFoundException e) {
+ Intent intent = new Intent(this, CreateKeyActivity.class);
+ intent.putExtra(CreateKeyActivity.EXTRA_NFC_AID, mNfcAid);
+ intent.putExtra(CreateKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
+ intent.putExtra(CreateKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints);
+ startActivity(intent);
+ }
+ }
+
+ /**
+ * Override to use something different than Notify (UI thread)
+ */
+ protected void onNfcError(String error) {
+ Notify.create(this, error, Style.WARN).show();
+ }
+
+ public void handleIntentInBackground(final Intent intent) {
+ // Actual NFC operations are executed in doInBackground to not block the UI thread
+ new AsyncTask<Void, Void, Exception>() {
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ onNfcPreExecute();
+ }
+
+ @Override
+ protected Exception doInBackground(Void... params) {
+ try {
+ handleTagDiscoveredIntent(intent);
+ } catch (CardException e) {
+ return e;
+ } catch (IOException e) {
+ return e;
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Exception exception) {
+ super.onPostExecute(exception);
+
+ if (exception != null) {
+ handleNfcError(exception);
+ return;
+ }
+
+ try {
+ onNfcPostExecute();
+ } catch (IOException e) {
+ handleNfcError(e);
+ }
+ }
+ }.execute();
+ }
+
+ protected void pauseTagHandling() {
+ mTagHandlingEnabled = false;
+ }
+
+ protected void resumeTagHandling() {
+ mTagHandlingEnabled = true;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ // Check whether we're recreating a previously destroyed instance
+ if (savedInstanceState != null) {
+ // Restore value of members from saved state
+ mTagHandlingEnabled = savedInstanceState.getBoolean(EXTRA_TAG_HANDLING_ENABLED);
+ } else {
+ mTagHandlingEnabled = true;
+ }
+
Intent intent = getIntent();
String action = intent.getAction();
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
@@ -77,25 +202,108 @@ public abstract class BaseNfcActivity extends BaseActivity {
}
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putBoolean(EXTRA_TAG_HANDLING_ENABLED, mTagHandlingEnabled);
+ }
+
/**
* This activity is started as a singleTop activity.
* All new NFC Intents which are delivered to this activity are handled here
*/
@Override
- public void onNewIntent(Intent intent) {
- if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
- try {
- handleNdefDiscoveredIntent(intent);
- } catch (IOException e) {
- handleNfcError(e);
- }
+ public void onNewIntent(final Intent intent) {
+ if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())
+ && mTagHandlingEnabled) {
+ handleIntentInBackground(intent);
}
}
- public void handleNfcError(IOException e) {
-
+ private void handleNfcError(Exception e) {
Log.e(Constants.TAG, "nfc error", e);
- Notify.create(this, getString(R.string.error_nfc, e.getMessage()), Style.WARN).show();
+
+ if (e instanceof TagLostException) {
+ onNfcError(getString(R.string.error_nfc_tag_lost));
+ return;
+ }
+
+ short status;
+ if (e instanceof CardException) {
+ status = ((CardException) e).getResponseCode();
+ } else {
+ status = -1;
+ }
+ // When entering a PIN, a status of 63CX indicates X attempts remaining.
+ if ((status & (short)0xFFF0) == 0x63C0) {
+ int tries = status & 0x000F;
+ onNfcError(getResources().getQuantityString(R.plurals.error_pin, tries, tries));
+ return;
+ }
+
+ // Otherwise, all status codes are fixed values.
+ switch (status) {
+ // These errors should not occur in everyday use; if they are returned, it means we
+ // made a mistake sending data to the card, or the card is misbehaving.
+ case 0x6A80: {
+ onNfcError(getString(R.string.error_nfc_bad_data));
+ break;
+ }
+ case 0x6883: {
+ onNfcError(getString(R.string.error_nfc_chaining_error));
+ break;
+ }
+ case 0x6B00: {
+ onNfcError(getString(R.string.error_nfc_header, "P1/P2"));
+ break;
+ }
+ case 0x6D00: {
+ onNfcError(getString(R.string.error_nfc_header, "INS"));
+ break;
+ }
+ case 0x6E00: {
+ onNfcError(getString(R.string.error_nfc_header, "CLA"));
+ break;
+ }
+ // These error conditions are more likely to be experienced by an end user.
+ case 0x6285: {
+ onNfcError(getString(R.string.error_nfc_terminated));
+ break;
+ }
+ case 0x6700: {
+ onNfcError(getString(R.string.error_nfc_wrong_length));
+ break;
+ }
+ case 0x6982: {
+ onNfcError(getString(R.string.error_nfc_security_not_satisfied));
+ break;
+ }
+ case 0x6983: {
+ onNfcError(getString(R.string.error_nfc_authentication_blocked));
+ break;
+ }
+ case 0x6985: {
+ onNfcError(getString(R.string.error_nfc_conditions_not_satisfied));
+ break;
+ }
+ // 6A88 is "Not Found" in the spec, but Yubikey also returns 6A83 for this in some cases.
+ case 0x6A88:
+ case 0x6A83: {
+ onNfcError(getString(R.string.error_nfc_data_not_found));
+ break;
+ }
+ // 6F00 is a JavaCard proprietary status code, SW_UNKNOWN, and usually represents an
+ // unhandled exception on the smart card.
+ case 0x6F00: {
+ onNfcError(getString(R.string.error_nfc_unknown));
+ break;
+ }
+ default: {
+ onNfcError(getString(R.string.error_nfc, e.getMessage()));
+ break;
+ }
+ }
}
@@ -129,16 +337,29 @@ public abstract class BaseNfcActivity extends BaseActivity {
protected void obtainYubiKeyPin(RequiredInputParcel requiredInput) {
+ // shortcut if we only use the default yubikey pin
Preferences prefs = Preferences.getPreferences(this);
if (prefs.useDefaultYubiKeyPin()) {
mPin = new Passphrase("123456");
return;
}
- Intent intent = new Intent(this, PassphraseDialogActivity.class);
- intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT,
- RequiredInputParcel.createRequiredPassphrase(requiredInput));
- startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
+ try {
+ Passphrase phrase = PassphraseCacheService.getCachedPassphrase(this,
+ requiredInput.getMasterKeyId(), requiredInput.getSubKeyId());
+ if (phrase != null) {
+ mPin = phrase;
+ return;
+ }
+
+ Intent intent = new Intent(this, PassphraseDialogActivity.class);
+ intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT,
+ RequiredInputParcel.createRequiredPassphrase(requiredInput));
+ startActivityForResult(intent, REQUEST_CODE_PIN);
+ } catch (KeyNotFoundException e) {
+ throw new AssertionError(
+ "tried to find passphrase for non-existing key. this is a programming error!");
+ }
}
@@ -149,7 +370,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
- case REQUEST_CODE_PASSPHRASE:
+ case REQUEST_CODE_PIN: {
if (resultCode != Activity.RESULT_OK) {
setResult(resultCode);
finish();
@@ -158,6 +379,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
CryptoInputParcel input = data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
mPin = input.getPassphrase();
break;
+ }
default:
super.onActivityResult(requestCode, resultCode, data);
@@ -169,7 +391,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
* This method is called by onNewIntent above upon discovery of an NFC tag.
* It handles initialization and login to the application, subsequently
* calls either nfcCalculateSignature() or nfcDecryptSessionKey(), then
- * finishes the activity with an appropiate result.
+ * finishes the activity with an appropriate result.
*
* On general communication, see also
* http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_annex-a.aspx
@@ -178,7 +400,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
* on ISO SmartCard Systems specification.
*
*/
- protected void handleNdefDiscoveredIntent(Intent intent) throws IOException {
+ protected void handleTagDiscoveredIntent(Intent intent) throws IOException {
Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
@@ -200,52 +422,23 @@ public abstract class BaseNfcActivity extends BaseActivity {
+ "06" // Lc (number of bytes)
+ "D27600012401" // Data (6 bytes)
+ "00"; // Le
- if ( ! nfcCommunicate(opening).endsWith(accepted)) { // activate connection
- throw new IOException("Initialization failed!");
+ String response = nfcCommunicate(opening); // activate connection
+ if ( ! response.endsWith(accepted) ) {
+ throw new CardException("Initialization failed!", parseCardStatus(response));
}
byte[] pwStatusBytes = nfcGetPwStatusBytes();
mPw1ValidForMultipleSignatures = (pwStatusBytes[0] == 1);
mPw1ValidatedForSignature = false;
mPw1ValidatedForDecrypt = false;
+ mPw3Validated = false;
- onNfcPerform();
-
- mIsoDep.close();
- mIsoDep = null;
+ doNfcInBackground();
}
- protected void onNfcPerform() throws IOException {
-
- final byte[] nfcFingerprints = nfcGetFingerprints();
- final String nfcUserId = nfcGetUserId();
- final byte[] nfcAid = nfcGetAid();
-
- final long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(nfcFingerprints);
-
- try {
- CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(masterKeyId);
- ring.getMasterKeyId();
-
- Intent intent = new Intent(
- BaseNfcActivity.this, ViewKeyActivity.class);
- intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid);
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId);
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints);
- startActivity(intent);
- finish();
- } catch (PgpKeyNotFoundException e) {
- Intent intent = new Intent(
- BaseNfcActivity.this, CreateKeyActivity.class);
- intent.putExtra(CreateKeyActivity.EXTRA_NFC_AID, nfcAid);
- intent.putExtra(CreateKeyActivity.EXTRA_NFC_USER_ID, nfcUserId);
- intent.putExtra(CreateKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints);
- startActivity(intent);
- finish();
- }
-
+ public boolean isNfcConnected() {
+ return mIsoDep.isConnected();
}
/** Return the key id from application specific data stored on tag, or null
@@ -415,7 +608,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
}
if ( ! "9000".equals(status)) {
- throw new IOException("Bad NFC response code: " + status);
+ throw new CardException("Bad NFC response code: " + status, parseCardStatus(response));
}
// Make sure the signature we received is actually the expected number of bytes long!
@@ -460,13 +653,21 @@ public abstract class BaseNfcActivity extends BaseActivity {
return Hex.decode(decryptedSessionKey);
}
- /** Verifies the user's PW1 with the appropriate mode.
+ /** Verifies the user's PW1 or PW3 with the appropriate mode.
*
- * @param mode This is 0x81 for signing, 0x82 for everything else
+ * @param mode For PW1, this is 0x81 for signing, 0x82 for everything else.
+ * For PW3 (Admin PIN), mode is 0x83.
*/
public void nfcVerifyPIN(int mode) throws IOException {
- if (mPin != null) {
- byte[] pin = new String(mPin.getCharArray()).getBytes();
+ if (mPin != null || mode == 0x83) {
+
+ byte[] pin;
+ if (mode == 0x83) {
+ pin = mAdminPin.toStringUnsafe().getBytes();
+ } else {
+ pin = mPin.toStringUnsafe().getBytes();
+ }
+
// SW1/2 0x9000 is the generic "ok" response, which we expect most of the time.
// See specification, page 51
String accepted = "9000";
@@ -479,19 +680,231 @@ public abstract class BaseNfcActivity extends BaseActivity {
+ String.format("%02x", mode) // P2
+ String.format("%02x", pin.length) // Lc
+ Hex.toHexString(pin);
- if (!nfcCommunicate(login).equals(accepted)) { // login
+ String response = nfcCommunicate(login); // login
+ if (!response.equals(accepted)) {
handlePinError();
- throw new IOException("Bad PIN!");
+ throw new CardException("Bad PIN!", parseCardStatus(response));
}
if (mode == 0x81) {
mPw1ValidatedForSignature = true;
} else if (mode == 0x82) {
mPw1ValidatedForDecrypt = true;
+ } else if (mode == 0x83) {
+ mPw3Validated = true;
}
}
}
+ /** Modifies the user's PW1 or PW3. Before sending, the new PIN will be validated for
+ * conformance to the card's requirements for key length.
+ *
+ * @param pw For PW1, this is 0x81. For PW3 (Admin PIN), mode is 0x83.
+ * @param newPin The new PW1 or PW3.
+ */
+ public void nfcModifyPIN(int pw, byte[] newPin) throws IOException {
+ final int MAX_PW1_LENGTH_INDEX = 1;
+ final int MAX_PW3_LENGTH_INDEX = 3;
+
+ byte[] pwStatusBytes = nfcGetPwStatusBytes();
+
+ if (pw == 0x81) {
+ if (newPin.length < 6 || newPin.length > pwStatusBytes[MAX_PW1_LENGTH_INDEX]) {
+ throw new IOException("Invalid PIN length");
+ }
+ } else if (pw == 0x83) {
+ if (newPin.length < 8 || newPin.length > pwStatusBytes[MAX_PW3_LENGTH_INDEX]) {
+ throw new IOException("Invalid PIN length");
+ }
+ } else {
+ throw new IOException("Invalid PW index for modify PIN operation");
+ }
+
+ byte[] pin;
+ if (pw == 0x83) {
+ pin = mAdminPin.toStringUnsafe().getBytes();
+ } else {
+ pin = mPin.toStringUnsafe().getBytes();
+ }
+
+ // Command APDU for CHANGE REFERENCE DATA command (page 32)
+ String changeReferenceDataApdu = "00" // CLA
+ + "24" // INS
+ + "00" // P1
+ + String.format("%02x", pw) // P2
+ + String.format("%02x", pin.length + newPin.length) // Lc
+ + getHex(pin)
+ + getHex(newPin);
+ String response = nfcCommunicate(changeReferenceDataApdu); // change PIN
+ if (!response.equals("9000")) {
+ handlePinError();
+ throw new CardException("Failed to change PIN", parseCardStatus(response));
+ }
+ }
+
+ /**
+ * Stores a data object on the card. Automatically validates the proper PIN for the operation.
+ * Supported for all data objects < 255 bytes in length. Only the cardholder certificate
+ * (0x7F21) can exceed this length.
+ *
+ * @param dataObject The data object to be stored.
+ * @param data The data to store in the object
+ */
+ public void nfcPutData(int dataObject, byte[] data) throws IOException {
+ if (data.length > 254) {
+ throw new IOException("Cannot PUT DATA with length > 254");
+ }
+ if (dataObject == 0x0101 || dataObject == 0x0103) {
+ if (!mPw1ValidatedForDecrypt) {
+ nfcVerifyPIN(0x82); // (Verify PW1 for non-signing operations)
+ }
+ } else if (!mPw3Validated) {
+ nfcVerifyPIN(0x83); // (Verify PW3)
+ }
+
+ String putDataApdu = "00" // CLA
+ + "DA" // INS
+ + String.format("%02x", (dataObject & 0xFF00) >> 8) // P1
+ + String.format("%02x", dataObject & 0xFF) // P2
+ + String.format("%02x", data.length) // Lc
+ + getHex(data);
+
+ String response = nfcCommunicate(putDataApdu); // put data
+ if (!response.equals("9000")) {
+ throw new CardException("Failed to put data.", parseCardStatus(response));
+ }
+ }
+
+ /**
+ * Puts a key on the card in the given slot.
+ *
+ * @param slot The slot on the card where the key should be stored:
+ * 0xB6: Signature Key
+ * 0xB8: Decipherment Key
+ * 0xA4: Authentication Key
+ */
+ public void nfcPutKey(int slot, CanonicalizedSecretKey secretKey, Passphrase passphrase)
+ throws IOException {
+ if (slot != 0xB6 && slot != 0xB8 && slot != 0xA4) {
+ throw new IOException("Invalid key slot");
+ }
+
+ RSAPrivateCrtKey crtSecretKey;
+ try {
+ secretKey.unlock(passphrase);
+ crtSecretKey = secretKey.getCrtSecretKey();
+ } catch (PgpGeneralException e) {
+ throw new IOException(e.getMessage());
+ }
+
+ // Shouldn't happen; the UI should block the user from getting an incompatible key this far.
+ if (crtSecretKey.getModulus().bitLength() > 2048) {
+ throw new IOException("Key too large to export to smart card.");
+ }
+
+ // Should happen only rarely; all GnuPG keys since 2006 use public exponent 65537.
+ if (!crtSecretKey.getPublicExponent().equals(new BigInteger("65537"))) {
+ throw new IOException("Invalid public exponent for smart card key.");
+ }
+
+ if (!mPw3Validated) {
+ nfcVerifyPIN(0x83); // (Verify PW3 with mode 83)
+ }
+
+ byte[] header= Hex.decode(
+ "4D82" + "03A2" // Extended header list 4D82, length of 930 bytes. (page 23)
+ + String.format("%02x", slot) + "00" // CRT to indicate targeted key, no length
+ + "7F48" + "15" // Private key template 0x7F48, length 21 (decimal, 0x15 hex)
+ + "9103" // Public modulus, length 3
+ + "928180" // Prime P, length 128
+ + "938180" // Prime Q, length 128
+ + "948180" // Coefficient (1/q mod p), length 128
+ + "958180" // Prime exponent P (d mod (p - 1)), length 128
+ + "968180" // Prime exponent Q (d mod (1 - 1)), length 128
+ + "97820100" // Modulus, length 256, last item in private key template
+ + "5F48" + "820383");// DO 5F48; 899 bytes of concatenated key data will follow
+ byte[] dataToSend = new byte[934];
+ byte[] currentKeyObject;
+ int offset = 0;
+
+ System.arraycopy(header, 0, dataToSend, offset, header.length);
+ offset += header.length;
+ currentKeyObject = crtSecretKey.getPublicExponent().toByteArray();
+ System.arraycopy(currentKeyObject, 0, dataToSend, offset, 3);
+ offset += 3;
+ // NOTE: For a 2048-bit key, these lengths are fixed. However, bigint includes a leading 0
+ // in the array to represent sign, so we take care to set the offset to 1 if necessary.
+ currentKeyObject = crtSecretKey.getPrimeP().toByteArray();
+ System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
+ Arrays.fill(currentKeyObject, (byte)0);
+ offset += 128;
+ currentKeyObject = crtSecretKey.getPrimeQ().toByteArray();
+ System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
+ Arrays.fill(currentKeyObject, (byte)0);
+ offset += 128;
+ currentKeyObject = crtSecretKey.getCrtCoefficient().toByteArray();
+ System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
+ Arrays.fill(currentKeyObject, (byte)0);
+ offset += 128;
+ currentKeyObject = crtSecretKey.getPrimeExponentP().toByteArray();
+ System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
+ Arrays.fill(currentKeyObject, (byte)0);
+ offset += 128;
+ currentKeyObject = crtSecretKey.getPrimeExponentQ().toByteArray();
+ System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128);
+ Arrays.fill(currentKeyObject, (byte)0);
+ offset += 128;
+ currentKeyObject = crtSecretKey.getModulus().toByteArray();
+ System.arraycopy(currentKeyObject, currentKeyObject.length - 256, dataToSend, offset, 256);
+
+ String putKeyCommand = "10DB3FFF";
+ String lastPutKeyCommand = "00DB3FFF";
+
+ // Now we're ready to communicate with the card.
+ offset = 0;
+ String response;
+ while(offset < dataToSend.length) {
+ int dataRemaining = dataToSend.length - offset;
+ if (dataRemaining > 254) {
+ response = nfcCommunicate(
+ putKeyCommand + "FE" + Hex.toHexString(dataToSend, offset, 254)
+ );
+ offset += 254;
+ } else {
+ int length = dataToSend.length - offset;
+ response = nfcCommunicate(
+ lastPutKeyCommand + String.format("%02x", length)
+ + Hex.toHexString(dataToSend, offset, length));
+ offset += length;
+ }
+
+ if (!response.endsWith("9000")) {
+ throw new CardException("Key export to card failed", parseCardStatus(response));
+ }
+ }
+
+ // Clear array with secret data before we return.
+ Arrays.fill(dataToSend, (byte) 0);
+ }
+
+ /**
+ * Parses out the status word from a JavaCard response string.
+ *
+ * @param response A hex string with the response from the card
+ * @return A short indicating the SW1/SW2, or 0 if a status could not be determined.
+ */
+ short parseCardStatus(String response) {
+ if (response.length() < 4) {
+ return 0; // invalid input
+ }
+
+ try {
+ return Short.parseShort(response.substring(response.length() - 4), 16);
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+
/**
* Prints a message to the screen
*
@@ -561,4 +974,18 @@ public abstract class BaseNfcActivity extends BaseActivity {
return new String(Hex.encode(raw));
}
+ public class CardException extends IOException {
+ private short mResponseCode;
+
+ public CardException(String detailMessage, short responseCode) {
+ super(detailMessage);
+ mResponseCode = responseCode;
+ }
+
+ public short getResponseCode() {
+ return mResponseCode;
+ }
+
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java
new file mode 100644
index 000000000..321af0df5
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java
@@ -0,0 +1,59 @@
+package org.sufficientlysecure.keychain.ui.base;
+
+
+import android.os.Bundle;
+import android.os.Parcelable;
+
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+
+
+public abstract class CachingCryptoOperationFragment <T extends Parcelable, S extends OperationResult>
+ extends QueueingCryptoOperationFragment<T, S> {
+
+ public static final String ARG_CACHED_ACTIONS = "cached_actions";
+
+ private T mCachedActionsParcel;
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putParcelable(ARG_CACHED_ACTIONS, mCachedActionsParcel);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState != null) {
+ mCachedActionsParcel = savedInstanceState.getParcelable(ARG_CACHED_ACTIONS);
+ }
+ }
+
+ @Override
+ public void onQueuedOperationSuccess(S result) {
+ mCachedActionsParcel = null;
+ }
+
+ @Override
+ public void onQueuedOperationError(S result) {
+ super.onQueuedOperationError(result);
+ mCachedActionsParcel = null;
+ }
+
+ @Override
+ public abstract T createOperationInput();
+
+ protected T getCachedActionsParcel() {
+ return mCachedActionsParcel;
+ }
+
+ protected void cacheActionsParcel(T cachedActionsParcel) {
+ mCachedActionsParcel = cachedActionsParcel;
+ }
+
+ public void onCryptoOperationCancelled() {
+ mCachedActionsParcel = null;
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java
index 232a39f86..de90d48fd 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java
@@ -18,114 +18,116 @@
package org.sufficientlysecure.keychain.ui.base;
-import android.app.Activity;
+
+import android.content.Context;
import android.content.Intent;
-import android.os.Bundle;
-import android.os.Message;
+import android.os.Parcelable;
+import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
-import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
+import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
-import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
-import org.sufficientlysecure.keychain.ui.NfcOperationActivity;
-import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
-/**
- * All fragments executing crypto operations need to extend this class.
+/** This is a base class for fragments which implement a cryptoOperation.
+ *
+ * Subclasses of this class can call the cryptoOperation method to execute an
+ * operation in KeychainService which takes a parcelable of type T as its input
+ * and returns an OperationResult of type S as a result.
+ *
+ * The input (of type T) is not given directly to the cryptoOperation method,
+ * but must be provided by the overriden createOperationInput method to be
+ * available upon request during execution of the cryptoOperation.
+ *
+ * After running cryptoOperation, one of the onCryptoOperation*() methods will
+ * be called, depending on the success status of the operation. The subclass
+ * must override at least onCryptoOperationSuccess to proceed after a
+ * successful operation.
+ *
+ * @see KeychainService
+ *
*/
-public abstract class CryptoOperationFragment extends Fragment {
-
- public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
- public static final int REQUEST_CODE_NFC = 0x00008002;
-
- private void initiateInputActivity(RequiredInputParcel requiredInput) {
-
- switch (requiredInput.mType) {
- case NFC_DECRYPT:
- case NFC_SIGN: {
- Intent intent = new Intent(getActivity(), NfcOperationActivity.class);
- intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
- startActivityForResult(intent, REQUEST_CODE_NFC);
- return;
- }
-
- case PASSPHRASE:
- case PASSPHRASE_SYMMETRIC: {
- Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class);
- intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, requiredInput);
- startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
- return;
- }
- }
+public abstract class CryptoOperationFragment<T extends Parcelable, S extends OperationResult>
+ extends Fragment implements CryptoOperationHelper.Callback<T, S> {
- throw new RuntimeException("Unhandled pending result!");
+ final private CryptoOperationHelper<T, S> mOperationHelper;
+
+ public CryptoOperationFragment() {
+ mOperationHelper = new CryptoOperationHelper<>(1, this, this, R.string.progress_processing);
}
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode == Activity.RESULT_CANCELED) {
- onCryptoOperationCancelled();
- return;
- }
+ public CryptoOperationFragment(Integer initialProgressMsg) {
+ mOperationHelper = new CryptoOperationHelper<>(1, this, this, initialProgressMsg);
+ }
- switch (requestCode) {
- case REQUEST_CODE_PASSPHRASE: {
- if (resultCode == Activity.RESULT_OK && data != null) {
- CryptoInputParcel cryptoInput =
- data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
- cryptoOperation(cryptoInput);
- return;
- }
- break;
- }
-
- case REQUEST_CODE_NFC: {
- if (resultCode == Activity.RESULT_OK && data != null) {
- CryptoInputParcel cryptoInput =
- data.getParcelableExtra(NfcOperationActivity.RESULT_DATA);
- cryptoOperation(cryptoInput);
- return;
- }
- break;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
- }
- }
+ public CryptoOperationFragment(int id, Integer initialProgressMsg) {
+ mOperationHelper = new CryptoOperationHelper<>(id, this, this, initialProgressMsg);
}
- public boolean handlePendingMessage(Message message) {
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ mOperationHelper.handleActivityResult(requestCode, resultCode, data);
+ }
- if (message.arg1 == ServiceProgressHandler.MessageStatus.OKAY.ordinal()) {
- Bundle data = message.getData();
+ /** Starts execution of the cryptographic operation.
+ *
+ * During this process, the createOperationInput() method will be called,
+ * this input will be handed to KeychainService, where it is executed in
+ * the appropriate *Operation class. If the result is a PendingInputResult,
+ * it is handled accordingly. Otherwise, it is returned in one of the
+ * onCryptoOperation* callbacks.
+ */
+ protected void cryptoOperation() {
+ mOperationHelper.cryptoOperation();
+ }
- OperationResult result = data.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null || !(result instanceof InputPendingResult)) {
- return false;
- }
+ protected void cryptoOperation(CryptoInputParcel cryptoInput) {
+ mOperationHelper.cryptoOperation(cryptoInput);
+ }
- InputPendingResult pendingResult = (InputPendingResult) result;
- if (pendingResult.isPending()) {
- RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel();
- initiateInputActivity(requiredInput);
- return true;
- }
- }
+ @Override @Nullable
+ /** Creates input for the crypto operation. Called internally after the
+ * crypto operation is started by a call to cryptoOperation(). Silently
+ * cancels operation if this method returns null. */
+ public abstract T createOperationInput();
+ /** Returns false, indicating that we did not handle progress ourselves. */
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
- protected void cryptoOperation() {
- cryptoOperation(new CryptoInputParcel());
+ public void setProgressMessageResource(int id) {
+ mOperationHelper.setProgressMessageResource(id);
+ }
+
+ @Override
+ /** Called when the cryptoOperation() was successful. No default behavior
+ * here, this should always be implemented by a subclass! */
+ abstract public void onCryptoOperationSuccess(S result);
+
+ @Override
+ abstract public void onCryptoOperationError(S result);
+
+ @Override
+ public void onCryptoOperationCancelled() {
}
- protected abstract void cryptoOperation(CryptoInputParcel cryptoInput);
+ public void hideKeyboard() {
+ if (getActivity() == null) {
+ return;
+ }
+ InputMethodManager inputManager = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ // check if no view has focus
+ View v = getActivity().getCurrentFocus();
+ if (v == null)
+ return;
- protected void onCryptoOperationCancelled() {
- // Nothing to do here, in most cases
+ inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java
new file mode 100644
index 000000000..6d7bf4cd0
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.base;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcelable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.service.KeychainService;
+import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
+import org.sufficientlysecure.keychain.ui.NfcOperationActivity;
+import org.sufficientlysecure.keychain.ui.OrbotRequiredDialogActivity;
+import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
+import org.sufficientlysecure.keychain.ui.RetryUploadDialogActivity;
+import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
+
+/**
+ * Designed to be integrated into activities or fragments used for CryptoOperations.
+ * Encapsulates the execution of a crypto operation and handling of input pending cases.s
+ *
+ * @param <T> The type of input parcel sent to the operation
+ * @param <S> The type of result returned by the operation
+ */
+public class CryptoOperationHelper<T extends Parcelable, S extends OperationResult> {
+
+ public interface Callback<T extends Parcelable, S extends OperationResult> {
+ T createOperationInput();
+
+ void onCryptoOperationSuccess(S result);
+
+ void onCryptoOperationCancelled();
+
+ void onCryptoOperationError(S result);
+
+ boolean onCryptoSetProgress(String msg, int progress, int max);
+ }
+
+ // request codes from CryptoOperationHelper are created essentially
+ // a static property, used to identify requestCodes meant for this
+ // particular helper. a request code looks as follows:
+ // (id << 9) + (1<<8) + REQUEST_CODE_X
+ // that is, starting from LSB, there are 8 bits request code, 1
+ // fixed bit set, then 7 bit helper-id code. the first two
+ // summands are stored in the mHelperId for easy operation.
+ private final int mHelperId;
+ // bitmask for helperId is everything except the least 8 bits
+ public static final int HELPER_ID_BITMASK = ~0xff;
+
+ public static final int REQUEST_CODE_PASSPHRASE = 1;
+ public static final int REQUEST_CODE_NFC = 2;
+ public static final int REQUEST_CODE_ENABLE_ORBOT = 3;
+ public static final int REQUEST_CODE_RETRY_UPLOAD = 4;
+
+ private Integer mProgressMessageResource;
+
+ private FragmentActivity mActivity;
+ private Fragment mFragment;
+ private Callback<T, S> mCallback;
+
+ private boolean mUseFragment; // short hand for mActivity == null
+
+ /**
+ * If OperationHelper is being integrated into an activity
+ */
+ public CryptoOperationHelper(int id, FragmentActivity activity, Callback<T, S> callback,
+ Integer progressMessageString) {
+ mHelperId = (id << 9) + (1<<8);
+ mActivity = activity;
+ mUseFragment = false;
+ mCallback = callback;
+ mProgressMessageResource = progressMessageString;
+ }
+
+ /**
+ * if OperationHelper is being integrated into a fragment
+ */
+ public CryptoOperationHelper(int id, Fragment fragment, Callback<T, S> callback, Integer progressMessageString) {
+ mHelperId = (id << 9) + (1<<8);
+ mFragment = fragment;
+ mUseFragment = true;
+ mProgressMessageResource = progressMessageString;
+ mCallback = callback;
+ }
+
+ public void setProgressMessageResource(int id) {
+ mProgressMessageResource = id;
+ }
+
+ private void initiateInputActivity(RequiredInputParcel requiredInput,
+ CryptoInputParcel cryptoInputParcel) {
+
+ Activity activity = mUseFragment ? mFragment.getActivity() : mActivity;
+
+ switch (requiredInput.mType) {
+ // always use CryptoOperationHelper.startActivityForResult!
+ case NFC_MOVE_KEY_TO_CARD:
+ case NFC_DECRYPT:
+ case NFC_SIGN: {
+ Intent intent = new Intent(activity, NfcOperationActivity.class);
+ intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
+ intent.putExtra(NfcOperationActivity.EXTRA_CRYPTO_INPUT, cryptoInputParcel);
+ startActivityForResult(intent, REQUEST_CODE_NFC);
+ return;
+ }
+
+ case PASSPHRASE:
+ case PASSPHRASE_SYMMETRIC: {
+ Intent intent = new Intent(activity, PassphraseDialogActivity.class);
+ intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, requiredInput);
+ intent.putExtra(PassphraseDialogActivity.EXTRA_CRYPTO_INPUT, cryptoInputParcel);
+ startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
+ return;
+ }
+
+ case ENABLE_ORBOT: {
+ Intent intent = new Intent(activity, OrbotRequiredDialogActivity.class);
+ intent.putExtra(OrbotRequiredDialogActivity.EXTRA_CRYPTO_INPUT, cryptoInputParcel);
+ startActivityForResult(intent, REQUEST_CODE_ENABLE_ORBOT);
+ return;
+ }
+
+ case UPLOAD_FAIL_RETRY: {
+ Intent intent = new Intent(activity, RetryUploadDialogActivity.class);
+ intent.putExtra(RetryUploadDialogActivity.EXTRA_CRYPTO_INPUT, cryptoInputParcel);
+ startActivityForResult(intent, REQUEST_CODE_RETRY_UPLOAD);
+ return;
+ }
+
+ default: {
+ throw new RuntimeException("Unhandled pending result!");
+ }
+ }
+ }
+
+ protected void startActivityForResult(Intent intent, int requestCode) {
+ if (mUseFragment) {
+ mFragment.startActivityForResult(intent, mHelperId + requestCode);
+ } else {
+ mActivity.startActivityForResult(intent, mHelperId + requestCode);
+ }
+ }
+
+ /**
+ * Attempts the result of an activity started by this helper. Returns true if requestCode is
+ * recognized, false otherwise.
+ * @return true if requestCode was recognized, false otherwise
+ */
+ public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d(Constants.TAG, "received activity result in OperationHelper");
+
+ if ((requestCode & HELPER_ID_BITMASK) != mHelperId) {
+ // this wasn't meant for us to handle
+ return false;
+ }
+ Log.d(Constants.TAG, "handling activity result in OperationHelper");
+ // filter out mHelperId from requestCode
+ requestCode ^= mHelperId;
+
+ if (resultCode == Activity.RESULT_CANCELED) {
+ mCallback.onCryptoOperationCancelled();
+ return true;
+ }
+
+ switch (requestCode) {
+ case REQUEST_CODE_PASSPHRASE: {
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ CryptoInputParcel cryptoInput =
+ data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
+ cryptoOperation(cryptoInput);
+ }
+ break;
+ }
+
+ case REQUEST_CODE_NFC: {
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ CryptoInputParcel cryptoInput =
+ data.getParcelableExtra(NfcOperationActivity.RESULT_CRYPTO_INPUT);
+ cryptoOperation(cryptoInput);
+ }
+ break;
+ }
+
+ case REQUEST_CODE_ENABLE_ORBOT: {
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ CryptoInputParcel cryptoInput =
+ data.getParcelableExtra(
+ OrbotRequiredDialogActivity.RESULT_CRYPTO_INPUT);
+ cryptoOperation(cryptoInput);
+ }
+ break;
+ }
+
+ case REQUEST_CODE_RETRY_UPLOAD: {
+ if (resultCode == Activity.RESULT_OK) {
+ CryptoInputParcel cryptoInput =
+ data.getParcelableExtra(
+ RetryUploadDialogActivity.RESULT_CRYPTO_INPUT);
+ cryptoOperation(cryptoInput);
+ }
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ protected void dismissProgress() {
+ FragmentManager fragmentManager =
+ mUseFragment ? mFragment.getFragmentManager() :
+ mActivity.getSupportFragmentManager();
+
+ if (fragmentManager == null) { // the fragment holding us has died
+ // fragmentManager was null when used with DialogFragments. (they close on click?)
+ return;
+ }
+
+ ProgressDialogFragment progressDialogFragment =
+ (ProgressDialogFragment) fragmentManager.findFragmentByTag(
+ ServiceProgressHandler.TAG_PROGRESS_DIALOG);
+
+ if (progressDialogFragment == null) {
+ return;
+ }
+
+ progressDialogFragment.dismissAllowingStateLoss();
+
+ }
+
+ public void cryptoOperation(final CryptoInputParcel cryptoInput) {
+
+ FragmentActivity activity = mUseFragment ? mFragment.getActivity() : mActivity;
+
+ T operationInput = mCallback.createOperationInput();
+ if (operationInput == null) {
+ return;
+ }
+
+ // Send all information needed to service to edit key in other thread
+ Intent intent = new Intent(activity, KeychainService.class);
+
+ intent.putExtra(KeychainService.EXTRA_OPERATION_INPUT, operationInput);
+ intent.putExtra(KeychainService.EXTRA_CRYPTO_INPUT, cryptoInput);
+
+ ServiceProgressHandler saveHandler = new ServiceProgressHandler(activity) {
+ @Override
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == MessageStatus.OKAY.ordinal()) {
+
+ // get returned data bundle
+ Bundle returnData = message.getData();
+ if (returnData == null) {
+ return;
+ }
+
+ final OperationResult result =
+ returnData.getParcelable(OperationResult.EXTRA_RESULT);
+
+ onHandleResult(result);
+ }
+ }
+
+ @Override
+ protected void onSetProgress(String msg, int progress, int max) {
+ // allow handling of progress in fragment, or delegate upwards
+ if (!mCallback.onCryptoSetProgress(msg, progress, max)) {
+ super.onSetProgress(msg, progress, max);
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
+
+ if (mProgressMessageResource != null) {
+ saveHandler.showProgressDialog(
+ activity.getString(mProgressMessageResource),
+ ProgressDialog.STYLE_HORIZONTAL, false);
+ }
+
+ activity.startService(intent);
+ }
+
+ public void cryptoOperation() {
+ cryptoOperation(new CryptoInputParcel());
+ }
+
+ public void onHandleResult(OperationResult result) {
+ Log.d(Constants.TAG, "Handling result in OperationHelper success: " + result.success());
+
+ if (result instanceof InputPendingResult) {
+ InputPendingResult pendingResult = (InputPendingResult) result;
+ if (pendingResult.isPending()) {
+ RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel();
+ initiateInputActivity(requiredInput, pendingResult.mCryptoInputParcel);
+ return;
+ }
+ }
+
+ dismissProgress();
+
+ try {
+ if (result.success()) {
+ // noinspection unchecked, because type erasure :(
+ mCallback.onCryptoOperationSuccess((S) result);
+ } else {
+ // noinspection unchecked, because type erasure :(
+ mCallback.onCryptoOperationError((S) result);
+ }
+ } catch (ClassCastException e) {
+ throw new AssertionError("bad return class ("
+ + result.getClass().getSimpleName() + "), this is a programming error!");
+ }
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/QueueingCryptoOperationFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/QueueingCryptoOperationFragment.java
new file mode 100644
index 000000000..65e0ce941
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/QueueingCryptoOperationFragment.java
@@ -0,0 +1,93 @@
+package org.sufficientlysecure.keychain.ui.base;
+
+
+import android.os.Bundle;
+import android.os.Parcelable;
+
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+
+
+/** CryptoOperationFragment which calls crypto operation results only while
+ * attached to Activity.
+ *
+ * This subclass of CryptoOperationFragment substitutes the onCryptoOperation*
+ * methods for onQueuedOperation* ones, which are ensured to be called while
+ * the fragment is attached to an Activity, possibly delaying the call until
+ * the Fragment is re-attached.
+ *
+ * TODO merge this functionality into CryptoOperationFragment?
+ *
+ * @see CryptoOperationFragment
+ */
+public abstract class QueueingCryptoOperationFragment<T extends Parcelable, S extends OperationResult>
+ extends CryptoOperationFragment<T,S> {
+
+ public static final String ARG_QUEUED_RESULT = "queued_result";
+ private S mQueuedResult;
+
+ public QueueingCryptoOperationFragment() {
+ super();
+ }
+
+ public QueueingCryptoOperationFragment(Integer initialProgressMsg) {
+ super(initialProgressMsg);
+ }
+
+ @Override
+ public void onViewStateRestored(Bundle savedInstanceState) {
+ super.onViewStateRestored(savedInstanceState);
+
+ if (mQueuedResult != null) {
+ try {
+ if (mQueuedResult.success()) {
+ onQueuedOperationSuccess(mQueuedResult);
+ } else {
+ onQueuedOperationError(mQueuedResult);
+ }
+ } finally {
+ mQueuedResult = null;
+ }
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putParcelable(ARG_QUEUED_RESULT, mQueuedResult);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState != null) {
+ mQueuedResult = savedInstanceState.getParcelable(ARG_QUEUED_RESULT);
+ }
+ }
+
+ public abstract void onQueuedOperationSuccess(S result);
+
+ public void onQueuedOperationError(S result) {
+ hideKeyboard();
+ result.createNotify(getActivity()).show();
+ }
+
+ @Override
+ final public void onCryptoOperationSuccess(S result) {
+ if (getActivity() == null) {
+ mQueuedResult = result;
+ return;
+ }
+ onQueuedOperationSuccess(result);
+ }
+
+ @Override
+ final public void onCryptoOperationError(S result) {
+ if (getActivity() == null) {
+ mQueuedResult = result;
+ return;
+ }
+ onQueuedOperationError(result);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddKeyserverDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java
index cbef5950f..47bc7dfda 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddKeyserverDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java
@@ -17,8 +17,14 @@
package org.sufficientlysecure.keychain.ui.dialog;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
import android.app.Activity;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
@@ -29,8 +35,8 @@ import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
+import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
-import android.test.suitebuilder.TestSuiteBuilder;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -42,23 +48,24 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
+import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.Request;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.TlsHelper;
+import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-
-import javax.net.ssl.HttpsURLConnection;
+import java.net.Proxy;
-public class AddKeyserverDialogFragment extends DialogFragment implements OnEditorActionListener {
- private static final String ARG_MESSENGER = "messenger";
- private static final String ARG_TITLE = "title";
+public class AddEditKeyserverDialogFragment extends DialogFragment implements OnEditorActionListener {
+ private static final String ARG_MESSENGER = "arg_messenger";
+ private static final String ARG_ACTION = "arg_dialog_action";
+ private static final String ARG_POSITION = "arg_position";
+ private static final String ARG_KEYSERVER = "arg_keyserver";
public static final int MESSAGE_OKAY = 1;
public static final int MESSAGE_VERIFICATION_FAILED = 2;
@@ -66,50 +73,54 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
public static final String MESSAGE_KEYSERVER = "new_keyserver";
public static final String MESSAGE_VERIFIED = "verified";
public static final String MESSAGE_FAILURE_REASON = "failure_reason";
+ public static final String MESSAGE_KEYSERVER_DELETED = "keyserver_deleted";
+ public static final String MESSAGE_DIALOG_ACTION = "message_dialog_action";
+ public static final String MESSAGE_EDIT_POSITION = "keyserver_edited_position";
private Messenger mMessenger;
+ private DialogAction mDialogAction;
+ private int mPosition;
+
private EditText mKeyserverEditText;
private CheckBox mVerifyKeyserverCheckBox;
- public static enum FailureReason {
+ public enum DialogAction {
+ ADD,
+ EDIT
+ }
+
+ public enum FailureReason {
INVALID_URL,
CONNECTION_FAILED
}
- ;
-
- /**
- * Creates new instance of this dialog fragment
- *
- * @param title title of dialog
- * @param messenger to communicate back after setting the passphrase
- * @return
- */
- public static AddKeyserverDialogFragment newInstance(Messenger messenger, int title) {
- AddKeyserverDialogFragment frag = new AddKeyserverDialogFragment();
+ public static AddEditKeyserverDialogFragment newInstance(Messenger messenger,
+ DialogAction action,
+ String keyserver,
+ int position) {
+ AddEditKeyserverDialogFragment frag = new AddEditKeyserverDialogFragment();
Bundle args = new Bundle();
- args.putInt(ARG_TITLE, title);
args.putParcelable(ARG_MESSENGER, messenger);
+ args.putSerializable(ARG_ACTION, action);
+ args.putString(ARG_KEYSERVER, keyserver);
+ args.putInt(ARG_POSITION, position);
frag.setArguments(args);
return frag;
}
- /**
- * Creates dialog
- */
+ @NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity();
- int title = getArguments().getInt(ARG_TITLE);
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
+ mDialogAction = (DialogAction) getArguments().getSerializable(ARG_ACTION);
+ mPosition = getArguments().getInt(ARG_POSITION);
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
- alert.setTitle(title);
-
LayoutInflater inflater = activity.getLayoutInflater();
View view = inflater.inflate(R.layout.add_keyserver_dialog, null);
alert.setView(view);
@@ -117,14 +128,26 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
mKeyserverEditText = (EditText) view.findViewById(R.id.keyserver_url_edit_text);
mVerifyKeyserverCheckBox = (CheckBox) view.findViewById(R.id.verify_keyserver_checkbox);
- // we don't want dialog to be dismissed on click, thereby requiring the hack seen below
- // and in onStart
+ switch (mDialogAction) {
+ case ADD: {
+ alert.setTitle(R.string.add_keyserver_dialog_title);
+ break;
+ }
+ case EDIT: {
+ alert.setTitle(R.string.edit_keyserver_dialog_title);
+ mKeyserverEditText.setText(getArguments().getString(ARG_KEYSERVER));
+ break;
+ }
+ }
+
+ // we don't want dialog to be dismissed on click for keyserver addition or edit,
+ // thereby requiring the hack seen below and in onStart
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
// we need to have an empty listener to prevent errors on some devices as mentioned
// at http://stackoverflow.com/q/13746412/3000919
- // actual listener set in onStart
+ // actual listener set in onStart for adding keyservers or editing them
}
});
@@ -136,6 +159,23 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
}
});
+ switch (mDialogAction) {
+ case EDIT: {
+ alert.setNeutralButton(R.string.label_keyserver_dialog_delete,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ deleteKeyserver(mPosition);
+ }
+ });
+ break;
+ }
+ case ADD: {
+ // do nothing
+ break;
+ }
+ }
+
// Hack to open keyboard.
// This is the only method that I found to work across all Android versions
// http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/
@@ -172,25 +212,63 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
positiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- String keyserverUrl = mKeyserverEditText.getText().toString();
+ // behaviour same for edit and add
+ final String keyserverUrl = mKeyserverEditText.getText().toString();
if (mVerifyKeyserverCheckBox.isChecked()) {
- verifyConnection(keyserverUrl);
+ final Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(getActivity())
+ .getProxyPrefs();
+ OrbotHelper.DialogActions dialogActions = new OrbotHelper.DialogActions() {
+ @Override
+ public void onOrbotStarted() {
+ verifyConnection(keyserverUrl,
+ proxyPrefs.parcelableProxy.getProxy());
+ }
+
+ @Override
+ public void onNeutralButton() {
+ verifyConnection(keyserverUrl, null);
+ }
+
+ @Override
+ public void onCancel() {
+ // do nothing
+ }
+ };
+
+ if (OrbotHelper.putOrbotInRequiredState(dialogActions, getActivity())) {
+ verifyConnection(keyserverUrl, proxyPrefs.parcelableProxy.getProxy());
+ }
} else {
dismiss();
// return unverified keyserver back to activity
- addKeyserver(keyserverUrl, false);
+ keyserverEdited(keyserverUrl, false);
}
}
});
}
}
- public void addKeyserver(String keyserver, boolean verified) {
+ public void keyserverEdited(String keyserver, boolean verified) {
dismiss();
Bundle data = new Bundle();
+ data.putSerializable(MESSAGE_DIALOG_ACTION, mDialogAction);
data.putString(MESSAGE_KEYSERVER, keyserver);
data.putBoolean(MESSAGE_VERIFIED, verified);
+ if (mDialogAction == DialogAction.EDIT) {
+ data.putInt(MESSAGE_EDIT_POSITION, mPosition);
+ }
+
+ sendMessageToHandler(MESSAGE_OKAY, data);
+ }
+
+ public void deleteKeyserver(int position) {
+ dismiss();
+ Bundle data = new Bundle();
+ data.putSerializable(MESSAGE_DIALOG_ACTION, DialogAction.EDIT);
+ data.putInt(MESSAGE_EDIT_POSITION, position);
+ data.putBoolean(MESSAGE_KEYSERVER_DELETED, true);
+
sendMessageToHandler(MESSAGE_OKAY, data);
}
@@ -201,7 +279,7 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
sendMessageToHandler(MESSAGE_VERIFICATION_FAILED, data);
}
- public void verifyConnection(String keyserver) {
+ public void verifyConnection(String keyserver, final Proxy proxy) {
new AsyncTask<String, Void, FailureReason>() {
ProgressDialog mProgressDialog;
@@ -225,19 +303,24 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
String scheme = keyserverUri.getScheme();
String schemeSpecificPart = keyserverUri.getSchemeSpecificPart();
String fragment = keyserverUri.getFragment();
- if (scheme == null) throw new MalformedURLException();
- if (scheme.equalsIgnoreCase("hkps")) scheme = "https";
- else if (scheme.equalsIgnoreCase("hkp")) scheme = "http";
+ if (scheme == null) {
+ throw new MalformedURLException();
+ }
+ if ("hkps".equalsIgnoreCase(scheme)) {
+ scheme = "https";
+ } else if ("hkp".equalsIgnoreCase(scheme)) {
+ scheme = "http";
+ }
URI newKeyserver = new URI(scheme, schemeSpecificPart, fragment);
Log.d("Converted URL", newKeyserver.toString());
- TlsHelper.openConnection(newKeyserver.toURL()).getInputStream();
+
+ OkHttpClient client = HkpKeyserver.getClient(newKeyserver.toURL(), proxy);
+ TlsHelper.pinCertificateIfNecessary(client, newKeyserver.toURL());
+ client.newCall(new Request.Builder().url(newKeyserver.toURL()).build()).execute();
} catch (TlsHelper.TlsHelperException e) {
reason = FailureReason.CONNECTION_FAILED;
- } catch (MalformedURLException e) {
- Log.w(Constants.TAG, "Invalid keyserver URL entered by user.");
- reason = FailureReason.INVALID_URL;
- } catch (URISyntaxException e) {
+ } catch (MalformedURLException | URISyntaxException e) {
Log.w(Constants.TAG, "Invalid keyserver URL entered by user.");
reason = FailureReason.INVALID_URL;
} catch (IOException e) {
@@ -251,7 +334,7 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
protected void onPostExecute(FailureReason failureReason) {
mProgressDialog.dismiss();
if (failureReason == null) {
- addKeyserver(mKeyserver, true);
+ keyserverEdited(mKeyserver, true);
} else {
verificationFailed(failureReason);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEmailDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEmailDialogFragment.java
index 5b91b9d37..c55c75b55 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEmailDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEmailDialogFragment.java
@@ -18,7 +18,7 @@
package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Activity;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java
index 0b1d39fc1..b51d081e1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java
@@ -18,12 +18,12 @@
package org.sufficientlysecure.keychain.ui.dialog;
import android.annotation.TargetApi;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
+import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java
index fe4ba0262..bc82feb70 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddUserIdDialogFragment.java
@@ -18,7 +18,7 @@
package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Activity;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AdvancedAppSettingsDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AdvancedAppSettingsDialogFragment.java
index d2fa37cf7..d96879119 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AdvancedAppSettingsDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AdvancedAppSettingsDialogFragment.java
@@ -27,7 +27,7 @@ import org.sufficientlysecure.keychain.R;
public class AdvancedAppSettingsDialogFragment extends DialogFragment {
private static final String ARG_PACKAGE_NAME = "package_name";
- private static final String ARG_SIGNATURE = "signature";
+ private static final String ARG_CERTIFICATE = "certificate";
/**
* Creates new instance of this fragment
@@ -36,7 +36,7 @@ public class AdvancedAppSettingsDialogFragment extends DialogFragment {
AdvancedAppSettingsDialogFragment frag = new AdvancedAppSettingsDialogFragment();
Bundle args = new Bundle();
args.putString(ARG_PACKAGE_NAME, packageName);
- args.putString(ARG_SIGNATURE, digest);
+ args.putString(ARG_CERTIFICATE, digest);
frag.setArguments(args);
return frag;
@@ -62,10 +62,10 @@ public class AdvancedAppSettingsDialogFragment extends DialogFragment {
});
String packageName = getArguments().getString(ARG_PACKAGE_NAME);
- String signature = getArguments().getString(ARG_SIGNATURE);
+ String certificate = getArguments().getString(ARG_CERTIFICATE);
alert.setMessage(getString(R.string.api_settings_package_name) + ": " + packageName + "\n\n"
- + getString(R.string.api_settings_package_signature) + ": " + signature);
+ + getString(R.string.api_settings_package_certificate) + ": " + certificate);
return alert.show();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java
index 794af5b15..840b95a3c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java
@@ -17,42 +17,15 @@
package org.sufficientlysecure.keychain.ui.dialog;
-import android.app.AlertDialog;
import android.content.Context;
-import android.view.View;
-import android.widget.TextView;
-
-import org.sufficientlysecure.keychain.R;
+import android.support.v7.app.AlertDialog;
/**
- * This class extends AlertDiaog.Builder, styling the header using emphasis color.
- * Note that this class is a huge hack, because dialog boxes aren't easily stylable.
- * Also, the dialog NEEDS to be called with show() directly, not create(), otherwise
- * the order of internal operations will lead to a crash!
+ * Uses support lib's dialog builder. We can apply a theme here later!
*/
public class CustomAlertDialogBuilder extends AlertDialog.Builder {
public CustomAlertDialogBuilder(Context context) {
super(context);
}
-
- @Override
- public AlertDialog show() {
- AlertDialog dialog = super.show();
-
- int dividerId = dialog.getContext().getResources().getIdentifier("android:id/titleDivider", null, null);
- View divider = dialog.findViewById(dividerId);
- if (divider != null) {
- divider.setBackgroundColor(dialog.getContext().getResources().getColor(R.color.header_text));
- }
-
- int textViewId = dialog.getContext().getResources().getIdentifier("android:id/alertTitle", null, null);
- TextView tv = (TextView) dialog.findViewById(textViewId);
- if (tv != null) {
- tv.setTextColor(dialog.getContext().getResources().getColor(R.color.header_text));
- }
-
- return dialog;
- }
-
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
deleted file mode 100644
index 581a96e52..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.ui.dialog;
-
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.support.v4.app.DialogFragment;
-import android.support.v4.app.FragmentActivity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.TextView;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.util.Log;
-
-import java.util.HashMap;
-
-public class DeleteKeyDialogFragment extends DialogFragment {
- private static final String ARG_MESSENGER = "messenger";
- private static final String ARG_DELETE_MASTER_KEY_IDS = "delete_master_key_ids";
-
- public static final int MESSAGE_OKAY = 1;
- public static final int MESSAGE_ERROR = 0;
-
- private TextView mMainMessage;
- private View mInflateView;
-
- /**
- * Creates new instance of this delete file dialog fragment
- */
- public static DeleteKeyDialogFragment newInstance(Messenger messenger, long[] masterKeyIds) {
- DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment();
- Bundle args = new Bundle();
-
- args.putParcelable(ARG_MESSENGER, messenger);
- args.putLongArray(ARG_DELETE_MASTER_KEY_IDS, masterKeyIds);
-
- frag.setArguments(args);
-
- return frag;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final FragmentActivity activity = getActivity();
- final Messenger messenger = getArguments().getParcelable(ARG_MESSENGER);
-
- final long[] masterKeyIds = getArguments().getLongArray(ARG_DELETE_MASTER_KEY_IDS);
-
- CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(activity);
-
- // Setup custom View to display in AlertDialog
- LayoutInflater inflater = activity.getLayoutInflater();
- mInflateView = inflater.inflate(R.layout.view_key_delete_fragment, null);
- builder.setView(mInflateView);
-
- mMainMessage = (TextView) mInflateView.findViewById(R.id.mainMessage);
-
- final boolean hasSecret;
-
- // If only a single key has been selected
- if (masterKeyIds.length == 1) {
- long masterKeyId = masterKeyIds[0];
-
- try {
- HashMap<String, Object> data = new ProviderHelper(activity).getUnifiedData(
- masterKeyId, new String[]{
- KeyRings.USER_ID,
- KeyRings.HAS_ANY_SECRET
- }, new int[]{
- ProviderHelper.FIELD_TYPE_STRING,
- ProviderHelper.FIELD_TYPE_INTEGER
- }
- );
- String name;
- KeyRing.UserId mainUserId = KeyRing.splitUserId((String) data.get(KeyRings.USER_ID));
- if (mainUserId.name != null) {
- name = mainUserId.name;
- } else {
- name = getString(R.string.user_id_no_name);
- }
- hasSecret = ((Long) data.get(KeyRings.HAS_ANY_SECRET)) == 1;
-
- if (hasSecret) {
- // show title only for secret key deletions,
- // see http://www.google.com/design/spec/components/dialogs.html#dialogs-behavior
- builder.setTitle(getString(R.string.title_delete_secret_key, name));
- mMainMessage.setText(getString(R.string.secret_key_deletion_confirmation, name));
- } else {
- mMainMessage.setText(getString(R.string.public_key_deletetion_confirmation, name));
- }
- } catch (ProviderHelper.NotFoundException e) {
- dismiss();
- return null;
- }
- } else {
- mMainMessage.setText(R.string.key_deletion_confirmation_multi);
- hasSecret = false;
- }
-
- builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
-
- // Send all information needed to service to import key in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
-
- intent.setAction(KeychainIntentService.ACTION_DELETE);
-
- // Message is received after importing is done in KeychainIntentService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_deleting),
- ProgressDialog.STYLE_HORIZONTAL,
- true,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- @Override
- public void handleMessage(Message message) {
- super.handleMessage(message);
- // handle messages by standard KeychainIntentServiceHandler first
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- try {
- Message msg = Message.obtain();
- msg.copyFrom(message);
- messenger.send(msg);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "messenger error", e);
- }
- }
- }
- };
-
- // fill values for this action
- Bundle data = new Bundle();
- data.putLongArray(KeychainIntentService.DELETE_KEY_LIST, masterKeyIds);
- data.putBoolean(KeychainIntentService.DELETE_IS_SECRET, hasSecret);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(getActivity());
-
- // start service with intent
- getActivity().startService(intent);
-
- dismiss();
- }
- });
- builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int id) {
- dismiss();
- }
- });
-
- return builder.show();
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java
index 9568312f5..b51648740 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java
@@ -35,6 +35,7 @@ public class EditSubkeyDialogFragment extends DialogFragment {
public static final int MESSAGE_CHANGE_EXPIRY = 1;
public static final int MESSAGE_REVOKE = 2;
public static final int MESSAGE_STRIP = 3;
+ public static final int MESSAGE_MOVE_KEY_TO_CARD = 4;
private Messenger mMessenger;
@@ -76,6 +77,9 @@ public class EditSubkeyDialogFragment extends DialogFragment {
case 2:
sendMessageToHandler(MESSAGE_STRIP, null);
break;
+ case 3:
+ sendMessageToHandler(MESSAGE_MOVE_KEY_TO_CARD, null);
+ break;
default:
break;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java
index 37e05a61d..c9fb990a2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java
@@ -24,6 +24,7 @@ import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
+import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
@@ -36,6 +37,8 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.Log;
import java.util.Calendar;
@@ -72,17 +75,19 @@ public class EditSubkeyExpiryDialogFragment extends DialogFragment {
/**
* Creates dialog
*/
+ @NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
- final Activity activity = getActivity();
+ Activity activity = getActivity();
+
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
long creation = getArguments().getLong(ARG_CREATION);
long expiry = getArguments().getLong(ARG_EXPIRY);
- Calendar creationCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- creationCal.setTime(new Date(creation * 1000));
- final Calendar expiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- expiryCal.setTime(new Date(expiry * 1000));
+ final Calendar creationCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ creationCal.setTimeInMillis(creation * 1000);
+ Calendar expiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ expiryCal.setTimeInMillis(expiry * 1000);
// date picker works with default time zone, we need to convert from UTC to default timezone
creationCal.setTimeZone(TimeZone.getDefault());
@@ -123,11 +128,8 @@ public class EditSubkeyExpiryDialogFragment extends DialogFragment {
noExpiry.setChecked(false);
expiryLayout.setVisibility(View.VISIBLE);
- // convert from UTC to time zone of device
- Calendar expiryCalTimeZone = (Calendar) expiryCal.clone();
- expiryCalTimeZone.setTimeZone(TimeZone.getDefault());
currentExpiry.setText(DateFormat.getDateFormat(
- getActivity()).format(expiryCalTimeZone.getTime()));
+ getActivity()).format(expiryCal.getTime()));
}
// date picker works based on default time zone
@@ -175,10 +177,13 @@ public class EditSubkeyExpiryDialogFragment extends DialogFragment {
selectedCal.setTimeZone(TimeZone.getTimeZone("UTC"));
long numDays = (selectedCal.getTimeInMillis() / 86400000)
- - (expiryCal.getTimeInMillis() / 86400000);
+ - (creationCal.getTimeInMillis() / 86400000);
if (numDays <= 0) {
- Log.e(Constants.TAG, "Should not happen! Expiry num of days <= 0!");
- throw new RuntimeException();
+ Activity activity = getActivity();
+ if (activity != null) {
+ Notify.create(activity, R.string.error_expiry_past, Style.ERROR).show();
+ }
+ return;
}
expiry = selectedCal.getTime().getTime() / 1000;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
index 63b6d26ac..84774ae5e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
@@ -119,14 +120,19 @@ public class FileDialogFragment extends DialogFragment {
mFilename = (EditText) view.findViewById(R.id.input);
mFilename.setText(mFile.getName());
mBrowse = (ImageButton) view.findViewById(R.id.btn_browse);
- mBrowse.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- // only .asc or .gpg files
- // setting it to text/plain prevents Cynaogenmod's file manager from selecting asc
- // or gpg types!
- FileHelper.openFile(FileDialogFragment.this, Uri.fromFile(mFile), "*/*", REQUEST_CODE);
- }
- });
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+ mBrowse.setVisibility(View.GONE);
+ } else {
+ mBrowse.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ // only .asc or .gpg files
+ // setting it to text/plain prevents Cynaogenmod's file manager from selecting asc
+ // or gpg types!
+ FileHelper.saveDocumentKitKat(
+ FileDialogFragment.this, "*/*", mFile.getName(), REQUEST_CODE);
+ }
+ });
+ }
mCheckBox = (CheckBox) view.findViewById(R.id.checkbox);
if (checkboxText == null) {
@@ -136,6 +142,7 @@ public class FileDialogFragment extends DialogFragment {
mCheckBox.setEnabled(true);
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setText(checkboxText);
+ mCheckBox.setChecked(true);
}
alert.setView(view);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/OrbotStartDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/OrbotStartDialogFragment.java
new file mode 100644
index 000000000..b06e05c30
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/OrbotStartDialogFragment.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.dialog;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+import android.widget.Button;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
+
+/**
+ * displays a dialog asking the user to enable Tor
+ */
+public class OrbotStartDialogFragment extends DialogFragment {
+ private static final String ARG_MESSENGER = "messenger";
+ private static final String ARG_TITLE = "title";
+ private static final String ARG_MESSAGE = "message";
+ private static final String ARG_MIDDLE_BUTTON = "middleButton";
+
+ private static final int ORBOT_REQUEST_CODE = 1;
+
+ public static final int MESSAGE_MIDDLE_BUTTON = 1;
+ public static final int MESSAGE_DIALOG_CANCELLED = 2; // for either cancel or enable pressed
+ public static final int MESSAGE_ORBOT_STARTED = 3; // for either cancel or enable pressed
+
+ public static OrbotStartDialogFragment newInstance(Messenger messenger, int title, int message, int middleButton) {
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_MESSENGER, messenger);
+ args.putInt(ARG_TITLE, title);
+ args.putInt(ARG_MESSAGE, message);
+ args.putInt(ARG_MIDDLE_BUTTON, middleButton);
+
+ OrbotStartDialogFragment fragment = new OrbotStartDialogFragment();
+ fragment.setArguments(args);
+
+ return fragment;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+
+ final Messenger messenger = getArguments().getParcelable(ARG_MESSENGER);
+ int title = getArguments().getInt(ARG_TITLE);
+ final int message = getArguments().getInt(ARG_MESSAGE);
+ int middleButton = getArguments().getInt(ARG_MIDDLE_BUTTON);
+ final Activity activity = getActivity();
+
+ ContextThemeWrapper theme = ThemeChanger.getDialogThemeWrapper(activity);
+
+ CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(theme);
+ builder.setTitle(title).setMessage(message);
+
+ builder.setNegativeButton(R.string.orbot_start_dialog_cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Message msg = Message.obtain();
+ msg.what = MESSAGE_DIALOG_CANCELLED;
+ try {
+ messenger.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);
+ }
+
+ }
+ });
+
+ builder.setNeutralButton(middleButton, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Message msg = new Message();
+ msg.what = MESSAGE_MIDDLE_BUTTON;
+ try {
+ messenger.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);
+ }
+ }
+ });
+
+ builder.setPositiveButton(R.string.orbot_start_dialog_start, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // actual onClick defined in onStart, this is just to make the button appear
+ }
+ });
+
+ return builder.show();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ //super.onStart() is where dialog.show() is actually called on the underlying dialog,
+ // so we have to do it after this point
+ AlertDialog d = (AlertDialog) getDialog();
+ if (d != null) {
+ Button positiveButton = d.getButton(Dialog.BUTTON_POSITIVE);
+ positiveButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ startActivityForResult(OrbotHelper.getShowOrbotStartIntent(),
+ ORBOT_REQUEST_CODE);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == ORBOT_REQUEST_CODE) {
+ // assume Orbot was started
+ final Messenger messenger = getArguments().getParcelable(ARG_MESSENGER);
+
+ Message msg = Message.obtain();
+ msg.what = MESSAGE_ORBOT_STARTED;
+ try {
+ messenger.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);
+ }
+ dismiss();
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PreferenceInstallDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PreferenceInstallDialogFragment.java
new file mode 100644
index 000000000..3f8bce28b
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PreferenceInstallDialogFragment.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.dialog;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import android.os.Messenger;
+import android.app.DialogFragment;
+
+import org.sufficientlysecure.keychain.ui.util.InstallDialogFragmentHelper;
+
+public class PreferenceInstallDialogFragment extends DialogFragment {
+
+ public static final int MESSAGE_MIDDLE_CLICKED = 1;
+ public static final int MESSAGE_DIALOG_DISMISSED = 2;
+
+ /**
+ * Creates a dialog which prompts the user to install an application. Consists of two default buttons ("Install"
+ * and "Cancel") and an optional third button. Callbacks are provided only for the middle button, if set.
+ *
+ * @param messenger required only for callback from middle button if it has been set
+ * @param title
+ * @param message content of dialog
+ * @param packageToInstall package name of application to install
+ * @param middleButton if not null, adds a third button to the app with a call back
+ * @return The dialog to display
+ */
+ public static PreferenceInstallDialogFragment newInstance(Messenger messenger, int title, int message,
+ String packageToInstall, int middleButton, boolean
+ useMiddleButton) {
+ PreferenceInstallDialogFragment frag = new PreferenceInstallDialogFragment();
+ Bundle args = new Bundle();
+
+ InstallDialogFragmentHelper.wrapIntoArgs(messenger, title, message, packageToInstall, middleButton,
+ useMiddleButton, args);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ /**
+ * To create a DialogFragment with only two buttons
+ *
+ * @param title
+ * @param message
+ * @param packageToInstall
+ * @return
+ */
+ public static PreferenceInstallDialogFragment newInstance(int title, int message,
+ String packageToInstall) {
+ return newInstance(null, title, message, packageToInstall, -1, false);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return InstallDialogFragmentHelper.getInstallDialogFromArgs(getArguments(), getActivity(),
+ MESSAGE_MIDDLE_CLICKED, MESSAGE_DIALOG_DISMISSED);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java
index af9d175ff..764291dd0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java
@@ -25,6 +25,7 @@ import android.content.DialogInterface.OnKeyListener;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
@@ -32,25 +33,19 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.service.CloudImportService;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.service.KeychainService;
+import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
+/**
+ * meant to be used
+ */
public class ProgressDialogFragment extends DialogFragment {
private static final String ARG_MESSAGE = "message";
private static final String ARG_STYLE = "style";
private static final String ARG_CANCELABLE = "cancelable";
private static final String ARG_SERVICE_TYPE = "service_class";
- public static enum ServiceType {
- KEYCHAIN_INTENT,
- CLOUD_IMPORT
- }
-
- ServiceType mServiceType;
-
boolean mCanCancel = false, mPreventCancel = false, mIsCancelled = false;
/**
@@ -58,41 +53,35 @@ public class ProgressDialogFragment extends DialogFragment {
* @param message the message to be displayed initially above the progress bar
* @param style the progress bar style, as defined in ProgressDialog (horizontal or spinner)
* @param cancelable should we let the user cancel this operation
- * @param serviceType which Service this progress dialog is meant for
* @return
*/
- public static ProgressDialogFragment newInstance(String message, int style, boolean cancelable,
- ServiceType serviceType) {
+ public static ProgressDialogFragment newInstance(String message, int style, boolean cancelable) {
ProgressDialogFragment frag = new ProgressDialogFragment();
Bundle args = new Bundle();
args.putString(ARG_MESSAGE, message);
args.putInt(ARG_STYLE, style);
args.putBoolean(ARG_CANCELABLE, cancelable);
- args.putSerializable(ARG_SERVICE_TYPE, serviceType);
frag.setArguments(args);
return frag;
}
- /** Updates progress of dialog */
public void setProgress(int messageId, int progress, int max) {
setProgress(getString(messageId), progress, max);
}
- /** Updates progress of dialog */
public void setProgress(int progress, int max) {
- if (mIsCancelled) {
+ ProgressDialog dialog = (ProgressDialog) getDialog();
+
+ if (mIsCancelled || dialog == null) {
return;
}
- ProgressDialog dialog = (ProgressDialog) getDialog();
-
dialog.setProgress(progress);
dialog.setMax(max);
}
- /** Updates progress of dialog */
public void setProgress(String message, int progress, int max) {
ProgressDialog dialog = (ProgressDialog) getDialog();
@@ -105,17 +94,12 @@ public class ProgressDialogFragment extends DialogFragment {
dialog.setMax(max);
}
- /**
- * Creates dialog
- */
+ @NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity();
- // if the progress dialog is displayed from the application class, design is missing
- // hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay
- ContextThemeWrapper context = new ContextThemeWrapper(activity,
- R.style.Theme_AppCompat_Light);
+ ContextThemeWrapper context = ThemeChanger.getDialogThemeWrapper(activity);
ProgressDialog dialog = new ProgressDialog(context);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
@@ -126,7 +110,6 @@ public class ProgressDialogFragment extends DialogFragment {
String message = getArguments().getString(ARG_MESSAGE);
int style = getArguments().getInt(ARG_STYLE);
mCanCancel = getArguments().getBoolean(ARG_CANCELABLE);
- mServiceType = (ServiceType) getArguments().getSerializable(ARG_SERVICE_TYPE);
dialog.setMessage(message);
dialog.setProgressStyle(style);
@@ -165,7 +148,12 @@ public class ProgressDialogFragment extends DialogFragment {
}
mPreventCancel = preventCancel;
- final Button negative = ((ProgressDialog) getDialog()).getButton(DialogInterface.BUTTON_NEGATIVE);
+ ProgressDialog dialog = (ProgressDialog) getDialog();
+ if (dialog == null) {
+ return;
+ }
+
+ final Button negative = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
negative.setEnabled(mIsCancelled && !preventCancel);
}
@@ -188,30 +176,24 @@ public class ProgressDialogFragment extends DialogFragment {
negative.setClickable(false);
negative.setTextColor(Color.GRAY);
+ // send a cancel message. note that this message will be handled by
+ // KeychainService.onStartCommand, which runs in this thread,
+ // not the service one, and will not queue up a command.
+ Intent serviceIntent = new Intent(getActivity(), KeychainService.class);
+
+ serviceIntent.setAction(KeychainService.ACTION_CANCEL);
+ getActivity().startService(serviceIntent);
+
// Set the progress bar accordingly
ProgressDialog dialog = (ProgressDialog) getDialog();
+ if (dialog == null) {
+ return;
+ }
+
dialog.setIndeterminate(true);
dialog.setMessage(getString(R.string.progress_cancelling));
- // send a cancel message. note that this message will be handled by
- // KeychainIntentService.onStartCommand, which runs in this thread,
- // not the service one, and will not queue up a command.
- Intent serviceIntent = null;
-
- switch (mServiceType) {
- case CLOUD_IMPORT:
- serviceIntent = new Intent(getActivity(), CloudImportService.class);
- break;
- case KEYCHAIN_INTENT:
- serviceIntent = new Intent(getActivity(), KeychainIntentService.class);
- break;
- default:
- //should never happen, unless we forget to include a ServiceType enum case
- Log.e(Constants.TAG, "Unrecognized ServiceType at ProgressDialogFragment");
- }
- serviceIntent.setAction(KeychainIntentService.ACTION_CANCEL);
- getActivity().startService(serviceIntent);
}
});
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
index 4eb253825..a990682f6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
@@ -18,7 +18,7 @@
package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Activity;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SupportInstallDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SupportInstallDialogFragment.java
new file mode 100644
index 000000000..82d1be4ed
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SupportInstallDialogFragment.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.dialog;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import android.os.Messenger;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+
+import org.sufficientlysecure.keychain.ui.util.InstallDialogFragmentHelper;
+
+public class SupportInstallDialogFragment extends DialogFragment {
+
+ public static final int MESSAGE_MIDDLE_CLICKED = 1;
+ public static final int MESSAGE_DIALOG_DISMISSED = 2;
+
+ /**
+ * Creates a dialog which prompts the user to install an application. Consists of two default buttons ("Install"
+ * and "Cancel") and an optional third button. Callbacks are provided only for the middle button, if set.
+ *
+ * @param messenger required only for callback from middle button if it has been set
+ * @param title xml resource for title of the install dialog
+ * @param message content of dialog
+ * @param packageToInstall package name of application to install
+ * @param middleButton if not null, adds a third button to the app with a call back
+ * @return The dialog to display
+ */
+ public static SupportInstallDialogFragment newInstance(Messenger messenger, int title, int message,
+ String packageToInstall, int middleButton, boolean
+ useMiddleButton) {
+ SupportInstallDialogFragment frag = new SupportInstallDialogFragment();
+ Bundle args = new Bundle();
+
+ InstallDialogFragmentHelper.wrapIntoArgs(messenger, title, message, packageToInstall, middleButton,
+ useMiddleButton, args);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ /**
+ * To create a DialogFragment with only two buttons
+ *
+ * @param title xml string resource for title of the dialog
+ * @param message xml string resource to display as dialog body
+ * @param packageToInstall name of package to install
+ * @return
+ */
+ public static SupportInstallDialogFragment newInstance(int title, int message,
+ String packageToInstall) {
+ return newInstance(null, title, message, packageToInstall, -1, false);
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+
+ return InstallDialogFragmentHelper.getInstallDialogFromArgs(getArguments(), getActivity(),
+ MESSAGE_MIDDLE_CLICKED, MESSAGE_DIALOG_DISMISSED);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java
index 8062428e3..c54d0c948 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java
@@ -107,10 +107,10 @@ public class LinkedIdCreateDnsStep1Fragment extends Fragment {
if (uri.length() > 0) {
if (checkUri(uri)) {
mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.uid_mail_ok, 0);
+ R.drawable.ic_stat_retyped_ok, 0);
} else {
mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.uid_mail_bad, 0);
+ R.drawable.ic_stat_retyped_bad, 0);
}
} else {
// remove drawable if email is empty
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java
index e0e6976ee..c9eca8882 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java
@@ -17,6 +17,14 @@
package org.sufficientlysecure.keychain.ui.linked;
+
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -26,17 +34,14 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.TextView;
+import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.linked.LinkedTokenResource;
import org.sufficientlysecure.keychain.linked.resources.DnsResource;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
-import java.io.FileNotFoundException;
-import java.io.PrintWriter;
-
public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment {
private static final int REQUEST_CODE_OUTPUT = 0x00007007;
@@ -117,7 +122,20 @@ public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment
}
private void proofToClipboard() {
- ClipboardReflection.copyToClipboard(getActivity(), mResourceString);
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
+ if (clipMan == null) {
+ Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR).show();
+ return;
+ }
+
+ ClipData clip = ClipData.newPlainText(Constants.CLIPBOARD_LABEL, mResourceString);
+ clipMan.setPrimaryClip(clip);
+
Notify.create(getActivity(), R.string.linked_text_clipboard, Notify.Style.OK).show();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java
index eedc7cdd9..24499a467 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java
@@ -1,12 +1,11 @@
package org.sufficientlysecure.keychain.ui.linked;
-import android.app.ProgressDialog;
-import android.content.Intent;
+
import android.graphics.PorterDuff;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
+import android.os.Parcelable;
+import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -16,20 +15,18 @@ import android.widget.TextView;
import android.widget.ViewAnimator;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.linked.LinkedAttribute;
+import org.sufficientlysecure.keychain.linked.LinkedTokenResource;
import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
-import org.sufficientlysecure.keychain.linked.LinkedTokenResource;
-import org.sufficientlysecure.keychain.linked.LinkedAttribute;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment.ServiceType;
import org.sufficientlysecure.keychain.ui.util.Notify;
+
public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragment {
protected LinkedIdWizard mLinkedIdWizard;
@@ -169,54 +166,31 @@ public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragmen
}
- protected void cryptoOperation(CryptoInputParcel cryptoInput) {
-
+ @Override
+ protected void cryptoOperation() {
if (mVerifiedResource == null) {
Notify.create(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR)
.show(LinkedIdCreateFinalFragment.this);
return;
}
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_saving),
- ProgressDialog.STYLE_HORIZONTAL,
- true, ServiceType.KEYCHAIN_INTENT) {
-
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- // handle pending messages
- if (handlePendingMessage(message)) {
- return;
- }
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
-
- // get returned data bundle
- Bundle returnData = message.getData();
- if (returnData == null) {
- return;
- }
- final OperationResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null) {
- return;
- }
-
- // if bad -> display here!
- if (!result.success()) {
- result.createNotify(getActivity()).show(LinkedIdCreateFinalFragment.this);
- return;
- }
+ super.cryptoOperation();
+ }
- getActivity().finish();
+ @Override
+ protected void cryptoOperation(CryptoInputParcel cryptoInput) {
+ if (mVerifiedResource == null) {
+ Notify.create(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR)
+ .show(LinkedIdCreateFinalFragment.this);
+ return;
+ }
- }
- }
- };
+ super.cryptoOperation(cryptoInput);
+ }
+ @Nullable
+ @Override
+ public Parcelable createOperationInput() {
SaveKeyringParcel skp =
new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint);
@@ -225,25 +199,22 @@ public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragmen
skp.mAddUserAttribute.add(ua);
- // Send all information needed to service to import key in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING);
-
- // fill values for this action
- Bundle data = new Bundle();
- data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput);
- data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, skp);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+ return skp;
+ }
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+ @Override
+ public void onCryptoOperationSuccess(OperationResult result) {
+ // if bad -> display here!
+ if (!result.success()) {
+ result.createNotify(getActivity()).show(LinkedIdCreateFinalFragment.this);
+ return;
+ }
- // show progress dialog
- saveHandler.showProgressDialog(getActivity());
+ getActivity().finish();
+ }
- // start service with intent
- getActivity().startService(intent);
+ @Override
+ public void onCryptoOperationError(OperationResult result) {
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java
index 7bc33c93b..8a05c35db 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java
@@ -103,10 +103,10 @@ public class LinkedIdCreateHttpsStep1Fragment extends Fragment {
if (uri.length() > 0) {
if (checkUri(uri)) {
mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.uid_mail_ok, 0);
+ R.drawable.ic_stat_retyped_ok, 0);
} else {
mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.uid_mail_bad, 0);
+ R.drawable.ic_stat_retyped_bad, 0);
}
} else {
// remove drawable if email is empty
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java
index 2af97fe36..22a201ba3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java
@@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.ui.linked;
import android.content.Intent;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.view.LayoutInflater;
@@ -135,13 +134,10 @@ public class LinkedIdCreateHttpsStep2Fragment extends LinkedIdCreateFinalFragmen
String targetName = "pgpkey.txt";
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
- File targetFile = new File(Constants.Path.APP_DIR, targetName);
- FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file),
- getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT);
- } else {
- FileHelper.saveDocument(this, "text/plain", targetName, REQUEST_CODE_OUTPUT);
- }
+ FileHelper.saveDocument(this,
+ targetName, Uri.fromFile(new File(Constants.Path.APP_DIR, targetName)),
+ "text/plain", R.string.title_decrypt_to_file, R.string.specify_file_to_decrypt_to,
+ REQUEST_CODE_OUTPUT);
}
private void saveFile(Uri uri) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java
index 82aaa51c4..7007fa50c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java
@@ -12,8 +12,8 @@ import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
+import android.os.Parcelable;
+import android.support.annotation.Nullable;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentManager.OnBackStackChangedListener;
import android.support.v4.app.LoaderManager;
@@ -31,20 +31,17 @@ import android.widget.ViewAnimator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Constants.key;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.operations.results.CertifyResult;
import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult;
import org.sufficientlysecure.keychain.linked.LinkedTokenResource;
import org.sufficientlysecure.keychain.linked.LinkedAttribute;
import org.sufficientlysecure.keychain.linked.LinkedResource;
import org.sufficientlysecure.keychain.linked.UriAttribute;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
@@ -58,7 +55,6 @@ import org.sufficientlysecure.keychain.ui.widget.CertListWidget;
import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
import org.sufficientlysecure.keychain.util.Log;
-
public class LinkedIdViewFragment extends CryptoOperationFragment implements
LoaderManager.LoaderCallbacks<Cursor>, OnBackStackChangedListener {
@@ -97,6 +93,12 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements
return frag;
}
+ public LinkedIdViewFragment() {
+ // IMPORTANT: the id must be unique in the ViewKeyActivity CryptoOperationHelper id namespace!
+ // no initial progress message -> we handle progress ourselves!
+ super(5, null);
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -499,7 +501,7 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements
}
// get the user's passphrase for this key (if required)
- mCertifyKeyId = mViewHolder.vKeySpinner.getSelectedItemId();
+ mCertifyKeyId = mViewHolder.vKeySpinner.getSelectedKeyId();
if (mCertifyKeyId == key.none || mCertifyKeyId == key.symmetric) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
SubtleAttentionSeeker.tintBackground(mViewHolder.vKeySpinnerContainer, 600).start();
@@ -509,11 +511,13 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements
return;
}
+ mViewHolder.setVerifyingState(mContext, VerifyState.CERTIFYING, false);
cryptoOperation();
+
}
@Override
- protected void onCryptoOperationCancelled() {
+ public void onCryptoOperationCancelled() {
super.onCryptoOperationCancelled();
// go back to 'verified ok'
@@ -521,68 +525,34 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements
}
+ @Nullable
@Override
- protected void cryptoOperation(CryptoInputParcel cryptoInput) {
-
- if (mIsSecret) {
- return;
- }
-
- mViewHolder.setVerifyingState(mContext, VerifyState.CERTIFYING, false);
-
- Bundle data = new Bundle();
- {
-
- long masterKeyId = KeyFormattingUtils.convertFingerprintToKeyId(mFingerprint);
- CertifyAction action = new CertifyAction(masterKeyId, null,
- Collections.singletonList(mLinkedId.toUserAttribute()));
-
- // fill values for this action
- CertifyActionsParcel parcel = new CertifyActionsParcel(mCertifyKeyId);
- parcel.mCertifyActions.addAll(Collections.singletonList(action));
- data.putParcelable(KeychainIntentService.CERTIFY_PARCEL, parcel);
-
- data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput);
+ public Parcelable createOperationInput() {
+ long masterKeyId = KeyFormattingUtils.convertFingerprintToKeyId(mFingerprint);
+ CertifyAction action = new CertifyAction(masterKeyId, null,
+ Collections.singletonList(mLinkedId.toUserAttribute()));
- /* if (mUploadKeyCheckbox.isChecked()) {
- String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
- data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, keyserver);
- } */
- }
-
- // Send all information needed to service to sign key in other thread
- Intent intent = new Intent(getActivity(), KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_CERTIFY_KEYRING);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after signing is done in KeychainIntentService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- // handle pending messages
- if (handlePendingMessage(message)) {
- return;
- }
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- Bundle data = message.getData();
- CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);
- result.createNotify(getActivity()).show();
- // no need to do anything else, we will get a loader refresh!
- }
+ // fill values for this action
+ CertifyActionsParcel parcel = new CertifyActionsParcel(mCertifyKeyId);
+ parcel.mCertifyActions.addAll(Collections.singletonList(action));
- }
- };
+ return parcel;
+ }
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+ @Override
+ public void onCryptoOperationSuccess(OperationResult result) {
+ result.createNotify(getActivity()).show();
+ // no need to do anything else, we will get a loader refresh!
+ }
- // start service with intent
- getActivity().startService(intent);
+ @Override
+ public void onCryptoOperationError(OperationResult result) {
+ result.createNotify(getActivity()).show();
+ }
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return true;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ExperimentalWordConfirm.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ExperimentalWordConfirm.java
new file mode 100644
index 000000000..43ccac24f
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ExperimentalWordConfirm.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2015 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.util;
+
+import android.content.Context;
+
+import org.spongycastle.util.Arrays;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.BitSet;
+
+public class ExperimentalWordConfirm {
+
+ public static String getWords(Context context, byte[] fingerprintBlob) {
+ ArrayList<String> words = new ArrayList<>();
+
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(
+ context.getAssets().open("word_confirm_list.txt"),
+ "UTF-8"
+ ));
+
+ String line = reader.readLine();
+ while (line != null) {
+ words.add(line);
+
+ line = reader.readLine();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("IOException", e);
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ String fingerprint = "";
+
+ // NOTE: 160 bit SHA-1 truncated to 156 bit
+ byte[] fingerprintBlobTruncated = Arrays.copyOfRange(fingerprintBlob, 0, 156 / 8);
+
+ // TODO: implement key stretching to minimize fp length?
+
+ // BitSet bits = BitSet.valueOf(fingerprintBlob); // min API 19 and little endian!
+ BitSet bits = bitSetToByteArray(fingerprintBlobTruncated);
+ Log.d(Constants.TAG, "bits: " + bits.toString());
+
+ final int CHUNK_SIZE = 13;
+ final int LAST_CHUNK_INDEX = fingerprintBlobTruncated.length * 8 / CHUNK_SIZE; // 12
+ Log.d(Constants.TAG, "LAST_CHUNK_INDEX: " + LAST_CHUNK_INDEX);
+
+ int from = 0;
+ int to = CHUNK_SIZE;
+ for (int i = 0; i < (LAST_CHUNK_INDEX + 1); i++) {
+ Log.d(Constants.TAG, "from: " + from + " to: " + to);
+
+ BitSet setIndex = bits.get(from, to);
+ int wordIndex = (int) bitSetToLong(setIndex);
+ // int wordIndex = (int) setIndex.toLongArray()[0]; // min API 19
+
+ fingerprint += words.get(wordIndex);
+
+ if (i != LAST_CHUNK_INDEX) {
+ // line break every 3 words
+ if (to % (CHUNK_SIZE * 3) == 0) {
+ fingerprint += "\n";
+ } else {
+ fingerprint += " ";
+ }
+ }
+
+ from = to;
+ to += CHUNK_SIZE;
+ }
+
+ return fingerprint;
+ }
+
+ /**
+ * Returns a BitSet containing the values in bytes.
+ * BIG ENDIAN!
+ */
+ private static BitSet bitSetToByteArray(byte[] bytes) {
+ int arrayLength = bytes.length * 8;
+ BitSet bits = new BitSet();
+
+ for (int i = 0; i < arrayLength; i++) {
+ if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0) {
+ bits.set(i);
+ }
+ }
+ return bits;
+ }
+
+ private static long bitSetToLong(BitSet bits) {
+ long value = 0L;
+ for (int i = 0; i < bits.length(); ++i) {
+ value += bits.get(i) ? (1L << i) : 0L;
+ }
+ return value;
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/FormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/FormattingUtils.java
index eb5c3df45..902a7ec56 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/FormattingUtils.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/FormattingUtils.java
@@ -18,9 +18,11 @@
package org.sufficientlysecure.keychain.ui.util;
import android.content.Context;
+import android.content.res.Resources.Theme;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.StrikethroughSpan;
+import android.util.TypedValue;
public class FormattingUtils {
@@ -32,4 +34,10 @@ public class FormattingUtils {
return (int) ((px / context.getResources().getDisplayMetrics().density) + 0.5f);
}
+ public static int getColorFromAttr(Context context, int attr) {
+ TypedValue typedValue = new TypedValue();
+ Theme theme = context.getTheme();
+ theme.resolveAttribute(attr, typedValue, true);
+ return typedValue.data;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Highlighter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Highlighter.java
index 69338aa3e..ac34d5526 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Highlighter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Highlighter.java
@@ -22,6 +22,7 @@ import android.text.Spannable;
import android.text.style.ForegroundColorSpan;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -44,9 +45,12 @@ public class Highlighter {
Pattern pattern = Pattern.compile("(?i)(" + mQuery.trim().replaceAll("\\s+", "|") + ")");
Matcher matcher = pattern.matcher(text);
+
+ int colorEmphasis = FormattingUtils.getColorFromAttr(mContext, R.attr.colorEmphasis);
+
while (matcher.find()) {
highlight.setSpan(
- new ForegroundColorSpan(mContext.getResources().getColor(R.color.emphasis)),
+ new ForegroundColorSpan(colorEmphasis),
matcher.start(),
matcher.end(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/InstallDialogFragmentHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/InstallDialogFragmentHelper.java
new file mode 100644
index 000000000..b2213ed10
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/InstallDialogFragmentHelper.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.util;
+
+import android.app.Activity;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.support.v7.app.AlertDialog;
+import android.view.ContextThemeWrapper;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
+import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class InstallDialogFragmentHelper {
+ private static final String ARG_MESSENGER = "messenger";
+ private static final String ARG_TITLE = "title";
+ private static final String ARG_MESSAGE = "message";
+ private static final String ARG_MIDDLE_BUTTON = "middleButton";
+ private static final String ARG_INSTALL_PATH = "installPath";
+ private static final String ARG_USE_MIDDLE_BUTTON = "useMiddleButton";
+
+ private static final String PLAY_STORE_PATH = "market://search?q=pname:";
+
+ public static void wrapIntoArgs(Messenger messenger, int title, int message, String packageToInstall,
+ int middleButton, boolean useMiddleButton, Bundle args) {
+ args.putParcelable(ARG_MESSENGER, messenger);
+
+ args.putInt(ARG_TITLE, title);
+ args.putInt(ARG_MESSAGE, message);
+ args.putInt(ARG_MIDDLE_BUTTON, middleButton);
+ args.putString(ARG_INSTALL_PATH, PLAY_STORE_PATH + packageToInstall);
+ args.putBoolean(ARG_USE_MIDDLE_BUTTON, useMiddleButton);
+ }
+
+ public static AlertDialog getInstallDialogFromArgs(Bundle args, final Activity activity,
+ final int messengerMiddleButtonClicked,
+ final int messengerDialogDimissed) {
+ final Messenger messenger = args.getParcelable(ARG_MESSENGER);
+
+ final int title = args.getInt(ARG_TITLE);
+ final int message = args.getInt(ARG_MESSAGE);
+ final int middleButton = args.getInt(ARG_MIDDLE_BUTTON);
+ final String installPath = args.getString(ARG_INSTALL_PATH);
+ final boolean useMiddleButton = args.getBoolean(ARG_USE_MIDDLE_BUTTON);
+
+ ContextThemeWrapper theme = ThemeChanger.getDialogThemeWrapper(activity);
+ CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(theme);
+
+ builder.setTitle(title).setMessage(message);
+
+ builder.setNegativeButton(R.string.orbot_install_dialog_cancel,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Message msg = Message.obtain();
+ msg.what = messengerDialogDimissed;
+ try {
+ messenger.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);
+ }
+ }
+ });
+
+ builder.setPositiveButton(R.string.orbot_install_dialog_install,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Uri uri = Uri.parse(installPath);
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ activity.startActivity(intent);
+
+ Message msg = Message.obtain();
+ msg.what = messengerDialogDimissed;
+ try {
+ messenger.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);
+ }
+ }
+ }
+ );
+
+ if (useMiddleButton) {
+ builder.setNeutralButton(middleButton,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Message msg = Message.obtain();
+ msg.what = messengerMiddleButtonClicked;
+ try {
+ messenger.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);
+ }
+ }
+ }
+ );
+ }
+
+ return builder.show();
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
index 3d98034d2..284c17e7a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java
@@ -24,9 +24,12 @@ import android.graphics.PorterDuff;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
+import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
+import org.openintents.openpgp.OpenPgpDecryptionResult;
+import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.asn1.ASN1ObjectIdentifier;
import org.spongycastle.asn1.nist.NISTNamedCurves;
import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
@@ -34,6 +37,8 @@ import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
import org.sufficientlysecure.keychain.util.Log;
@@ -218,14 +223,11 @@ public class KeyFormattingUtils {
public static String convertFingerprintToHex(byte[] fingerprint) {
// NOTE: Even though v3 keys are not imported we need to support both fingerprints for
// display/comparison before import
- // Also better cut of unneeded parts, e.g., for fingerprints returned from YubiKeys
- if (fingerprint.length < 20) {
- // v3 key fingerprint with 128 bit (MD5)
- return Hex.toHexString(fingerprint, 0, 16).toLowerCase(Locale.ENGLISH);
- } else {
- // v4 key fingerprint with 160 bit (SHA1)
- return Hex.toHexString(fingerprint, 0, 20).toLowerCase(Locale.ENGLISH);
+ if (fingerprint.length != 16 && fingerprint.length != 20) {
+ throw new IllegalArgumentException("No valid v3 or v4 fingerprint!");
}
+
+ return Hex.toHexString(fingerprint).toLowerCase(Locale.ENGLISH);
}
public static long getKeyIdFromFingerprint(byte[] fingerprint) {
@@ -383,7 +385,6 @@ public class KeyFormattingUtils {
/**
* Converts the given bytes to a unique RGB color using SHA1 algorithm
*
- * @param bytes
* @return an integer array containing 3 numeric color representations (Red, Green, Black)
* @throws java.security.NoSuchAlgorithmException
* @throws java.security.DigestException
@@ -401,7 +402,7 @@ public class KeyFormattingUtils {
public static final int DEFAULT_COLOR = -1;
- public static enum State {
+ public enum State {
REVOKED,
EXPIRED,
VERIFIED,
@@ -411,7 +412,8 @@ public class KeyFormattingUtils {
UNVERIFIED,
UNKNOWN_KEY,
INVALID,
- NOT_SIGNED
+ NOT_SIGNED,
+ INSECURE
}
public static void setStatusImage(Context context, ImageView statusIcon, State state) {
@@ -427,9 +429,196 @@ public class KeyFormattingUtils {
setStatusImage(context, statusIcon, statusText, state, color, false);
}
+ public interface StatusHolder {
+ ImageView getEncryptionStatusIcon();
+ TextView getEncryptionStatusText();
+
+ ImageView getSignatureStatusIcon();
+ TextView getSignatureStatusText();
+
+ View getSignatureLayout();
+ TextView getSignatureUserName();
+ TextView getSignatureUserEmail();
+ TextView getSignatureAction();
+
+ boolean hasEncrypt();
+
+ }
+
+ @SuppressWarnings("deprecation") // context.getDrawable is api lvl 21, need to use deprecated
+ public static void setStatus(Context context, StatusHolder holder, DecryptVerifyResult result) {
+
+ if (holder.hasEncrypt()) {
+ OpenPgpDecryptionResult decryptionResult = result.getDecryptionResult();
+
+ int encText, encIcon, encColor;
+
+ switch (decryptionResult.getResult()) {
+ case OpenPgpDecryptionResult.RESULT_ENCRYPTED: {
+ encText = R.string.decrypt_result_encrypted;
+ encIcon = R.drawable.status_lock_closed_24dp;
+ encColor = R.color.key_flag_green;
+ break;
+ }
+
+ case OpenPgpDecryptionResult.RESULT_INSECURE: {
+ encText = R.string.decrypt_result_insecure;
+ encIcon = R.drawable.status_signature_invalid_cutout_24dp;
+ encColor = R.color.key_flag_red;
+ break;
+ }
+
+ default:
+ case OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED: {
+ encText = R.string.decrypt_result_not_encrypted;
+ encIcon = R.drawable.status_lock_open_24dp;
+ encColor = R.color.key_flag_red;
+ break;
+ }
+ }
+
+ int encColorRes = context.getResources().getColor(encColor);
+ holder.getEncryptionStatusIcon().setColorFilter(encColorRes, PorterDuff.Mode.SRC_IN);
+ holder.getEncryptionStatusIcon().setImageDrawable(context.getResources().getDrawable(encIcon));
+ holder.getEncryptionStatusText().setText(encText);
+ holder.getEncryptionStatusText().setTextColor(encColorRes);
+ }
+
+ OpenPgpSignatureResult signatureResult = result.getSignatureResult();
+
+ int sigText, sigIcon, sigColor;
+ int sigActionText, sigActionIcon;
+
+ switch (signatureResult.getResult()) {
+
+ case OpenPgpSignatureResult.RESULT_NO_SIGNATURE: {
+ // no signature
+
+ sigText = R.string.decrypt_result_no_signature;
+ sigIcon = R.drawable.status_signature_invalid_cutout_24dp;
+ sigColor = R.color.key_flag_gray;
+
+ // won't be used, but makes compiler happy
+ sigActionText = 0;
+ sigActionIcon = 0;
+ break;
+ }
+
+ case OpenPgpSignatureResult.RESULT_VALID_CONFIRMED: {
+ sigText = R.string.decrypt_result_signature_certified;
+ sigIcon = R.drawable.status_signature_verified_cutout_24dp;
+ sigColor = R.color.key_flag_green;
+
+ sigActionText = R.string.decrypt_result_action_show;
+ sigActionIcon = R.drawable.ic_vpn_key_grey_24dp;
+ break;
+ }
+
+ case OpenPgpSignatureResult.RESULT_VALID_UNCONFIRMED: {
+ sigText = R.string.decrypt_result_signature_uncertified;
+ sigIcon = R.drawable.status_signature_unverified_cutout_24dp;
+ sigColor = R.color.key_flag_orange;
+
+ sigActionText = R.string.decrypt_result_action_show;
+ sigActionIcon = R.drawable.ic_vpn_key_grey_24dp;
+ break;
+ }
+
+ case OpenPgpSignatureResult.RESULT_INVALID_KEY_REVOKED: {
+ sigText = R.string.decrypt_result_signature_revoked_key;
+ sigIcon = R.drawable.status_signature_revoked_cutout_24dp;
+ sigColor = R.color.key_flag_red;
+
+ sigActionText = R.string.decrypt_result_action_show;
+ sigActionIcon = R.drawable.ic_vpn_key_grey_24dp;
+ break;
+ }
+
+ case OpenPgpSignatureResult.RESULT_INVALID_KEY_EXPIRED: {
+ sigText = R.string.decrypt_result_signature_expired_key;
+ sigIcon = R.drawable.status_signature_expired_cutout_24dp;
+ sigColor = R.color.key_flag_red;
+
+ sigActionText = R.string.decrypt_result_action_show;
+ sigActionIcon = R.drawable.ic_vpn_key_grey_24dp;
+ break;
+ }
+
+ case OpenPgpSignatureResult.RESULT_KEY_MISSING: {
+ sigText = R.string.decrypt_result_signature_missing_key;
+ sigIcon = R.drawable.status_signature_unknown_cutout_24dp;
+ sigColor = R.color.key_flag_red;
+
+ sigActionText = R.string.decrypt_result_action_Lookup;
+ sigActionIcon = R.drawable.ic_file_download_grey_24dp;
+ break;
+ }
+
+ case OpenPgpSignatureResult.RESULT_INVALID_INSECURE: {
+ sigText = R.string.decrypt_result_insecure_cryptography;
+ sigIcon = R.drawable.status_signature_invalid_cutout_24dp;
+ sigColor = R.color.key_flag_red;
+
+ sigActionText = R.string.decrypt_result_action_show;
+ sigActionIcon = R.drawable.ic_vpn_key_grey_24dp;
+ break;
+ }
+
+ default:
+ case OpenPgpSignatureResult.RESULT_INVALID_SIGNATURE: {
+ sigText = R.string.decrypt_result_invalid_signature;
+ sigIcon = R.drawable.status_signature_invalid_cutout_24dp;
+ sigColor = R.color.key_flag_red;
+
+ sigActionText = R.string.decrypt_result_action_show;
+ sigActionIcon = R.drawable.ic_vpn_key_grey_24dp;
+ break;
+ }
+
+ }
+
+ int sigColorRes = context.getResources().getColor(sigColor);
+ holder.getSignatureStatusIcon().setColorFilter(sigColorRes, PorterDuff.Mode.SRC_IN);
+ holder.getSignatureStatusIcon().setImageDrawable(context.getResources().getDrawable(sigIcon));
+ holder.getSignatureStatusText().setText(sigText);
+ holder.getSignatureStatusText().setTextColor(sigColorRes);
+
+ if (signatureResult.getResult() != OpenPgpSignatureResult.RESULT_NO_SIGNATURE) {
+ // has a signature, thus display layouts
+
+ holder.getSignatureLayout().setVisibility(View.VISIBLE);
+
+ holder.getSignatureAction().setText(sigActionText);
+ holder.getSignatureAction().setCompoundDrawablesWithIntrinsicBounds(
+ 0, 0, sigActionIcon, 0);
+
+ String userId = result.getSignatureResult().getPrimaryUserId();
+ KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId);
+ if (userIdSplit.name != null) {
+ holder.getSignatureUserName().setText(userIdSplit.name);
+ } else {
+ holder.getSignatureUserName().setText(R.string.user_id_no_name);
+ }
+ if (userIdSplit.email != null) {
+ holder.getSignatureUserEmail().setVisibility(View.VISIBLE);
+ holder.getSignatureUserEmail().setText(userIdSplit.email);
+ } else {
+ holder.getSignatureUserEmail().setVisibility(View.GONE);
+ }
+
+ } else {
+
+ holder.getSignatureLayout().setVisibility(View.GONE);
+
+ }
+
+
+ }
+
/**
* Sets status image based on constant
*/
+ @SuppressWarnings("deprecation") // context.getDrawable is api lvl 21
public static void setStatusImage(Context context, ImageView statusIcon, TextView statusText,
State state, int color, boolean big) {
switch (state) {
@@ -443,7 +632,7 @@ public class KeyFormattingUtils {
context.getResources().getDrawable(R.drawable.status_signature_verified_cutout_24dp));
}
if (color == KeyFormattingUtils.DEFAULT_COLOR) {
- color = R.color.android_green_light;
+ color = R.color.key_flag_green;
}
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
@@ -456,7 +645,7 @@ public class KeyFormattingUtils {
statusIcon.setImageDrawable(
context.getResources().getDrawable(R.drawable.status_lock_closed_24dp));
if (color == KeyFormattingUtils.DEFAULT_COLOR) {
- color = R.color.android_green_light;
+ color = R.color.key_flag_green;
}
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
@@ -475,7 +664,7 @@ public class KeyFormattingUtils {
context.getResources().getDrawable(R.drawable.status_signature_unverified_cutout_24dp));
}
if (color == KeyFormattingUtils.DEFAULT_COLOR) {
- color = R.color.android_orange_light;
+ color = R.color.key_flag_orange;
}
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
@@ -488,7 +677,7 @@ public class KeyFormattingUtils {
statusIcon.setImageDrawable(
context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout_24dp));
if (color == KeyFormattingUtils.DEFAULT_COLOR) {
- color = R.color.android_red_light;
+ color = R.color.key_flag_red;
}
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
@@ -507,7 +696,7 @@ public class KeyFormattingUtils {
context.getResources().getDrawable(R.drawable.status_signature_revoked_cutout_24dp));
}
if (color == KeyFormattingUtils.DEFAULT_COLOR) {
- color = R.color.android_red_light;
+ color = R.color.key_flag_red;
}
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
@@ -525,7 +714,25 @@ public class KeyFormattingUtils {
context.getResources().getDrawable(R.drawable.status_signature_expired_cutout_24dp));
}
if (color == KeyFormattingUtils.DEFAULT_COLOR) {
- color = R.color.android_red_light;
+ color = R.color.key_flag_red;
+ }
+ statusIcon.setColorFilter(context.getResources().getColor(color),
+ PorterDuff.Mode.SRC_IN);
+ if (statusText != null) {
+ statusText.setTextColor(context.getResources().getColor(color));
+ }
+ break;
+ }
+ case INSECURE: {
+ if (big) {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout_96dp));
+ } else {
+ statusIcon.setImageDrawable(
+ context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout_24dp));
+ }
+ if (color == KeyFormattingUtils.DEFAULT_COLOR) {
+ color = R.color.key_flag_red;
}
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
@@ -538,7 +745,7 @@ public class KeyFormattingUtils {
statusIcon.setImageDrawable(
context.getResources().getDrawable(R.drawable.status_lock_open_24dp));
if (color == KeyFormattingUtils.DEFAULT_COLOR) {
- color = R.color.android_red_light;
+ color = R.color.key_flag_red;
}
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
@@ -551,7 +758,7 @@ public class KeyFormattingUtils {
statusIcon.setImageDrawable(
context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout_24dp));
if (color == KeyFormattingUtils.DEFAULT_COLOR) {
- color = R.color.android_red_light;
+ color = R.color.key_flag_red;
}
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
@@ -564,7 +771,7 @@ public class KeyFormattingUtils {
statusIcon.setImageDrawable(
context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout_24dp));
if (color == KeyFormattingUtils.DEFAULT_COLOR) {
- color = R.color.android_red_light;
+ color = R.color.key_flag_red;
}
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
@@ -578,7 +785,7 @@ public class KeyFormattingUtils {
statusIcon.setImageDrawable(
context.getResources().getDrawable(R.drawable.status_signature_invalid_cutout_24dp));
if (color == KeyFormattingUtils.DEFAULT_COLOR) {
- color = R.color.bg_gray;
+ color = R.color.key_flag_gray;
}
statusIcon.setColorFilter(context.getResources().getColor(color),
PorterDuff.Mode.SRC_IN);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java
index 7e07ed818..7dfd56430 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java
@@ -38,33 +38,25 @@ import org.sufficientlysecure.keychain.util.FabContainer;
public class Notify {
public static enum Style {
- OK, WARN, ERROR;
+ OK (R.color.android_green_light), WARN(R.color.android_orange_light), ERROR(R.color.android_red_light);
- public void applyToBar(Snackbar bar) {
+ public final int mLineColor;
- switch (this) {
- case OK:
- // bar.actionColorResource(R.color.android_green_light);
- bar.lineColorResource(R.color.android_green_light);
- break;
- case WARN:
- // bar.textColorResource(R.color.android_orange_light);
- bar.lineColorResource(R.color.android_orange_light);
- break;
- case ERROR:
- // bar.textColorResource(R.color.android_red_light);
- bar.lineColorResource(R.color.android_red_light);
- break;
- }
+ Style(int color) {
+ mLineColor = color;
+ }
+ public void applyToBar(Snackbar bar) {
+ bar.lineColorResource(mLineColor);
}
}
public static final int LENGTH_INDEFINITE = 0;
public static final int LENGTH_LONG = 3500;
+ public static final int LENGTH_SHORT = 1500;
public static Showable create(final Activity activity, String text, int duration, Style style,
- final ActionListener actionListener, int actionResId) {
+ final ActionListener actionListener, Integer actionResId) {
final Snackbar snackbar = Snackbar.with(activity)
.type(SnackbarType.MULTI_LINE)
.text(text);
@@ -77,14 +69,16 @@ public class Notify {
style.applyToBar(snackbar);
+ if (actionResId != null) {
+ snackbar.actionLabel(actionResId);
+ }
if (actionListener != null) {
- snackbar.actionLabel(actionResId)
- .actionListener(new ActionClickListener() {
- @Override
- public void onActionClicked(Snackbar snackbar) {
- actionListener.onAction();
- }
- });
+ snackbar.actionListener(new ActionClickListener() {
+ @Override
+ public void onActionClicked(Snackbar snackbar) {
+ actionListener.onAction();
+ }
+ });
}
if (activity instanceof FabContainer) {
@@ -108,6 +102,13 @@ public class Notify {
}
@Override
+ public void show(Fragment fragment, boolean animate) {
+ snackbar.animation(animate);
+ snackbar.dismissOnActionClicked(animate);
+ show(fragment);
+ }
+
+ @Override
public void show(Fragment fragment) {
if (fragment != null) {
View view = fragment.getView();
@@ -134,7 +135,7 @@ public class Notify {
}
public static Showable create(Activity activity, String text, int duration, Style style) {
- return create(activity, text, duration, style, null, -1);
+ return create(activity, text, duration, style, null, null);
}
public static Showable create(Activity activity, String text, Style style) {
@@ -159,24 +160,26 @@ public class Notify {
/**
* Shows the notification on the bottom of the Activity.
*/
- public void show();
+ void show();
+
+ void show(Fragment fragment, boolean animate);
/**
* Shows the notification on the bottom of the Fragment.
*/
- public void show(Fragment fragment);
+ void show(Fragment fragment);
/**
* Shows the notification on the given ViewGroup.
* The viewGroup should be either a RelativeLayout or FrameLayout.
*/
- public void show(ViewGroup viewGroup);
+ void show(ViewGroup viewGroup);
}
public interface ActionListener {
- public void onAction();
+ void onAction();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java
index 5f71abdab..a6394a3fb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui.util;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.net.Uri;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
@@ -33,17 +34,24 @@ import org.sufficientlysecure.keychain.KeychainApplication;
import org.sufficientlysecure.keychain.util.Log;
import java.util.Hashtable;
+import java.util.Locale;
/**
* Copied from Bitcoin Wallet
*/
public class QrCodeUtils {
+ public static Bitmap getQRCodeBitmap(final Uri uri, final int size) {
+ // for URIs we want alphanumeric encoding to save space, thus make everything upper case!
+ // zxing will then select Mode.ALPHANUMERIC internally
+ return getQRCodeBitmap(uri.toString().toUpperCase(Locale.ENGLISH), size);
+ }
+
/**
* Generate Bitmap with QR Code based on input.
* @return QR Code as Bitmap
*/
- public static Bitmap getQRCodeBitmap(final String input, final int size) {
+ private static Bitmap getQRCodeBitmap(final String input, final int size) {
try {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ThemeChanger.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ThemeChanger.java
new file mode 100644
index 000000000..375483d89
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ThemeChanger.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 Thialfihar <thi@thialfihar.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.util;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.util.Preferences;
+
+public class ThemeChanger {
+ private Activity mContext;
+ private Preferences mPreferences;
+ private String mCurrentTheme = null;
+
+ private int mLightResId;
+ private int mDarkResId;
+
+ static public ContextThemeWrapper getDialogThemeWrapper(Context context) {
+ Preferences preferences = Preferences.getPreferences(context);
+
+ // if the dialog is displayed from the application class, design is missing.
+ // hack to get holo design (which is not automatically applied due to activity's
+ // Theme.NoDisplay)
+ if (Constants.Pref.Theme.DARK.equals(preferences.getTheme())) {
+ return new ContextThemeWrapper(context, R.style.Theme_Keychain_Dark);
+ } else {
+ return new ContextThemeWrapper(context, R.style.Theme_Keychain_Light);
+ }
+ }
+
+ public void setThemes(int lightResId, int darkResId) {
+ mLightResId = lightResId;
+ mDarkResId = darkResId;
+ }
+
+ public ThemeChanger(Activity context) {
+ mContext = context;
+ mPreferences = Preferences.getPreferences(mContext);
+ }
+
+ /**
+ * Apply the theme set in preferences if it isn't equal to mCurrentTheme
+ * anymore or mCurrentTheme hasn't been set yet.
+ * If a new theme is applied in this method, then return true, so
+ * the caller can re-create the activity, if need be.
+ */
+ public boolean changeTheme() {
+ String newTheme = mPreferences.getTheme();
+ if (mCurrentTheme != null && mCurrentTheme.equals(newTheme)) {
+ return false;
+ }
+
+ int themeId = mLightResId;
+ if (Constants.Pref.Theme.DARK.equals(newTheme)) {
+ themeId = mDarkResId;
+ }
+
+ ContextThemeWrapper w = new ContextThemeWrapper(mContext, themeId);
+ mContext.getTheme().setTo(w.getTheme());
+ mCurrentTheme = newTheme;
+
+ return true;
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/DividerItemDecoration.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/DividerItemDecoration.java
new file mode 100644
index 000000000..95199bcd5
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/DividerItemDecoration.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.ui.util.recyclerview;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+public class DividerItemDecoration extends RecyclerView.ItemDecoration {
+
+ private static final int[] ATTRS = new int[]{
+ android.R.attr.listDivider
+ };
+
+ public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
+
+ public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
+
+ private Drawable mDivider;
+
+ private int mOrientation;
+
+ public DividerItemDecoration(Context context, int orientation) {
+ final TypedArray a = context.obtainStyledAttributes(ATTRS);
+ mDivider = a.getDrawable(0);
+ a.recycle();
+ setOrientation(orientation);
+ }
+
+ public void setOrientation(int orientation) {
+ if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
+ throw new IllegalArgumentException("invalid orientation");
+ }
+ mOrientation = orientation;
+ }
+
+ @Override
+ public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+ if (mOrientation == VERTICAL_LIST) {
+ drawVertical(c, parent);
+ } else {
+ drawHorizontal(c, parent);
+ }
+ }
+
+ public void drawVertical(Canvas c, RecyclerView parent) {
+ final int left = parent.getPaddingLeft();
+ final int right = parent.getWidth() - parent.getPaddingRight();
+
+ final int childCount = parent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = parent.getChildAt(i);
+ final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
+ .getLayoutParams();
+ final int top = child.getBottom() + params.bottomMargin;
+ final int bottom = top + mDivider.getIntrinsicHeight();
+ mDivider.setBounds(left, top, right, bottom);
+ mDivider.draw(c);
+ }
+ }
+
+ public void drawHorizontal(Canvas c, RecyclerView parent) {
+ final int top = parent.getPaddingTop();
+ final int bottom = parent.getHeight() - parent.getPaddingBottom();
+
+ final int childCount = parent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = parent.getChildAt(i);
+ final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
+ .getLayoutParams();
+ final int left = child.getRight() + params.rightMargin;
+ final int right = left + mDivider.getIntrinsicHeight();
+ mDivider.setBounds(left, top, right, bottom);
+ mDivider.draw(c);
+ }
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
+ RecyclerView.State state) {
+ if (mOrientation == VERTICAL_LIST) {
+ outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
+ } else {
+ outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
+ }
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperAdapter.java
new file mode 100644
index 000000000..c691182bf
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperAdapter.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 Paul Burke
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.ui.util.recyclerview;
+
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
+
+/**
+ * Interface to listen for a move or dismissal event from a {@link ItemTouchHelper.Callback}.
+ */
+public interface ItemTouchHelperAdapter {
+
+ /**
+ * Called when an item has been dragged far enough to trigger a move. This is called every time
+ * an item is shifted, and <strong>not</strong> at the end of a "drop" event.<br/>
+ * <br/>
+ * Implementations should call {@link RecyclerView.Adapter#notifyItemMoved(int, int)} after
+ * adjusting the underlying data to reflect this move.
+ *
+ * @param fromPosition The start position of the moved item.
+ * @param toPosition Then resolved position of the moved item.
+ * @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
+ * @see RecyclerView.ViewHolder#getAdapterPosition()
+ */
+ void onItemMove(RecyclerView.ViewHolder source, RecyclerView.ViewHolder target,
+ int fromPosition, int toPosition);
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperDragCallback.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperDragCallback.java
new file mode 100644
index 000000000..0fd24581d
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperDragCallback.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 Paul Burke
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.ui.util.recyclerview;
+
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
+
+/**
+ * An implementation of {@link ItemTouchHelper.Callback} that enables basic drag & drop and
+ * swipe-to-dismiss. Drag events are automatically started by an item long-press.<br/>
+ * </br/>
+ * Expects the <code>RecyclerView.Adapter</code> to listen for {@link
+ * ItemTouchHelperAdapter} callbacks and the <code>RecyclerView.ViewHolder</code> to implement
+ * {@link ItemTouchHelperViewHolder}.
+ */
+public class ItemTouchHelperDragCallback extends ItemTouchHelper.Callback {
+
+ private final ItemTouchHelperAdapter mAdapter;
+
+ public ItemTouchHelperDragCallback(ItemTouchHelperAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ @Override
+ public boolean isLongPressDragEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isItemViewSwipeEnabled() {
+ return false;
+ }
+
+ @Override
+ public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
+ // Enable drag and swipe in both directions
+ final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
+ final int swipeFlags = 0;
+ return makeMovementFlags(dragFlags, swipeFlags);
+ }
+
+ @Override
+ public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source,
+ RecyclerView.ViewHolder target) {
+ if (source.getItemViewType() != target.getItemViewType()) {
+ return false;
+ }
+
+ // Notify the adapter of the move
+ mAdapter.onItemMove(source, target, source.getAdapterPosition(), target.getAdapterPosition());
+ return true;
+ }
+
+ @Override
+ public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
+ // we don't support swipe
+ }
+
+ @Override
+ public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
+ if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
+ // Let the view holder know that this item is being moved or dragged
+ ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
+ itemViewHolder.onItemSelected();
+ }
+
+ super.onSelectedChanged(viewHolder, actionState);
+ }
+
+ @Override
+ public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
+ super.clearView(recyclerView, viewHolder);
+
+ // Tell the view holder it's time to restore the idle state
+ ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
+ itemViewHolder.onItemClear();
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperViewHolder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperViewHolder.java
new file mode 100644
index 000000000..97e70d71e
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperViewHolder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 Paul Burke
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.ui.util.recyclerview;
+
+import android.support.v7.widget.helper.ItemTouchHelper;
+
+/**
+ * Interface to notify an item ViewHolder of relevant callbacks from {@link
+ * android.support.v7.widget.helper.ItemTouchHelper.Callback}.
+ */
+public interface ItemTouchHelperViewHolder {
+
+ /**
+ * Called when the {@link ItemTouchHelper} first registers an item as being moved or swiped.
+ * Implementations should update the item view to indicate it's active state.
+ */
+ void onItemSelected();
+
+
+ /**
+ * Called when the {@link ItemTouchHelper} has completed the move or swipe, and the active item
+ * state should be cleared.
+ */
+ void onItemClear();
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/RecyclerItemClickListener.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/RecyclerItemClickListener.java
new file mode 100644
index 000000000..7efcbb30c
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/RecyclerItemClickListener.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 Jacob Tabak
+ *
+ * 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.util.recyclerview;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * based on http://stackoverflow.com/a/26196831/3000919
+ */
+public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
+ private OnItemClickListener mListener;
+ private boolean mIgnoreTouch = false;
+
+ public interface OnItemClickListener {
+ void onItemClick(View view, int position);
+ }
+
+ GestureDetector mGestureDetector;
+
+ public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
+ mListener = listener;
+ mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ return true;
+ }
+ });
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
+ if (mIgnoreTouch) {
+ return false;
+ }
+ View childView = view.findChildViewUnder(e.getX(), e.getY());
+ if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
+ mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
+ // TODO: should we move mListener.onItemClick here
+ }
+
+ @Override
+ public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ mIgnoreTouch = disallowIntercept;
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java
index e5b3df8c9..6a51085f3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java
@@ -25,14 +25,12 @@ import android.support.annotation.StringRes;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.AttributeSet;
-import android.widget.ImageView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
+import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
public class CertifyKeySpinner extends KeySpinner {
private long mHiddenMasterKeyId = Constants.key.none;
@@ -61,19 +59,9 @@ public class CertifyKeySpinner extends KeySpinner {
// sample only has one Loader, so we don't care about the ID.
Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri();
- // These are the rows that we will retrieve.
- String[] projection = new String[]{
- KeychainContract.KeyRings._ID,
- KeychainContract.KeyRings.MASTER_KEY_ID,
- KeychainContract.KeyRings.KEY_ID,
- KeychainContract.KeyRings.USER_ID,
- KeychainContract.KeyRings.IS_REVOKED,
- KeychainContract.KeyRings.IS_EXPIRED,
+ String[] projection = KeyAdapter.getProjectionWith(new String[] {
KeychainContract.KeyRings.HAS_CERTIFY,
- KeychainContract.KeyRings.HAS_ANY_SECRET,
- KeychainContract.KeyRings.HAS_DUPLICATE_USER_ID,
- KeychainContract.KeyRings.CREATION
- };
+ });
String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1 AND "
+ KeychainDatabase.Tables.KEYS + "." + KeychainContract.KeyRings.MASTER_KEY_ID
@@ -84,7 +72,7 @@ public class CertifyKeySpinner extends KeySpinner {
return new CursorLoader(getContext(), baseUri, projection, where, null, null);
}
- private int mIndexHasCertify, mIndexIsRevoked, mIndexIsExpired;
+ private int mIndexHasCertify;
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
@@ -92,16 +80,13 @@ public class CertifyKeySpinner extends KeySpinner {
if (loader.getId() == LOADER_ID) {
mIndexHasCertify = data.getColumnIndex(KeychainContract.KeyRings.HAS_CERTIFY);
- mIndexIsRevoked = data.getColumnIndex(KeychainContract.KeyRings.IS_REVOKED);
- mIndexIsExpired = data.getColumnIndex(KeychainContract.KeyRings.IS_EXPIRED);
// If:
// - no key has been pre-selected (e.g. by SageSlinger)
// - there are actually keys (not just "none" entry)
// Then:
// - select key that is capable of certifying, but only if there is only one key capable of it
- mIsSingle = false;
- if (mSelectedKeyId == Constants.key.none && mAdapter.getCount() > 1) {
+ if (mPreSelectedKeyId == Constants.key.none && mAdapter.getCount() > 1) {
// preselect if key can certify
int selection = -1;
while (data.moveToNext()) {
@@ -127,18 +112,19 @@ public class CertifyKeySpinner extends KeySpinner {
}
@Override
- boolean setStatus(Context context, Cursor cursor, ImageView statusView) {
- if (cursor.getInt(mIndexIsRevoked) != 0) {
- KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.REVOKED, R.color.bg_gray);
+ boolean isItemEnabled(Cursor cursor) {
+ // "none" entry is always enabled!
+ if (cursor.getPosition() == 0) {
+ return true;
+ }
+
+ if (cursor.getInt(KeyAdapter.INDEX_IS_REVOKED) != 0) {
return false;
}
- if (cursor.getInt(mIndexIsExpired) != 0) {
- KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.EXPIRED, R.color.bg_gray);
+ if (cursor.getInt(KeyAdapter.INDEX_IS_EXPIRED) != 0) {
return false;
}
- // don't invalidate the "None" entry, which is also null!
- if (cursor.getPosition() != 0 && cursor.isNull(mIndexHasCertify)) {
- KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.UNAVAILABLE, R.color.bg_gray);
+ if (cursor.isNull(mIndexHasCertify)) {
return false;
}
@@ -146,6 +132,7 @@ public class CertifyKeySpinner extends KeySpinner {
return true;
}
+ @Override
public @StringRes int getNoneString() {
return R.string.choice_select_cert;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java
deleted file mode 100644
index ec91b9fe4..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.ui.widget;
-
-public interface Editor {
- public interface EditorListener {
- public void onDeleted(Editor editor, boolean wasNewItem);
- public void onEdited();
- }
-
- public void setEditorListener(EditorListener listener);
- public boolean needsSaving();
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java
index e55f6b1ad..494ccb6d3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java
@@ -75,10 +75,10 @@ public class EmailEditText extends AppCompatAutoCompleteTextView {
Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
if (emailMatcher.matches()) {
EmailEditText.this.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.uid_mail_ok, 0);
+ R.drawable.ic_stat_retyped_ok, 0);
} else {
EmailEditText.this.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.uid_mail_bad, 0);
+ R.drawable.ic_stat_retyped_bad, 0);
}
} else {
// remove drawable if email is empty
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
index 63a1aade9..48e6c2cee 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
@@ -38,6 +38,7 @@ import com.tokenautocomplete.TokenCompleteTextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
@@ -126,7 +127,13 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// These are the rows that we will retrieve.
Uri baseUri = KeyRings.buildUnifiedKeyRingsUri();
- String where = KeyRings.HAS_ENCRYPT + " NOT NULL AND " + KeyRings.IS_EXPIRED + " = 0 AND "
+
+ String[] projection = KeyAdapter.getProjectionWith(new String[] {
+ KeychainContract.KeyRings.HAS_ENCRYPT,
+ });
+
+ String where = KeyRings.HAS_ENCRYPT + " NOT NULL AND "
+ + KeyRings.IS_EXPIRED + " = 0 AND "
+ Tables.KEYS + "." + KeyRings.IS_REVOKED + " = 0";
if (args != null && args.containsKey(ARG_QUERY)) {
@@ -135,12 +142,12 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView
where += " AND " + KeyRings.USER_ID + " LIKE ?";
- return new CursorLoader(getContext(), baseUri, KeyAdapter.PROJECTION, where,
+ return new CursorLoader(getContext(), baseUri, projection, where,
new String[]{"%" + query + "%"}, null);
}
mAdapter.setSearchQuery(null);
- return new CursorLoader(getContext(), baseUri, KeyAdapter.PROJECTION, where, null, null);
+ return new CursorLoader(getContext(), baseUri, projection, where, null, null);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
deleted file mode 100644
index 3fd01958a..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.ui.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.sufficientlysecure.keychain.R;
-
-public class KeyServerEditor extends LinearLayout implements Editor, OnClickListener {
- private EditorListener mEditorListener = null;
-
- ImageButton mDeleteButton;
- TextView mServer;
-
- public KeyServerEditor(Context context) {
- super(context);
- }
-
- public KeyServerEditor(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- setDrawingCacheEnabled(true);
- setAlwaysDrawnWithCacheEnabled(true);
-
- mServer = (TextView) findViewById(R.id.server);
-
- mDeleteButton = (ImageButton) findViewById(R.id.delete);
- mDeleteButton.setOnClickListener(this);
-
- super.onFinishInflate();
- }
-
- public void setValue(String value) {
- mServer.setText(value);
- }
-
- public String getValue() {
- return mServer.getText().toString().trim();
- }
-
- public void onClick(View v) {
- final ViewGroup parent = (ViewGroup) getParent();
- if (v == mDeleteButton) {
- parent.removeView(this);
- if (mEditorListener != null) {
- mEditorListener.onDeleted(this, false);
- }
- }
- }
-
- @Override
- public boolean needsSaving() {
- return false;
- }
-
- public void setEditorListener(EditorListener listener) {
- mEditorListener = listener;
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java
index 61b7c718b..e3ec3d34b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java
@@ -17,41 +17,48 @@
package org.sufficientlysecure.keychain.ui.widget;
+
import android.content.Context;
import android.database.Cursor;
-import android.graphics.Color;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
-import android.support.v4.widget.CursorAdapter;
import android.support.v7.widget.AppCompatSpinner;
-import android.text.format.DateUtils;
import android.util.AttributeSet;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
-import android.widget.ImageView;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
+import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter.KeyItem;
+
/**
* Use AppCompatSpinner from AppCompat lib instead of Spinner. Fixes white dropdown icon.
* Related: http://stackoverflow.com/a/27713090
*/
-public abstract class KeySpinner extends AppCompatSpinner implements LoaderManager.LoaderCallbacks<Cursor> {
+public abstract class KeySpinner extends AppCompatSpinner implements
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ public static final String ARG_SUPER_STATE = "super_state";
+ public static final String ARG_KEY_ID = "key_id";
+
public interface OnKeyChangedListener {
- public void onKeyChanged(long masterKeyId);
+ void onKeyChanged(long masterKeyId);
}
- protected long mSelectedKeyId = Constants.key.none;
+ protected long mPreSelectedKeyId = Constants.key.none;
protected SelectKeyAdapter mAdapter = new SelectKeyAdapter();
protected OnKeyChangedListener mListener;
@@ -79,7 +86,8 @@ public abstract class KeySpinner extends AppCompatSpinner implements LoaderManag
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (mListener != null) {
- mListener.onKeyChanged(id);
+ long keyId = getSelectedKeyId(getItemAtPosition(position));
+ mListener.onKeyChanged(keyId);
}
}
@@ -111,7 +119,8 @@ public abstract class KeySpinner extends AppCompatSpinner implements LoaderManag
if (getContext() instanceof FragmentActivity) {
((FragmentActivity) getContext()).getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
} else {
- Log.e(Constants.TAG, "KeySpinner must be attached to FragmentActivity, this is " + getContext().getClass());
+ throw new AssertionError("KeySpinner must be attached to FragmentActivity, this is "
+ + getContext().getClass());
}
}
@@ -129,172 +138,134 @@ public abstract class KeySpinner extends AppCompatSpinner implements LoaderManag
}
}
- public void setSelectedKeyId(long selectedKeyId) {
- this.mSelectedKeyId = selectedKeyId;
+ public long getSelectedKeyId() {
+ Object item = getSelectedItem();
+ return getSelectedKeyId(item);
+ }
+
+ public long getSelectedKeyId(Object item) {
+ if (item instanceof KeyItem) {
+ return ((KeyItem) item).mKeyId;
+ }
+ return Constants.key.none;
+ }
+
+ public void setPreSelectedKeyId(long selectedKeyId) {
+ mPreSelectedKeyId = selectedKeyId;
}
protected class SelectKeyAdapter extends BaseAdapter implements SpinnerAdapter {
- private CursorAdapter inner;
- private int mIndexUserId;
- private int mIndexDuplicate;
+ private KeyAdapter inner;
private int mIndexMasterKeyId;
- private int mIndexCreationDate;
public SelectKeyAdapter() {
- inner = new CursorAdapter(getContext(), null, 0) {
- @Override
- public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return View.inflate(getContext(), R.layout.keyspinner_item, null);
- }
+ inner = new KeyAdapter(getContext(), null, 0) {
@Override
- public void bindView(View view, Context context, Cursor cursor) {
- TextView vKeyName = (TextView) view.findViewById(R.id.keyspinner_key_name);
- ImageView vKeyStatus = (ImageView) view.findViewById(R.id.keyspinner_key_status);
- TextView vKeyEmail = (TextView) view.findViewById(R.id.keyspinner_key_email);
- TextView vDuplicate = (TextView) view.findViewById(R.id.keyspinner_duplicate);
-
- KeyRing.UserId userId = KeyRing.splitUserId(cursor.getString(mIndexUserId));
- vKeyName.setText(userId.name);
- vKeyEmail.setText(userId.email);
-
- boolean duplicate = cursor.getLong(mIndexDuplicate) > 0;
- if (duplicate) {
- String dateTime = DateUtils.formatDateTime(context,
- cursor.getLong(mIndexCreationDate) * 1000,
- DateUtils.FORMAT_SHOW_DATE
- | DateUtils.FORMAT_SHOW_TIME
- | DateUtils.FORMAT_SHOW_YEAR
- | DateUtils.FORMAT_ABBREV_MONTH);
-
- vDuplicate.setText(context.getString(R.string.label_key_created, dateTime));
- vDuplicate.setVisibility(View.VISIBLE);
- } else {
- vDuplicate.setVisibility(View.GONE);
- }
-
- boolean valid = setStatus(getContext(), cursor, vKeyStatus);
- setItemEnabled(view, valid);
+ public boolean isEnabled(Cursor cursor) {
+ return KeySpinner.this.isItemEnabled(cursor);
}
- @Override
- public long getItemId(int position) {
- try {
- return ((Cursor) getItem(position)).getLong(mIndexMasterKeyId);
- } catch (Exception e) {
- // This can happen on concurrent modification :(
- return Constants.key.none;
- }
- }
};
}
- private void setItemEnabled(View view, boolean enabled) {
- TextView vKeyName = (TextView) view.findViewById(R.id.keyspinner_key_name);
- ImageView vKeyStatus = (ImageView) view.findViewById(R.id.keyspinner_key_status);
- TextView vKeyEmail = (TextView) view.findViewById(R.id.keyspinner_key_email);
- TextView vKeyDuplicate = (TextView) view.findViewById(R.id.keyspinner_duplicate);
-
- if (enabled) {
- vKeyName.setTextColor(Color.BLACK);
- vKeyEmail.setTextColor(Color.BLACK);
- vKeyDuplicate.setTextColor(Color.BLACK);
- vKeyStatus.setVisibility(View.GONE);
- view.setClickable(false);
- } else {
- vKeyName.setTextColor(Color.GRAY);
- vKeyEmail.setTextColor(Color.GRAY);
- vKeyDuplicate.setTextColor(Color.GRAY);
- vKeyStatus.setVisibility(View.VISIBLE);
- // this is a HACK. the trick is, if the element itself is clickable, the
- // click is not passed on to the view list
- view.setClickable(true);
- }
- }
-
public Cursor swapCursor(Cursor newCursor) {
if (newCursor == null) return inner.swapCursor(null);
- mIndexDuplicate = newCursor.getColumnIndex(KeychainContract.KeyRings.HAS_DUPLICATE_USER_ID);
- mIndexUserId = newCursor.getColumnIndex(KeychainContract.KeyRings.USER_ID);
mIndexMasterKeyId = newCursor.getColumnIndex(KeychainContract.KeyRings.MASTER_KEY_ID);
- mIndexCreationDate = newCursor.getColumnIndex(KeychainContract.KeyRings.CREATION);
- // pre-select key if mSelectedKeyId is given
- if (mSelectedKeyId != Constants.key.none && newCursor.moveToFirst()) {
+ Cursor oldCursor = inner.swapCursor(newCursor);
+
+ // pre-select key if mPreSelectedKeyId is given
+ if (mPreSelectedKeyId != Constants.key.none && newCursor.moveToFirst()) {
do {
- if (newCursor.getLong(mIndexMasterKeyId) == mSelectedKeyId) {
- setSelection(newCursor.getPosition() + 1);
+ if (newCursor.getLong(mIndexMasterKeyId) == mPreSelectedKeyId) {
+ setSelection(newCursor.getPosition() +1);
}
} while (newCursor.moveToNext());
}
- return inner.swapCursor(newCursor);
+ return oldCursor;
}
@Override
public int getCount() {
- return inner.getCount() + 1;
+ return inner.getCount() +1;
}
@Override
- public Object getItem(int position) {
- if (position == 0) return null;
- return inner.getItem(position - 1);
+ public KeyItem getItem(int position) {
+ if (position == 0) {
+ return null;
+ }
+ return inner.getItem(position -1);
}
@Override
public long getItemId(int position) {
- if (position == 0) return Constants.key.none;
- return inner.getItemId(position - 1);
+ if (position == 0) {
+ return Constants.key.none;
+ }
+ return inner.getItemId(position -1);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
- try {
- View v = getDropDownView(position, convertView, parent);
- v.findViewById(R.id.keyspinner_key_email).setVisibility(View.GONE);
- return v;
- } catch (NullPointerException e) {
- // This is for the preview...
- return View.inflate(getContext(), android.R.layout.simple_list_item_1, null);
- }
- }
- @Override
- public View getDropDownView(int position, View convertView, ViewGroup parent) {
- View view;
- if (position == 0) {
- if (convertView == null) {
- view = inner.newView(null, null, parent);
- } else {
- view = convertView;
+ // Unfortunately, SpinnerAdapter does not support multiple view
+ // types. For this reason, we throw away convertViews of a bad
+ // type. This is sort of a hack, but since the number of elements
+ // we deal with in KeySpinners is usually very small (number of
+ // secret keys), this is the easiest solution. (I'm sorry.)
+ if (convertView != null) {
+ // This assumes that the inner view has non-null tags on its views!
+ boolean isWrongType = (convertView.getTag() == null) != (position == 0);
+ if (isWrongType) {
+ convertView = null;
}
- TextView vKeyName = (TextView) view.findViewById(R.id.keyspinner_key_name);
- ImageView vKeyStatus = (ImageView) view.findViewById(R.id.keyspinner_key_status);
- TextView vKeyEmail = (TextView) view.findViewById(R.id.keyspinner_key_email);
- TextView vKeyDuplicate = (TextView) view.findViewById(R.id.keyspinner_duplicate);
-
- vKeyName.setText(getNoneString());
- vKeyEmail.setVisibility(View.GONE);
- vKeyDuplicate.setVisibility(View.GONE);
- vKeyStatus.setVisibility(View.GONE);
- setItemEnabled(view, true);
- } else {
- view = inner.getView(position - 1, convertView, parent);
- TextView vKeyEmail = (TextView) view.findViewById(R.id.keyspinner_key_email);
- vKeyEmail.setVisibility(View.VISIBLE);
}
+
+ if (position > 0) {
+ return inner.getView(position -1, convertView, parent);
+ }
+
+ View view = convertView != null ? convertView :
+ LayoutInflater.from(getContext()).inflate(
+ R.layout.keyspinner_item_none, parent, false);
+ ((TextView) view.findViewById(R.id.keyspinner_key_name)).setText(getNoneString());
return view;
}
}
- boolean setStatus(Context context, Cursor cursor, ImageView statusView) {
+ boolean isItemEnabled(Cursor cursor) {
return true;
}
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ Bundle bundle = (Bundle) state;
+
+ mPreSelectedKeyId = bundle.getLong(ARG_KEY_ID);
+
+ // restore super state
+ super.onRestoreInstanceState(bundle.getParcelable(ARG_SUPER_STATE));
+
+ }
+
+ @NonNull
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Bundle bundle = new Bundle();
+
+ // save super state
+ bundle.putParcelable(ARG_SUPER_STATE, super.onSaveInstanceState());
+
+ bundle.putLong(ARG_KEY_ID, getSelectedKeyId());
+ return bundle;
+ }
+
public @StringRes int getNoneString() {
- return R.string.choice_none;
+ return R.string.cert_none;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthView.java
index 1487c3053..a7ead8039 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthView.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PasswordStrengthView.java
@@ -31,7 +31,9 @@ import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
+import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.util.Log;
/**
* Created by Matt Allen
@@ -97,26 +99,23 @@ public class PasswordStrengthView extends View {
public PasswordStrengthView(Context context, AttributeSet attrs) {
super(context, attrs);
- int COLOR_FAIL = context.getResources().getColor(R.color.android_red_light);
- int COLOR_WEAK = context.getResources().getColor(R.color.android_orange_light);
- int COLOR_STRONG = context.getResources().getColor(R.color.android_green_light);
+ int COLOR_FAIL = getResources().getColor(R.color.password_strength_low);
+ int COLOR_WEAK = getResources().getColor(R.color.password_strength_medium);
+ int COLOR_STRONG = getResources().getColor(R.color.password_strength_high);
TypedArray style = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.PasswordStrengthView,
0, 0);
- try {
- mStrengthRequirement = style.getInteger(R.styleable.PasswordStrengthView_strength,
- STRENGTH_MEDIUM);
- mShowGuides = style.getBoolean(R.styleable.PasswordStrengthView_showGuides, true);
- mColorFail = style.getColor(R.styleable.PasswordStrengthView_color_fail, COLOR_FAIL);
- mColorWeak = style.getColor(R.styleable.PasswordStrengthView_color_weak, COLOR_WEAK);
- mColorStrong = style.getColor(R.styleable.PasswordStrengthView_color_strong,
- COLOR_STRONG);
- } catch (Exception e) {
- e.printStackTrace();
- }
+ mStrengthRequirement = style.getInteger(R.styleable.PasswordStrengthView_strength,
+ STRENGTH_MEDIUM);
+ mShowGuides = style.getBoolean(R.styleable.PasswordStrengthView_showGuides, true);
+ mColorFail = style.getColor(R.styleable.PasswordStrengthView_color_fail, COLOR_FAIL);
+ mColorWeak = style.getColor(R.styleable.PasswordStrengthView_color_weak, COLOR_WEAK);
+ mColorStrong = style.getColor(R.styleable.PasswordStrengthView_color_strong,
+ COLOR_STRONG);
+
// Create and style the paint used for drawing the guide on the indicator
mGuidePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mGuidePaint.setStyle(Paint.Style.FILL_AND_STROKE);
@@ -124,6 +123,9 @@ public class PasswordStrengthView extends View {
// Create and style paint for indicator
mIndicatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mIndicatorPaint.setStyle(Paint.Style.FILL);
+
+ style.recycle();
+
}
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java
index df7347fa4..8fb9e38aa 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java
@@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.ui.widget;
+
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
@@ -24,12 +25,9 @@ import android.os.Bundle;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.AttributeSet;
-import android.widget.ImageView;
-import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
+import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
public class SignKeySpinner extends KeySpinner {
public SignKeySpinner(Context context) {
@@ -50,19 +48,9 @@ public class SignKeySpinner extends KeySpinner {
// sample only has one Loader, so we don't care about the ID.
Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri();
- // These are the rows that we will retrieve.
- String[] projection = new String[]{
- KeychainContract.KeyRings._ID,
- KeychainContract.KeyRings.MASTER_KEY_ID,
- KeychainContract.KeyRings.KEY_ID,
- KeychainContract.KeyRings.USER_ID,
- KeychainContract.KeyRings.IS_REVOKED,
- KeychainContract.KeyRings.IS_EXPIRED,
+ String[] projection = KeyAdapter.getProjectionWith(new String[] {
KeychainContract.KeyRings.HAS_SIGN,
- KeychainContract.KeyRings.HAS_ANY_SECRET,
- KeychainContract.KeyRings.HAS_DUPLICATE_USER_ID,
- KeychainContract.KeyRings.CREATION
- };
+ });
String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1";
@@ -71,7 +59,7 @@ public class SignKeySpinner extends KeySpinner {
return new CursorLoader(getContext(), baseUri, projection, where, null, null);
}
- private int mIndexHasSign, mIndexIsRevoked, mIndexIsExpired;
+ private int mIndexHasSign;
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
@@ -79,23 +67,23 @@ public class SignKeySpinner extends KeySpinner {
if (loader.getId() == LOADER_ID) {
mIndexHasSign = data.getColumnIndex(KeychainContract.KeyRings.HAS_SIGN);
- mIndexIsRevoked = data.getColumnIndex(KeychainContract.KeyRings.IS_REVOKED);
- mIndexIsExpired = data.getColumnIndex(KeychainContract.KeyRings.IS_EXPIRED);
}
}
@Override
- boolean setStatus(Context context, Cursor cursor, ImageView statusView) {
- if (cursor.getInt(mIndexIsRevoked) != 0) {
- KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.REVOKED, R.color.bg_gray);
+ boolean isItemEnabled(Cursor cursor) {
+ // "none" entry is always enabled!
+ if (cursor.getPosition() == 0) {
+ return true;
+ }
+
+ if (cursor.getInt(KeyAdapter.INDEX_IS_REVOKED) != 0) {
return false;
}
- if (cursor.getInt(mIndexIsExpired) != 0) {
- KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.EXPIRED, R.color.bg_gray);
+ if (cursor.getInt(KeyAdapter.INDEX_IS_EXPIRED) != 0) {
return false;
}
- if (cursor.getInt(mIndexHasSign) == 0) {
- KeyFormattingUtils.setStatusImage(getContext(), statusView, null, State.UNAVAILABLE, R.color.bg_gray);
+ if (cursor.isNull(mIndexHasSign)) {
return false;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ToolableViewAnimator.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ToolableViewAnimator.java
new file mode 100644
index 000000000..18e830139
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/ToolableViewAnimator.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.sufficientlysecure.keychain.ui.widget;
+
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.annotation.NonNull;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ViewAnimator;
+
+import org.sufficientlysecure.keychain.R;
+
+
+/** This view is essentially identical to ViewAnimator, but allows specifying the initial view
+ * for preview as an xml attribute. */
+public class ToolableViewAnimator extends ViewAnimator {
+
+ private int mInitChild = -1;
+
+ public ToolableViewAnimator(Context context) {
+ super(context);
+ }
+
+ public ToolableViewAnimator(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ if (isInEditMode()) {
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ToolableViewAnimator);
+ mInitChild = a.getInt(R.styleable.ToolableViewAnimator_initialView, -1);
+ a.recycle();
+ }
+ }
+
+ public ToolableViewAnimator(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs);
+
+ if (isInEditMode()) {
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ToolableViewAnimator, defStyleAttr, 0);
+ mInitChild = a.getInt(R.styleable.ToolableViewAnimator_initialView, -1);
+ a.recycle();
+ }
+ }
+
+ @Override
+ public void addView(@NonNull View child, int index, ViewGroup.LayoutParams params) {
+ if (isInEditMode() && mInitChild-- > 0) {
+ return;
+ }
+ super.addView(child, index, params);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java
index e1efd5abc..77aa1a055 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java
@@ -303,10 +303,13 @@ public class ContactHelper {
Cursor contactMasterKey = context.getContentResolver().query(contactUri,
new String[]{ContactsContract.Data.DATA2}, null, null, null);
if (contactMasterKey != null) {
- if (contactMasterKey.moveToNext()) {
- return KeychainContract.KeyRings.buildGenericKeyRingUri(contactMasterKey.getLong(0));
+ try {
+ if (contactMasterKey.moveToNext()) {
+ return KeychainContract.KeyRings.buildGenericKeyRingUri(contactMasterKey.getLong(0));
+ }
+ } finally {
+ contactMasterKey.close();
}
- contactMasterKey.close();
}
return null;
}
@@ -537,7 +540,7 @@ public class ContactHelper {
KEYS_TO_CONTACT_PROJECTION,
KeychainContract.KeyRings.HAS_ANY_SECRET + "!=0",
null, null);
- if (cursor != null) {
+ if (cursor != null) try {
while (cursor.moveToNext()) {
long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0;
@@ -565,6 +568,8 @@ public class ContactHelper {
}
}
}
+ } finally {
+ cursor.close();
}
for (long masterKeyId : keysToDelete) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java
index 8334b37ec..d7491ab26 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java
@@ -18,16 +18,16 @@
package org.sufficientlysecure.keychain.util;
import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Messenger;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
+import java.net.Proxy;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -35,27 +35,40 @@ import java.util.Locale;
import java.util.Set;
public class EmailKeyHelper {
+ // TODO: Make this not require a proxy in it's constructor, redesign when it is to be used
+ // to import keys, simply use CryptoOperationHelper with this callback
+ public abstract class ImportContactKeysCallback
+ implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
- public static void importContacts(Context context, Messenger messenger) {
- importAll(context, messenger, ContactHelper.getContactMails(context));
- }
+ private ArrayList<ParcelableKeyRing> mKeyList;
+ private String mKeyserver;
- public static void importAll(Context context, Messenger messenger, List<String> mails) {
- // Collect all candidates as ImportKeysListEntry (set for deduplication)
- Set<ImportKeysListEntry> entries = new HashSet<>();
- for (String mail : mails) {
- entries.addAll(getEmailKeys(context, mail));
+ public ImportContactKeysCallback(Context context, String keyserver, Proxy proxy) {
+ this(context, ContactHelper.getContactMails(context), keyserver, proxy);
}
- // Put them in a list and import
- ArrayList<ParcelableKeyRing> keys = new ArrayList<>(entries.size());
- for (ImportKeysListEntry entry : entries) {
- keys.add(new ParcelableKeyRing(entry.getFingerprintHex(), entry.getKeyIdHex(), null));
+ public ImportContactKeysCallback(Context context, List<String> mails, String keyserver,
+ Proxy proxy) {
+ Set<ImportKeysListEntry> entries = new HashSet<>();
+ for (String mail : mails) {
+ entries.addAll(getEmailKeys(context, mail, proxy));
+ }
+
+ // Put them in a list and import
+ ArrayList<ParcelableKeyRing> keys = new ArrayList<>(entries.size());
+ for (ImportKeysListEntry entry : entries) {
+ keys.add(new ParcelableKeyRing(entry.getFingerprintHex(), entry.getKeyIdHex(), null));
+ }
+ mKeyList = keys;
+ mKeyserver = keyserver;
+ }
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(mKeyList, mKeyserver);
}
- importKeys(context, messenger, keys);
}
- public static Set<ImportKeysListEntry> getEmailKeys(Context context, String mail) {
+ public static Set<ImportKeysListEntry> getEmailKeys(Context context, String mail, Proxy proxy) {
Set<ImportKeysListEntry> keys = new HashSet<>();
// Try _hkp._tcp SRV record first
@@ -63,7 +76,7 @@ public class EmailKeyHelper {
if (mailparts.length == 2) {
HkpKeyserver hkp = HkpKeyserver.resolve(mailparts[1]);
if (hkp != null) {
- keys.addAll(getEmailKeys(mail, hkp));
+ keys.addAll(getEmailKeys(mail, hkp, proxy));
}
}
@@ -72,27 +85,17 @@ public class EmailKeyHelper {
String server = Preferences.getPreferences(context).getPreferredKeyserver();
if (server != null) {
HkpKeyserver hkp = new HkpKeyserver(server);
- keys.addAll(getEmailKeys(mail, hkp));
+ keys.addAll(getEmailKeys(mail, hkp, proxy));
}
}
return keys;
}
- private static void importKeys(Context context, Messenger messenger, ArrayList<ParcelableKeyRing> keys) {
- Intent importIntent = new Intent(context, KeychainIntentService.class);
- importIntent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING);
- Bundle importData = new Bundle();
- importData.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, keys);
- importIntent.putExtra(KeychainIntentService.EXTRA_DATA, importData);
- importIntent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- context.startService(importIntent);
- }
-
- public static List<ImportKeysListEntry> getEmailKeys(String mail, Keyserver keyServer) {
+ public static List<ImportKeysListEntry> getEmailKeys(String mail, Keyserver keyServer,
+ Proxy proxy) {
Set<ImportKeysListEntry> keys = new HashSet<>();
try {
- for (ImportKeysListEntry key : keyServer.search(mail)) {
+ for (ImportKeysListEntry key : keyServer.search(mail, proxy)) {
if (key.isRevoked() || key.isExpired()) continue;
for (String userId : key.getUserIds()) {
if (userId.toLowerCase().contains(mail.toLowerCase(Locale.ENGLISH))) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java
index 7efb7c5af..45dc33906 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java
@@ -17,41 +17,40 @@
package org.sufficientlysecure.keychain.util;
-import android.app.ProgressDialog;
+
+import java.io.File;
+
import android.content.Intent;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
+import android.net.Uri;
import android.support.v4.app.FragmentActivity;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
-import java.io.File;
-
-public class ExportHelper {
+public class ExportHelper
+ implements CryptoOperationHelper.Callback <ExportKeyringParcel, ExportResult> {
protected File mExportFile;
FragmentActivity mActivity;
+ private boolean mExportSecret;
+ private long[] mMasterKeyIds;
+
public ExportHelper(FragmentActivity activity) {
super();
this.mActivity = activity;
}
- /**
- * Show dialog where to export keys
- */
- public void showExportKeysDialog(final long[] masterKeyIds, final File exportFile,
- final boolean showSecretCheckbox) {
+ /** Show dialog where to export keys */
+ public void showExportKeysDialog(final Long masterKeyId, final File exportFile,
+ final boolean exportSecret) {
mExportFile = exportFile;
- String title = null;
- if (masterKeyIds == null) {
+ String title;
+ if (masterKeyId == null) {
// export all keys
title = mActivity.getString(R.string.title_export_keys);
} else {
@@ -59,72 +58,67 @@ public class ExportHelper {
title = mActivity.getString(R.string.title_export_key);
}
- String message = mActivity.getString(R.string.specify_file_to_export_to);
- String checkMsg = showSecretCheckbox ?
- mActivity.getString(R.string.also_export_secret_keys) : null;
+ String message;
+ if (exportSecret) {
+ message = mActivity.getString(masterKeyId == null
+ ? R.string.specify_backup_dest_secret
+ : R.string.specify_backup_dest_secret_single);
+ } else {
+ message = mActivity.getString(masterKeyId == null
+ ? R.string.specify_backup_dest
+ : R.string.specify_backup_dest_single);
+ }
- FileHelper.saveFile(new FileHelper.FileDialogCallback() {
+ FileHelper.saveDocumentDialog(new FileHelper.FileDialogCallback() {
@Override
public void onFileSelected(File file, boolean checked) {
mExportFile = file;
- exportKeys(masterKeyIds, checked);
+ exportKeys(masterKeyId == null ? null : new long[] { masterKeyId }, exportSecret);
}
- }, mActivity.getSupportFragmentManager() ,title, message, exportFile, checkMsg);
+ }, mActivity.getSupportFragmentManager(), title, message, exportFile, null);
}
+ // TODO: If ExportHelper requires pending data (see CryptoOPerationHelper), activities using
+ // TODO: this class should be able to call mExportOpHelper.handleActivity
+
/**
* Export keys
*/
public void exportKeys(long[] masterKeyIds, boolean exportSecret) {
Log.d(Constants.TAG, "exportKeys started");
+ mExportSecret = exportSecret;
+ mMasterKeyIds = masterKeyIds; // if masterKeyIds is null it means export all
- // Send all information needed to service to export key in other thread
- final Intent intent = new Intent(mActivity, KeychainIntentService.class);
-
- intent.setAction(KeychainIntentService.ACTION_EXPORT_KEYRING);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFile.getAbsolutePath());
- data.putBoolean(KeychainIntentService.EXPORT_SECRET, exportSecret);
-
- if (masterKeyIds == null) {
- data.putBoolean(KeychainIntentService.EXPORT_ALL, true);
- } else {
- data.putLongArray(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, masterKeyIds);
- }
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after exporting is done in KeychainIntentService
- ServiceProgressHandler exportHandler = new ServiceProgressHandler(mActivity,
- mActivity.getString(R.string.progress_exporting),
- ProgressDialog.STYLE_HORIZONTAL,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
+ CryptoOperationHelper<ExportKeyringParcel, ExportResult> exportOpHelper =
+ new CryptoOperationHelper<>(1, mActivity, this, R.string.progress_exporting);
+ exportOpHelper.cryptoOperation();
+ }
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle data = message.getData();
+ @Override
+ public ExportKeyringParcel createOperationInput() {
+ return new ExportKeyringParcel(mMasterKeyIds, mExportSecret, mExportFile.getAbsolutePath());
+ }
- ExportResult result = data.getParcelable(ExportResult.EXTRA_RESULT);
- result.createNotify(mActivity).show();
- }
- }
- };
+ @Override
+ final public void onCryptoOperationSuccess(ExportResult result) {
+ // trigger scan of the created 'media' file so it shows up on MTP
+ // http://stackoverflow.com/questions/13737261/nexus-4-not-showing-files-via-mtp
+ mActivity.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(mExportFile)));
+ result.createNotify(mActivity).show();
+ }
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(exportHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+ @Override
+ public void onCryptoOperationCancelled() {
- // show progress dialog
- exportHandler.showProgressDialog(mActivity);
+ }
- // start service with intent
- mActivity.startService(intent);
+ @Override
+ public void onCryptoOperationError(ExportResult result) {
+ result.createNotify(mActivity).show();
}
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java
index 677acb1b8..9fb362412 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.util;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
@@ -27,55 +28,116 @@ import android.graphics.Bitmap;
import android.graphics.Point;
import android.net.Uri;
import android.os.Build;
+import android.os.Build.VERSION_CODES;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.provider.DocumentsContract;
import android.provider.OpenableColumns;
+import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.widget.Toast;
+import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
import java.text.DecimalFormat;
+
+/** This class offers a number of helper functions for saving documents.
+ *
+ * There are three entry points here: openDocument, saveDocument and
+ * saveDocumentDialog. Each behaves a little differently depending on whether
+ * the Android version used is pre or post KitKat.
+ *
+ * - openDocument queries for a document for reading. Used in "open encrypted
+ * file" ui flow. On pre-kitkat, this relies on an external file manager,
+ * and will fail with a toast message if none is installed.
+ *
+ * - saveDocument queries for a document name for saving. on pre-kitkat, this
+ * shows a dialog where a filename can be input. on kitkat and up, it
+ * directly triggers a "save document" intent. Used in "save encrypted file"
+ * ui flow.
+ *
+ * - saveDocumentDialog queries for a document. this shows a dialog on all
+ * versions of android. the browse button opens an external browser on
+ * pre-kitkat or the "save document" intent on post-kitkat devices. Used in
+ * "backup key" ui flow.
+ *
+ * It is noteworthy that the "saveDocument" call is essentially substituted
+ * by the "saveDocumentDialog" on pre-kitkat devices.
+ *
+ */
public class FileHelper {
- /**
- * Checks if external storage is mounted if file is located on external storage
- *
- * @param file
- * @return true if storage is mounted
- */
- public static boolean isStorageMounted(String file) {
- if (file.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- return false;
- }
+ public static void openDocument(Fragment fragment, Uri last, String mimeType, boolean multiple, int requestCode) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+ openDocumentPreKitKat(fragment, last, mimeType, multiple, requestCode);
+ } else {
+ openDocumentKitKat(fragment, mimeType, multiple, requestCode);
}
+ }
- return true;
+ public static void saveDocument(Fragment fragment, String targetName, Uri inputUri,
+ @StringRes int title, @StringRes int message, int requestCode) {
+ saveDocument(fragment, targetName, inputUri, "*/*", title, message, requestCode);
}
- /**
- * Opens the preferred installed file manager on Android and shows a toast if no manager is
- * installed.
- *
- * @param fragment
- * @param last default selected Uri, not supported by all file managers
- * @param mimeType can be text/plain for example
- * @param requestCode requestCode used to identify the result coming back from file manager to
- * onActivityResult() in your activity
- */
- public static void openFile(Fragment fragment, Uri last, String mimeType, int requestCode) {
+ public static void saveDocument(Fragment fragment, String targetName, Uri inputUri, String mimeType,
+ @StringRes int title, @StringRes int message, int requestCode) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+ saveDocumentDialog(fragment, targetName, inputUri, title, message, requestCode);
+ } else {
+ saveDocumentKitKat(fragment, mimeType, targetName, requestCode);
+ }
+ }
+
+ public static void saveDocumentDialog(final Fragment fragment, String targetName, Uri inputUri,
+ @StringRes int title, @StringRes int message, final int requestCode) {
+
+ saveDocumentDialog(fragment, targetName, inputUri, title, message, new FileDialogCallback() {
+ // is this a good idea? seems hacky...
+ @Override
+ public void onFileSelected(File file, boolean checked) {
+ Intent intent = new Intent();
+ intent.setData(Uri.fromFile(file));
+ fragment.onActivityResult(requestCode, Activity.RESULT_OK, intent);
+ }
+ });
+ }
+
+ public static void saveDocumentDialog(final Fragment fragment, String targetName, Uri inputUri,
+ @StringRes int title, @StringRes int message, FileDialogCallback callback) {
+
+ File file = inputUri == null ? null : new File(inputUri.getPath());
+ File parentDir = file != null && file.exists() ? file.getParentFile() : Constants.Path.APP_DIR;
+ File targetFile = new File(parentDir, targetName);
+ saveDocumentDialog(callback, fragment.getActivity().getSupportFragmentManager(),
+ fragment.getString(title), fragment.getString(message), targetFile, null);
+
+ }
+
+ /** Opens the preferred installed file manager on Android and shows a toast
+ * if no manager is installed. */
+ private static void openDocumentPreKitKat(
+ Fragment fragment, Uri last, String mimeType, boolean multiple, int requestCode) {
+
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
-
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple);
+ }
intent.setData(last);
intent.setType(mimeType);
@@ -86,11 +148,34 @@ public class FileHelper {
Toast.makeText(fragment.getActivity(), R.string.no_filemanager_installed,
Toast.LENGTH_SHORT).show();
}
+
}
- public static void saveFile(final FileDialogCallback callback, final FragmentManager fragmentManager,
- final String title, final String message, final File defaultFile,
- final String checkMsg) {
+ /** Opens the storage browser on Android 4.4 or later for opening a file */
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ private static void openDocumentKitKat(Fragment fragment, String mimeType, boolean multiple, int requestCode) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType(mimeType);
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple);
+ fragment.startActivityForResult(intent, requestCode);
+ }
+
+ /** Opens the storage browser on Android 4.4 or later for saving a file. */
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ public static void saveDocumentKitKat(Fragment fragment, String mimeType, String suggestedName, int requestCode) {
+ Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType(mimeType);
+ intent.putExtra("android.content.extra.SHOW_ADVANCED", true); // Note: This is not documented, but works
+ intent.putExtra(Intent.EXTRA_TITLE, suggestedName);
+ fragment.startActivityForResult(intent, requestCode);
+ }
+
+ public static void saveDocumentDialog(
+ final FileDialogCallback callback, final FragmentManager fragmentManager,
+ final String title, final String message, final File defaultFile,
+ final String checkMsg) {
// Message is received after file is selected
Handler returnHandler = new Handler() {
@Override
@@ -117,63 +202,6 @@ public class FileHelper {
});
}
- public static void saveFile(Fragment fragment, String title, String message, File defaultFile, int requestCode) {
- saveFile(fragment, title, message, defaultFile, requestCode, null);
- }
-
- public static void saveFile(final Fragment fragment, String title, String message, File defaultFile,
- final int requestCode, String checkMsg) {
- saveFile(new FileDialogCallback() {
- @Override
- public void onFileSelected(File file, boolean checked) {
- Intent intent = new Intent();
- intent.setData(Uri.fromFile(file));
- fragment.onActivityResult(requestCode, Activity.RESULT_OK, intent);
- }
- }, fragment.getActivity().getSupportFragmentManager(), title, message, defaultFile, checkMsg);
- }
-
- @TargetApi(Build.VERSION_CODES.KITKAT)
- public static void openDocument(Fragment fragment, String mimeType, int requestCode) {
- openDocument(fragment, mimeType, false, requestCode);
- }
-
- /**
- * Opens the storage browser on Android 4.4 or later for opening a file
- *
- * @param fragment
- * @param mimeType can be text/plain for example
- * @param multiple allow file chooser to return multiple files
- * @param requestCode used to identify the result coming back from storage browser onActivityResult() in your
- */
- @TargetApi(Build.VERSION_CODES.KITKAT)
- public static void openDocument(Fragment fragment, String mimeType, boolean multiple, int requestCode) {
- Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setType(mimeType);
- intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple);
-
- fragment.startActivityForResult(intent, requestCode);
- }
-
- /**
- * Opens the storage browser on Android 4.4 or later for saving a file
- *
- * @param fragment
- * @param mimeType can be text/plain for example
- * @param suggestedName a filename desirable for the file to be saved
- * @param requestCode used to identify the result coming back from storage browser onActivityResult() in your
- */
- @TargetApi(Build.VERSION_CODES.KITKAT)
- public static void saveDocument(Fragment fragment, String mimeType, String suggestedName, int requestCode) {
- Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setType(mimeType);
- intent.putExtra("android.content.extra.SHOW_ADVANCED", true); // Note: This is not documented, but works
- intent.putExtra(Intent.EXTRA_TITLE, suggestedName);
- fragment.startActivityForResult(intent, requestCode);
- }
-
public static String getFilename(Context context, Uri uri) {
String filename = null;
try {
@@ -234,7 +262,78 @@ public class FileHelper {
return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
}
- public static interface FileDialogCallback {
- public void onFileSelected(File file, boolean checked);
+ public static String readTextFromUri(Context context, Uri outputUri, String charset)
+ throws IOException {
+
+ byte[] decryptedMessage;
+ {
+ InputStream in = context.getContentResolver().openInputStream(outputUri);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] buf = new byte[256];
+ int read;
+ while ( (read = in.read(buf)) > 0) {
+ out.write(buf, 0, read);
+ }
+ in.close();
+ out.close();
+ decryptedMessage = out.toByteArray();
+ }
+
+ String plaintext;
+ if (charset != null) {
+ try {
+ plaintext = new String(decryptedMessage, charset);
+ } catch (UnsupportedEncodingException e) {
+ // if we can't decode properly, just fall back to utf-8
+ plaintext = new String(decryptedMessage);
+ }
+ } else {
+ plaintext = new String(decryptedMessage);
+ }
+
+ return plaintext;
+
+ }
+
+ public static void copyUriData(Context context, Uri fromUri, Uri toUri) throws IOException {
+ BufferedInputStream bis = null;
+ BufferedOutputStream bos = null;
+
+ try {
+ ContentResolver resolver = context.getContentResolver();
+ bis = new BufferedInputStream(resolver.openInputStream(fromUri));
+ bos = new BufferedOutputStream(resolver.openOutputStream(toUri));
+ byte[] buf = new byte[1024];
+ int len;
+ while ( (len = bis.read(buf)) > 0) {
+ bos.write(buf, 0, len);
+ }
+ } finally {
+ try {
+ if (bis != null) {
+ bis.close();
+ }
+ if (bos != null) {
+ bos.close();
+ }
+ } catch (IOException e) {
+ // ignore, it's just stream closin'
+ }
+ }
+ }
+
+ /** Checks if external storage is mounted if file is located on external storage. */
+ public static boolean isStorageMounted(String file) {
+ if (file.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public interface FileDialogCallback {
+ void onFileSelected(File file, boolean checked);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyUpdateHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyUpdateHelper.java
index 3bbd86d6a..8a614d64d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyUpdateHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyUpdateHelper.java
@@ -51,15 +51,15 @@ public class KeyUpdateHelper {
}
// Start the service and update the keys
- Intent importIntent = new Intent(mContext, KeychainIntentService.class);
- importIntent.setAction(KeychainIntentService.ACTION_DOWNLOAD_AND_IMPORT_KEYS);
+ Intent importIntent = new Intent(mContext, KeychainService.class);
+ importIntent.setAction(KeychainService.ACTION_DOWNLOAD_AND_IMPORT_KEYS);
Bundle importData = new Bundle();
- importData.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST,
+ importData.putParcelableArrayList(KeychainService.DOWNLOAD_KEY_LIST,
new ArrayList<ImportKeysListEntry>(keys));
- importIntent.putExtra(KeychainIntentService.EXTRA_SERVICE_INTENT, importData);
+ importIntent.putExtra(KeychainService.EXTRA_SERVICE_INTENT, importData);
- importIntent.putExtra(KeychainIntentService.EXTRA_MESSENGER, new Messenger(mHandler));
+ importIntent.putExtra(KeychainService.EXTRA_MESSENGER, new Messenger(mHandler));
mContext.startService(importIntent);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/OrientationUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/OrientationUtils.java
new file mode 100644
index 000000000..43ed12429
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/OrientationUtils.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.util;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.view.Display;
+import android.view.Surface;
+import android.view.WindowManager;
+
+/**
+ * Static methods related to device orientation.
+ */
+public class OrientationUtils {
+
+ /**
+ * Locks the device window in landscape mode.
+ */
+ public static void lockOrientationLandscape(Activity activity) {
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ }
+
+ /**
+ * Locks the device window in portrait mode.
+ */
+ public static void lockOrientationPortrait(Activity activity) {
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
+
+ /**
+ * Locks the device window in actual screen mode.
+ */
+ public static void lockOrientation(Activity activity) {
+ Display display = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE))
+ .getDefaultDisplay();
+ int rotation = display.getRotation();
+ int tempOrientation = activity.getResources().getConfiguration().orientation;
+ int orientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+ switch (tempOrientation) {
+ case Configuration.ORIENTATION_LANDSCAPE: {
+ if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90) {
+ orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+ } else {
+ orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+ }
+ break;
+ }
+ case Configuration.ORIENTATION_PORTRAIT: {
+ if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_270) {
+ orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+ } else {
+ orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
+ }
+ break;
+ }
+ }
+ activity.setRequestedOrientation(orientation);
+ }
+
+ /**
+ * Unlocks the device window in user defined screen mode.
+ */
+ public static void unlockOrientation(Activity activity) {
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
+ }
+
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableFileCache.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableFileCache.java
index 5a314ad0b..eabbf83b8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableFileCache.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableFileCache.java
@@ -66,21 +66,24 @@ public class ParcelableFileCache<E extends Parcelable> {
File tempFile = new File(mContext.getCacheDir(), mFilename);
- DataOutputStream oos = new DataOutputStream(new FileOutputStream(tempFile));
- oos.writeInt(numEntries);
+ DataOutputStream oos = new DataOutputStream(new FileOutputStream(tempFile));
- while (it.hasNext()) {
- Parcel p = Parcel.obtain(); // creating empty parcel object
- p.writeParcelable(it.next(), 0); // saving bundle as parcel
- byte[] buf = p.marshall();
- oos.writeInt(buf.length);
- oos.write(buf);
- p.recycle();
+ try {
+ oos.writeInt(numEntries);
+
+ while (it.hasNext()) {
+ Parcel p = Parcel.obtain(); // creating empty parcel object
+ p.writeParcelable(it.next(), 0); // saving bundle as parcel
+ byte[] buf = p.marshall();
+ oos.writeInt(buf.length);
+ oos.write(buf);
+ p.recycle();
+ }
+ } finally {
+ oos.close();
}
- oos.close();
-
}
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableHashMap.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableHashMap.java
new file mode 100644
index 000000000..fa4081acc
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableHashMap.java
@@ -0,0 +1,63 @@
+package org.sufficientlysecure.keychain.util;
+
+
+import java.util.HashMap;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
+
+import org.sufficientlysecure.keychain.KeychainApplication;
+
+
+public class ParcelableHashMap <K extends Parcelable, V extends Parcelable> implements Parcelable {
+
+ HashMap<K,V> mInner;
+
+ public ParcelableHashMap(HashMap<K,V> inner) {
+ mInner = inner;
+ }
+
+ protected ParcelableHashMap(@NonNull Parcel in) {
+ mInner = new HashMap<>();
+ ClassLoader loader = KeychainApplication.class.getClassLoader();
+
+ int num = in.readInt();
+ while (num-- > 0) {
+ K key = in.readParcelable(loader);
+ V val = in.readParcelable(loader);
+ mInner.put(key, val);
+ }
+ }
+
+ public HashMap<K,V> getMap() {
+ return mInner;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mInner.size());
+ for (HashMap.Entry<K,V> entry : mInner.entrySet()) {
+ parcel.writeParcelable(entry.getKey(), 0);
+ parcel.writeParcelable(entry.getValue(), 0);
+ }
+ }
+
+ public static final Creator<ParcelableHashMap> CREATOR = new Creator<ParcelableHashMap>() {
+ @Override
+ public ParcelableHashMap createFromParcel(Parcel in) {
+ return new ParcelableHashMap(in);
+ }
+
+ @Override
+ public ParcelableHashMap[] newArray(int size) {
+ return new ParcelableHashMap[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableProxy.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableProxy.java
new file mode 100644
index 000000000..7e788d04c
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableProxy.java
@@ -0,0 +1,91 @@
+/*
+ * 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.util;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+
+/**
+ * used to simply transport java.net.Proxy objects created using InetSockets between services/activities
+ */
+public class ParcelableProxy implements Parcelable {
+ private String mProxyHost;
+ private int mProxyPort;
+ private Proxy.Type mProxyType;
+
+ public ParcelableProxy(String hostName, int port, Proxy.Type type) {
+ mProxyHost = hostName;
+
+ if (hostName == null) {
+ return; // represents a null proxy
+ }
+
+ mProxyPort = port;
+
+ mProxyType = type;
+ }
+
+ public static ParcelableProxy getForNoProxy() {
+ return new ParcelableProxy(null, -1, null);
+ }
+
+ public Proxy getProxy() {
+ if (mProxyHost == null) {
+ return null;
+ }
+ /*
+ * InetSocketAddress.createUnresolved so we can use this method even in the main thread
+ * (no network call)
+ */
+ return new Proxy(mProxyType, InetSocketAddress.createUnresolved(mProxyHost, mProxyPort));
+ }
+
+ protected ParcelableProxy(Parcel in) {
+ mProxyHost = in.readString();
+ mProxyPort = in.readInt();
+ mProxyType = (Proxy.Type) in.readSerializable();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mProxyHost);
+ dest.writeInt(mProxyPort);
+ dest.writeSerializable(mProxyType);
+ }
+
+ @SuppressWarnings("unused")
+ public static final Parcelable.Creator<ParcelableProxy> CREATOR = new Parcelable.Creator<ParcelableProxy>() {
+ @Override
+ public ParcelableProxy createFromParcel(Parcel in) {
+ return new ParcelableProxy(in);
+ }
+
+ @Override
+ public ParcelableProxy[] newArray(int size) {
+ return new ParcelableProxy[size];
+ }
+ };
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java
index 06efdde4d..fe42c7a2c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java
@@ -117,6 +117,13 @@ public class Passphrase implements Parcelable {
}
}
+ /**
+ * Creates a new String from the char[]. This is considered unsafe!
+ */
+ public String toStringUnsafe() {
+ return new String(mPassphrase);
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -127,11 +134,7 @@ public class Passphrase implements Parcelable {
}
Passphrase that = (Passphrase) o;
- if (!Arrays.equals(mPassphrase, that.mPassphrase)) {
- return false;
- }
-
- return true;
+ return Arrays.equals(mPassphrase, that.mPassphrase);
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
index 303687315..4ef215036 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
@@ -21,9 +21,13 @@ package org.sufficientlysecure.keychain.util;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.preference.PreferenceManager;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Constants.Pref;
+import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService;
+import java.net.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.ListIterator;
@@ -35,6 +39,10 @@ import java.util.Vector;
public class Preferences {
private static Preferences sPreferences;
private SharedPreferences mSharedPreferences;
+ private Resources mResources;
+
+ private static String PREF_FILE_NAME = "APG.main";
+ private static int PREF_FILE_MODE = Context.MODE_MULTI_PROCESS;
public static synchronized Preferences getPreferences(Context context) {
return getPreferences(context, false);
@@ -51,12 +59,18 @@ public class Preferences {
}
private Preferences(Context context) {
+ mResources = context.getResources();
updateSharedPreferences(context);
}
+ public static void setPreferenceManagerFileAndMode(PreferenceManager manager) {
+ manager.setSharedPreferencesName(PREF_FILE_NAME);
+ manager.setSharedPreferencesMode(PREF_FILE_MODE);
+ }
+
public void updateSharedPreferences(Context context) {
// multi-process safe preferences
- mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_MULTI_PROCESS);
+ mSharedPreferences = context.getSharedPreferences(PREF_FILE_NAME, PREF_FILE_MODE);
}
public String getLanguage() {
@@ -138,6 +152,9 @@ public class Preferences {
public String[] getKeyServers() {
String rawData = mSharedPreferences.getString(Constants.Pref.KEY_SERVERS,
Constants.Defaults.KEY_SERVERS);
+ if (rawData.equals("")) {
+ return new String[0];
+ }
Vector<String> servers = new Vector<>();
String chunks[] = rawData.split(",");
for (String c : chunks) {
@@ -150,7 +167,8 @@ public class Preferences {
}
public String getPreferredKeyserver() {
- return getKeyServers()[0];
+ String[] keyservers = getKeyServers();
+ return keyservers.length == 0 ? null : keyservers[0];
}
public void setKeyServers(String[] value) {
@@ -182,6 +200,142 @@ public class Preferences {
editor.commit();
}
+ public void setFilesUseCompression(boolean compress) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(Pref.FILE_USE_COMPRESSION, compress);
+ editor.commit();
+ }
+
+ public boolean getFilesUseCompression() {
+ return mSharedPreferences.getBoolean(Pref.FILE_USE_COMPRESSION, true);
+ }
+
+ public void setTextUseCompression(boolean compress) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(Pref.TEXT_USE_COMPRESSION, compress);
+ editor.commit();
+ }
+
+ public boolean getTextUseCompression() {
+ return mSharedPreferences.getBoolean(Pref.TEXT_USE_COMPRESSION, true);
+ }
+
+ public String getTheme() {
+ return mSharedPreferences.getString(Pref.THEME, Pref.Theme.LIGHT);
+ }
+
+ public void setTheme(String value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putString(Constants.Pref.THEME, value);
+ editor.commit();
+ }
+
+ public void setUseArmor(boolean useArmor) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(Pref.USE_ARMOR, useArmor);
+ editor.commit();
+ }
+
+ public boolean getUseArmor() {
+ return mSharedPreferences.getBoolean(Pref.USE_ARMOR, false);
+ }
+
+ public void setEncryptFilenames(boolean encryptFilenames) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(Pref.ENCRYPT_FILENAMES, encryptFilenames);
+ editor.commit();
+ }
+
+ public boolean getEncryptFilenames() {
+ return mSharedPreferences.getBoolean(Pref.ENCRYPT_FILENAMES, true);
+ }
+
+ // proxy preference functions start here
+
+ public boolean getUseNormalProxy() {
+ return mSharedPreferences.getBoolean(Constants.Pref.USE_NORMAL_PROXY, false);
+ }
+
+ public boolean getUseTorProxy() {
+ return mSharedPreferences.getBoolean(Constants.Pref.USE_TOR_PROXY, false);
+ }
+
+ public String getProxyHost() {
+ return mSharedPreferences.getString(Constants.Pref.PROXY_HOST, null);
+ }
+
+ /**
+ * we store port as String for easy interfacing with EditTextPreference, but return it as an integer
+ *
+ * @return port number of proxy
+ */
+ public int getProxyPort() {
+ return Integer.parseInt(mSharedPreferences.getString(Pref.PROXY_PORT, "-1"));
+ }
+
+ /**
+ * we store port as String for easy interfacing with EditTextPreference, but return it as an integer
+ *
+ * @param port proxy port
+ */
+ public void setProxyPort(String port) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putString(Pref.PROXY_PORT, port);
+ editor.commit();
+ }
+
+ public Proxy.Type getProxyType() {
+ final String typeHttp = Pref.ProxyType.TYPE_HTTP;
+ final String typeSocks = Pref.ProxyType.TYPE_SOCKS;
+
+ String type = mSharedPreferences.getString(Pref.PROXY_TYPE, typeHttp);
+
+ switch (type) {
+ case typeHttp:
+ return Proxy.Type.HTTP;
+ case typeSocks:
+ return Proxy.Type.SOCKS;
+ default: // shouldn't happen
+ Log.e(Constants.TAG, "Invalid Proxy Type in preferences");
+ return null;
+ }
+ }
+
+ public ProxyPrefs getProxyPrefs() {
+ boolean useTor = getUseTorProxy();
+ boolean useNormalProxy = getUseNormalProxy();
+
+ if (useTor) {
+ return new ProxyPrefs(true, false, Constants.Orbot.PROXY_HOST, Constants.Orbot.PROXY_PORT,
+ Constants.Orbot.PROXY_TYPE);
+ } else if (useNormalProxy) {
+ return new ProxyPrefs(false, true, getProxyHost(), getProxyPort(), getProxyType());
+ } else {
+ return new ProxyPrefs(false, false, null, -1, null);
+ }
+ }
+
+ public static class ProxyPrefs {
+ public final ParcelableProxy parcelableProxy;
+ public final boolean torEnabled;
+ public final boolean normalPorxyEnabled;
+
+ /**
+ * torEnabled and normalProxyEnabled are not expected to both be true
+ *
+ * @param torEnabled if Tor is to be used
+ * @param normalPorxyEnabled if user-specified proxy is to be used
+ */
+ public ProxyPrefs(boolean torEnabled, boolean normalPorxyEnabled, String hostName, int port, Proxy.Type type) {
+ this.torEnabled = torEnabled;
+ this.normalPorxyEnabled = normalPorxyEnabled;
+ if (!torEnabled && !normalPorxyEnabled) this.parcelableProxy = new ParcelableProxy(null, -1, null);
+ else this.parcelableProxy = new ParcelableProxy(hostName, port, type);
+ }
+ }
+
+ // cloud prefs
+
public CloudSearchPrefs getCloudSearchPrefs() {
return new CloudSearchPrefs(mSharedPreferences.getBoolean(Pref.SEARCH_KEYSERVER, true),
mSharedPreferences.getBoolean(Pref.SEARCH_KEYBASE, true),
@@ -205,7 +359,39 @@ public class Preferences {
}
}
- public void updatePreferences() {
+ // experimental prefs
+
+ public void setExperimentalEnableWordConfirm(boolean enableWordConfirm) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(Pref.EXPERIMENTAL_ENABLE_WORD_CONFIRM, enableWordConfirm);
+ editor.commit();
+ }
+
+ public boolean getExperimentalEnableWordConfirm() {
+ return mSharedPreferences.getBoolean(Pref.EXPERIMENTAL_ENABLE_WORD_CONFIRM, false);
+ }
+
+ public void setExperimentalEnableLinkedIdentities(boolean enableLinkedIdentities) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(Pref.EXPERIMENTAL_ENABLE_LINKED_IDENTITIES, enableLinkedIdentities);
+ editor.commit();
+ }
+
+ public boolean getExperimentalEnableLinkedIdentities() {
+ return mSharedPreferences.getBoolean(Pref.EXPERIMENTAL_ENABLE_LINKED_IDENTITIES, false);
+ }
+
+ public void setExperimentalEnableKeybase(boolean enableKeybase) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(Pref.EXPERIMENTAL_ENABLE_KEYBASE, enableKeybase);
+ editor.commit();
+ }
+
+ public boolean getExperimentalEnableKeybase() {
+ return mSharedPreferences.getBoolean(Pref.EXPERIMENTAL_ENABLE_KEYBASE, false);
+ }
+
+ public void upgradePreferences(Context context) {
if (mSharedPreferences.getInt(Constants.Pref.PREF_DEFAULT_VERSION, 0) !=
Constants.Defaults.PREF_VERSION) {
switch (mSharedPreferences.getInt(Constants.Pref.PREF_DEFAULT_VERSION, 0)) {
@@ -239,6 +425,14 @@ public class Preferences {
}
// fall through
case 4: {
+ setTheme(Constants.Pref.Theme.DEFAULT);
+ }
+ // fall through
+ case 5: {
+ KeyserverSyncAdapterService.enableKeyserverSync(context);
+ }
+ // fall through
+ case 6: {
}
}
@@ -248,4 +442,9 @@ public class Preferences {
.commit();
}
}
+
+ public void clear() {
+ mSharedPreferences.edit().clear().commit();
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareHelper.java
index 120b84a3b..0297d149c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ShareHelper.java
@@ -91,6 +91,7 @@ public class ShareHelper {
// Create chooser with only one Intent in it
Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), title);
// append all other Intents
+ // TODO this line looks wrong?!
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[]{}));
return chooserIntent;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/TlsHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/TlsHelper.java
index 4ff14e3bb..d1d1ada2a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/TlsHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/TlsHelper.java
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.util;
import android.content.res.AssetManager;
+import com.squareup.okhttp.OkHttpClient;
import org.sufficientlysecure.keychain.Constants;
import java.io.ByteArrayInputStream;
@@ -26,7 +27,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
-import java.net.URLConnection;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
@@ -61,7 +61,7 @@ public class TlsHelper {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int reads = is.read();
- while(reads != -1){
+ while (reads != -1) {
baos.write(reads);
reads = is.read();
}
@@ -74,15 +74,56 @@ public class TlsHelper {
}
}
- public static URLConnection openConnection(URL url) throws IOException, TlsHelperException {
+ public static void pinCertificateIfNecessary(OkHttpClient client, URL url) throws TlsHelperException, IOException {
if (url.getProtocol().equals("https")) {
for (String domain : sStaticCA.keySet()) {
if (url.getHost().endsWith(domain)) {
- return openCAConnection(sStaticCA.get(domain), url);
+ pinCertificate(sStaticCA.get(domain), client);
}
}
}
- return url.openConnection();
+ }
+
+ /**
+ * Modifies the client to accept only requests with a given certificate. Applies to all URLs requested by the
+ * client.
+ * Therefore a client that is pinned this way should be used to only make requests to URLs with passed certificate.
+ * TODO: Refactor - More like SSH StrictHostKeyChecking than pinning?
+ *
+ * @param certificate certificate to pin
+ * @param client OkHttpClient to enforce pinning on
+ * @throws TlsHelperException
+ * @throws IOException
+ */
+ private static void pinCertificate(byte[] certificate, OkHttpClient client)
+ throws TlsHelperException, IOException {
+ // We don't use OkHttp's CertificatePinner since it depends on a TrustManager to verify it too. Refer to
+ // note at end of description: http://square.github.io/okhttp/javadoc/com/squareup/okhttp/CertificatePinner.html
+ // Creating our own TrustManager that trusts only our certificate eliminates the need for certificate pinning
+ try {
+ // Load CA
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ Certificate ca = cf.generateCertificate(new ByteArrayInputStream(certificate));
+
+ // Create a KeyStore containing our trusted CAs
+ String keyStoreType = KeyStore.getDefaultType();
+ KeyStore keyStore = KeyStore.getInstance(keyStoreType);
+ keyStore.load(null, null);
+ keyStore.setCertificateEntry("ca", ca);
+
+ // Create a TrustManager that trusts the CAs in our KeyStore
+ String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
+ tmf.init(keyStore);
+
+ // Create an SSLContext that uses our TrustManager
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, tmf.getTrustManagers(), null);
+
+ client.setSslSocketFactory(context.getSocketFactory());
+ } catch (CertificateException | KeyStoreException | KeyManagementException | NoSuchAlgorithmException e) {
+ throw new TlsHelperException(e);
+ }
}
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java
new file mode 100644
index 000000000..d85ad9128
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java
@@ -0,0 +1,463 @@
+/* This is the license for Orlib, a free software project to
+ provide anonymity on the Internet from a Google Android smartphone.
+
+ For more information about Orlib, see https://guardianproject.info/
+
+ If you got this file as a part of a larger bundle, there may be other
+ license terms that you should be aware of.
+ ===============================================================================
+ Orlib is distributed under this license (aka the 3-clause BSD license)
+
+ Copyright (c) 2009-2010, Nathan Freitas, The Guardian Project
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+
+ * Neither the names of the copyright owners nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ *****
+ Orlib contains a binary distribution of the JSocks library:
+ http://code.google.com/p/jsocks-mirror/
+ which is licensed under the GNU Lesser General Public License:
+ http://www.gnu.org/licenses/lgpl.html
+
+ *****
+*/
+
+package org.sufficientlysecure.keychain.util.orbot;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentActivity;
+import android.text.TextUtils;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.dialog.SupportInstallDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.OrbotStartDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.PreferenceInstallDialogFragment;
+import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Preferences;
+
+import java.util.List;
+
+/**
+ * This class is taken from the NetCipher library: https://github.com/guardianproject/NetCipher/
+ */
+public class OrbotHelper {
+
+ public interface DialogActions {
+ void onOrbotStarted();
+
+ void onNeutralButton();
+
+ void onCancel();
+ }
+
+ private final static int REQUEST_CODE_STATUS = 100;
+
+ public final static String ORBOT_PACKAGE_NAME = "org.torproject.android";
+ public final static String ORBOT_MARKET_URI = "market://details?id=" + ORBOT_PACKAGE_NAME;
+ public final static String ORBOT_FDROID_URI = "https://f-droid.org/repository/browse/?fdid="
+ + ORBOT_PACKAGE_NAME;
+ public final static String ORBOT_PLAY_URI = "https://play.google.com/store/apps/details?id="
+ + ORBOT_PACKAGE_NAME;
+
+ /**
+ * A request to Orbot to transparently start Tor services
+ */
+ public final static String ACTION_START = "org.torproject.android.intent.action.START";
+ /**
+ * {@link Intent} send by Orbot with {@code ON/OFF/STARTING/STOPPING} status
+ */
+ public final static String ACTION_STATUS = "org.torproject.android.intent.action.STATUS";
+ /**
+ * {@code String} that contains a status constant: {@link #STATUS_ON},
+ * {@link #STATUS_OFF}, {@link #STATUS_STARTING}, or
+ * {@link #STATUS_STOPPING}
+ */
+ public final static String EXTRA_STATUS = "org.torproject.android.intent.extra.STATUS";
+ /**
+ * A {@link String} {@code packageName} for Orbot to direct its status reply
+ * to, used in {@link #ACTION_START} {@link Intent}s sent to Orbot
+ */
+ public final static String EXTRA_PACKAGE_NAME = "org.torproject.android.intent.extra.PACKAGE_NAME";
+
+ /**
+ * All tor-related services and daemons are stopped
+ */
+ @SuppressWarnings("unused") // we might use this later, sent by Orbot
+ public final static String STATUS_OFF = "OFF";
+ /**
+ * All tor-related services and daemons have completed starting
+ */
+ public final static String STATUS_ON = "ON";
+ @SuppressWarnings("unused") // we might use this later, sent by Orbot
+ public final static String STATUS_STARTING = "STARTING";
+ @SuppressWarnings("unused") // we might use this later, sent by Orbot
+ public final static String STATUS_STOPPING = "STOPPING";
+ /**
+ * The user has disabled the ability for background starts triggered by
+ * apps. Fallback to the old Intent that brings up Orbot.
+ */
+ public final static String STATUS_STARTS_DISABLED = "STARTS_DISABLED";
+
+ public final static String ACTION_START_TOR = "org.torproject.android.START_TOR";
+ /**
+ * request code used to start tor
+ */
+ public final static int START_TOR_RESULT = 0x9234;
+
+ private final static String FDROID_PACKAGE_NAME = "org.fdroid.fdroid";
+ private final static String PLAY_PACKAGE_NAME = "com.android.vending";
+
+ private OrbotHelper() {
+ // only static utility methods, do not instantiate
+ }
+
+ public static boolean isOrbotRunning(Context context) {
+ int procId = TorServiceUtils.findProcessId(context);
+
+ return (procId != -1);
+ }
+
+ public static boolean isOrbotInstalled(Context context) {
+ return isAppInstalled(context, ORBOT_PACKAGE_NAME);
+ }
+
+ private static boolean isAppInstalled(Context context, String uri) {
+ try {
+ PackageManager pm = context.getPackageManager();
+ pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);
+ return true;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ /**
+ * First, checks whether Orbot is installed, then checks whether Orbot is
+ * running. If Orbot is installed and not running, then an {@link Intent} is
+ * sent to request Orbot to start, which will show the main Orbot screen.
+ * The result will be returned in
+ * {@link Activity#onActivityResult(int requestCode, int resultCode, Intent data)}
+ * with a {@code requestCode} of {@code START_TOR_RESULT}
+ *
+ * @param activity the {@link Activity} that gets the
+ * {@code START_TOR_RESULT} result
+ * @return whether the start request was sent to Orbot
+ */
+ public static boolean requestShowOrbotStart(Activity activity) {
+ if (OrbotHelper.isOrbotInstalled(activity)) {
+ if (!OrbotHelper.isOrbotRunning(activity)) {
+ Intent intent = getShowOrbotStartIntent();
+ activity.startActivityForResult(intent, START_TOR_RESULT);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static Intent getShowOrbotStartIntent() {
+ Intent intent = new Intent(ACTION_START_TOR);
+ intent.setPackage(ORBOT_PACKAGE_NAME);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
+ }
+
+ /**
+ * First, checks whether Orbot is installed. If Orbot is installed, then a
+ * broadcast {@link Intent} is sent to request Orbot to start transparently
+ * in the background. When Orbot receives this {@code Intent}, it will
+ * immediately reply to this all with its status via an
+ * {@link #ACTION_STATUS} {@code Intent} that is broadcast to the
+ * {@code packageName} of the provided {@link Context} (i.e.
+ * {@link Context#getPackageName()}.
+ *
+ * @param context the app {@link Context} will receive the reply
+ * @return whether the start request was sent to Orbot
+ */
+ public static boolean requestStartTor(Context context) {
+ if (OrbotHelper.isOrbotInstalled(context)) {
+ Log.i("OrbotHelper", "requestStartTor " + context.getPackageName());
+ Intent intent = getOrbotStartIntent();
+ intent.putExtra(EXTRA_PACKAGE_NAME, context.getPackageName());
+ context.sendBroadcast(intent);
+ return true;
+ }
+ return false;
+ }
+
+ public static Intent getOrbotStartIntent() {
+ Intent intent = new Intent(ACTION_START);
+ intent.setPackage(ORBOT_PACKAGE_NAME);
+ return intent;
+ }
+
+ public static Intent getOrbotInstallIntent(Context context) {
+ final Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(ORBOT_MARKET_URI));
+
+ PackageManager pm = context.getPackageManager();
+ List<ResolveInfo> resInfos = pm.queryIntentActivities(intent, 0);
+
+ String foundPackageName = null;
+ for (ResolveInfo r : resInfos) {
+ Log.i("OrbotHelper", "market: " + r.activityInfo.packageName);
+ if (TextUtils.equals(r.activityInfo.packageName, FDROID_PACKAGE_NAME)
+ || TextUtils.equals(r.activityInfo.packageName, PLAY_PACKAGE_NAME)) {
+ foundPackageName = r.activityInfo.packageName;
+ break;
+ }
+ }
+
+ if (foundPackageName == null) {
+ intent.setData(Uri.parse(ORBOT_FDROID_URI));
+ } else {
+ intent.setPackage(foundPackageName);
+ }
+ return intent;
+ }
+
+ /**
+ * hack to get around the fact that PreferenceActivity still supports only android.app.DialogFragment
+ */
+ public static android.app.DialogFragment getPreferenceInstallDialogFragment() {
+ return PreferenceInstallDialogFragment.newInstance(R.string.orbot_install_dialog_title,
+ R.string.orbot_install_dialog_content, ORBOT_PACKAGE_NAME);
+ }
+
+ public static DialogFragment getInstallDialogFragmentWithThirdButton(Messenger messenger, int middleButton) {
+ return SupportInstallDialogFragment.newInstance(messenger, R.string.orbot_install_dialog_title,
+ R.string.orbot_install_dialog_content, ORBOT_PACKAGE_NAME, middleButton, true);
+ }
+
+ public static DialogFragment getOrbotStartDialogFragment(Messenger messenger, int middleButton) {
+ return OrbotStartDialogFragment.newInstance(messenger, R.string.orbot_start_dialog_title, R.string
+ .orbot_start_dialog_content,
+ middleButton);
+ }
+
+ /**
+ * checks preferences to see if Orbot is required, and if yes, if it is installed and running
+ *
+ * @param context used to retrieve preferences
+ * @return false if Tor is selected proxy and Orbot is not installed or running, true
+ * otherwise
+ */
+ public static boolean isOrbotInRequiredState(Context context) {
+ Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(context).getProxyPrefs();
+ if (!proxyPrefs.torEnabled) {
+ return true;
+ } else if (!OrbotHelper.isOrbotInstalled(context) || !OrbotHelper.isOrbotRunning(context)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * checks if Tor is enabled and if it is, that Orbot is installed and running. Generates appropriate dialogs.
+ *
+ * @param middleButton resourceId of string to display as the middle button of install and enable dialogs
+ * @param proxyPrefs proxy preferences used to determine if Tor is required to be started
+ * @return true if Tor is not enabled or Tor is enabled and Orbot is installed and running, else false
+ */
+ public static boolean putOrbotInRequiredState(final int middleButton,
+ final DialogActions dialogActions,
+ Preferences.ProxyPrefs proxyPrefs,
+ final FragmentActivity fragmentActivity) {
+
+ if (!proxyPrefs.torEnabled) {
+ return true;
+ }
+
+ if (!OrbotHelper.isOrbotInstalled(fragmentActivity)) {
+ Handler ignoreTorHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SupportInstallDialogFragment.MESSAGE_MIDDLE_CLICKED:
+ dialogActions.onNeutralButton();
+ break;
+ case SupportInstallDialogFragment.MESSAGE_DIALOG_DISMISSED:
+ // both install and cancel buttons mean we don't go ahead with an
+ // operation, so it's okay to cancel
+ dialogActions.onCancel();
+ break;
+ }
+ }
+ };
+
+ OrbotHelper.getInstallDialogFragmentWithThirdButton(
+ new Messenger(ignoreTorHandler),
+ middleButton
+ ).show(fragmentActivity.getSupportFragmentManager(), "OrbotHelperOrbotInstallDialog");
+
+ return false;
+ } else if (!OrbotHelper.isOrbotRunning(fragmentActivity)) {
+
+ final Handler dialogHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case OrbotStartDialogFragment.MESSAGE_MIDDLE_BUTTON:
+ dialogActions.onNeutralButton();
+ break;
+ case OrbotStartDialogFragment.MESSAGE_DIALOG_CANCELLED:
+ dialogActions.onCancel();
+ break;
+ case OrbotStartDialogFragment.MESSAGE_ORBOT_STARTED:
+ dialogActions.onOrbotStarted();
+ break;
+ }
+ }
+ };
+
+ new SilentStartManager() {
+
+ @Override
+ protected void onOrbotStarted() {
+ dialogActions.onOrbotStarted();
+ }
+
+ @Override
+ protected void onSilentStartDisabled() {
+ getOrbotStartDialogFragment(new Messenger(dialogHandler), middleButton)
+ .show(fragmentActivity.getSupportFragmentManager(),
+ "OrbotHelperOrbotStartDialog");
+ }
+ }.startOrbotAndListen(fragmentActivity, true);
+
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ public static boolean putOrbotInRequiredState(DialogActions dialogActions,
+ FragmentActivity fragmentActivity) {
+ return putOrbotInRequiredState(R.string.orbot_ignore_tor,
+ dialogActions,
+ Preferences.getPreferences(fragmentActivity).getProxyPrefs(),
+ fragmentActivity);
+ }
+
+ /**
+ * will attempt a silent start, which if disabled will fallback to the
+ * {@link #requestShowOrbotStart(Activity) requestShowOrbotStart} method, which returns the
+ * result in {@link Activity#onActivityResult(int requestCode, int resultCode, Intent data)}
+ * with a {@code requestCode} of {@code START_TOR_RESULT}, which will have to be implemented by
+ * activities wishing to respond to a change in Orbot state.
+ */
+ public static void bestPossibleOrbotStart(final DialogActions dialogActions,
+ final Activity activity,
+ boolean showProgress) {
+ new SilentStartManager() {
+
+ @Override
+ protected void onOrbotStarted() {
+ dialogActions.onOrbotStarted();
+ }
+
+ @Override
+ protected void onSilentStartDisabled() {
+ requestShowOrbotStart(activity);
+ }
+ }.startOrbotAndListen(activity, showProgress);
+ }
+
+ /**
+ * base class for listening to silent orbot starts. Also handles display of progress dialog.
+ */
+ public static abstract class SilentStartManager {
+
+ private ProgressDialog mProgressDialog;
+
+ public void startOrbotAndListen(final Context context, final boolean showProgress) {
+ Log.d(Constants.TAG, "starting orbot listener");
+ if (showProgress) {
+ showProgressDialog(context);
+ }
+
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getStringExtra(OrbotHelper.EXTRA_STATUS)) {
+ case OrbotHelper.STATUS_ON:
+ context.unregisterReceiver(this);
+ // generally Orbot starts working a little after this status is received
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (showProgress) {
+ mProgressDialog.dismiss();
+ }
+ onOrbotStarted();
+ }
+ }, 1000);
+ break;
+ case OrbotHelper.STATUS_STARTS_DISABLED:
+ context.unregisterReceiver(this);
+ if (showProgress) {
+ mProgressDialog.dismiss();
+ }
+ onSilentStartDisabled();
+ break;
+
+ }
+ Log.d(Constants.TAG, "Orbot silent start broadcast: " +
+ intent.getStringExtra(OrbotHelper.EXTRA_STATUS));
+ }
+ };
+ context.registerReceiver(receiver, new IntentFilter(OrbotHelper.ACTION_STATUS));
+
+ requestStartTor(context);
+ }
+
+ private void showProgressDialog(Context context) {
+ mProgressDialog = new ProgressDialog(ThemeChanger.getDialogThemeWrapper(context));
+ mProgressDialog.setMessage(context.getString(R.string.progress_starting_orbot));
+ mProgressDialog.setCancelable(false);
+ mProgressDialog.show();
+ }
+
+ protected abstract void onOrbotStarted();
+
+ protected abstract void onSilentStartDisabled();
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/TorServiceUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/TorServiceUtils.java
new file mode 100644
index 000000000..2638f8cd5
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/TorServiceUtils.java
@@ -0,0 +1,156 @@
+/* This is the license for Orlib, a free software project to
+ provide anonymity on the Internet from a Google Android smartphone.
+
+ For more information about Orlib, see https://guardianproject.info/
+
+ If you got this file as a part of a larger bundle, there may be other
+ license terms that you should be aware of.
+ ===============================================================================
+ Orlib is distributed under this license (aka the 3-clause BSD license)
+
+ Copyright (c) 2009-2010, Nathan Freitas, The Guardian Project
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+
+ * Neither the names of the copyright owners nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ *****
+ Orlib contains a binary distribution of the JSocks library:
+ http://code.google.com/p/jsocks-mirror/
+ which is licensed under the GNU Lesser General Public License:
+ http://www.gnu.org/licenses/lgpl.html
+
+ *****
+*/
+
+package org.sufficientlysecure.keychain.util.orbot;
+
+import android.content.Context;
+
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStreamReader;
+import java.net.URLEncoder;
+import java.util.StringTokenizer;
+
+/**
+ * This class is taken from the NetCipher library: https://github.com/guardianproject/NetCipher/
+ */
+public class TorServiceUtils {
+
+ private final static String TAG = "TorUtils";
+
+ public final static String SHELL_CMD_PS = "ps";
+ public final static String SHELL_CMD_PIDOF = "pidof";
+
+ public static int findProcessId(Context context) {
+ String dataPath = context.getFilesDir().getParentFile().getParentFile().getAbsolutePath();
+ String command = dataPath + "/" + OrbotHelper.ORBOT_PACKAGE_NAME + "/app_bin/tor";
+ int procId = -1;
+
+ try {
+ procId = findProcessIdWithPidOf(command);
+
+ if (procId == -1)
+ procId = findProcessIdWithPS(command);
+ } catch (Exception e) {
+ try {
+ procId = findProcessIdWithPS(command);
+ } catch (Exception e2) {
+ Log.e(TAG, "Unable to get proc id for command: " + URLEncoder.encode(command), e2);
+ }
+ }
+
+ return procId;
+ }
+
+ // use 'pidof' command
+ public static int findProcessIdWithPidOf(String command) throws Exception {
+
+ int procId = -1;
+
+ Runtime r = Runtime.getRuntime();
+
+ Process procPs;
+
+ String baseName = new File(command).getName();
+ // fix contributed my mikos on 2010.12.10
+ procPs = r.exec(new String[]{
+ SHELL_CMD_PIDOF, baseName
+ });
+ // procPs = r.exec(SHELL_CMD_PIDOF);
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(procPs.getInputStream()));
+ String line;
+
+ while ((line = reader.readLine()) != null) {
+
+ try {
+ // this line should just be the process id
+ procId = Integer.parseInt(line.trim());
+ break;
+ } catch (NumberFormatException e) {
+ Log.e("TorServiceUtils", "unable to parse process pid: " + line, e);
+ }
+ }
+
+ return procId;
+
+ }
+
+ // use 'ps' command
+ public static int findProcessIdWithPS(String command) throws Exception {
+
+ int procId = -1;
+
+ Runtime r = Runtime.getRuntime();
+
+ Process procPs;
+
+ procPs = r.exec(SHELL_CMD_PS);
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(procPs.getInputStream()));
+ String line;
+
+ while ((line = reader.readLine()) != null) {
+ if (line.contains(' ' + command)) {
+
+ StringTokenizer st = new StringTokenizer(line, " ");
+ st.nextToken(); // proc owner
+
+ procId = Integer.parseInt(st.nextToken().trim());
+
+ break;
+ }
+ }
+
+ return procId;
+
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/anim/fade_in.xml b/OpenKeychain/src/main/res/anim/fade_in.xml
index 5e2b8be60..7c3475326 100644
--- a/OpenKeychain/src/main/res/anim/fade_in.xml
+++ b/OpenKeychain/src/main/res/anim/fade_in.xml
@@ -4,4 +4,4 @@
android:interpolator="@android:anim/bounce_interpolator"
android:duration="700"
/>
-</set> \ No newline at end of file
+</set>
diff --git a/OpenKeychain/src/main/res/anim/fade_out.xml b/OpenKeychain/src/main/res/anim/fade_out.xml
index f96bf3cb3..9a9dbbbc8 100644
--- a/OpenKeychain/src/main/res/anim/fade_out.xml
+++ b/OpenKeychain/src/main/res/anim/fade_out.xml
@@ -4,4 +4,4 @@
android:interpolator="@android:anim/accelerate_interpolator"
android:duration="300"
/>
-</set> \ No newline at end of file
+</set>
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_add_white_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_add_white_24dp.png
new file mode 100644
index 000000000..694179bd4
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_add_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_apps_black_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_apps_black_24dp.png
index 37931a0ad..d9a5ebedf 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_apps_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_apps_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_arrow_back_white_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_arrow_back_white_24dp.png
index 5fa494878..a2051cef9 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_arrow_back_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_arrow_back_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_check_circle_black_48dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_check_circle_black_48dp.png
new file mode 100644
index 000000000..1c2ddcd5f
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_check_circle_black_48dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_check_white_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_check_white_24dp.png
index f42a0e2d2..729f29010 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_check_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_check_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_close_black_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_close_black_24dp.png
index d5a928783..1a9cd75a0 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_close_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_close_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_close_white_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_close_white_24dp.png
index 0fd15563a..ceb1a1eeb 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_close_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_close_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_cloud_black_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_cloud_black_24dp.png
new file mode 100644
index 000000000..c3445cb8d
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_cloud_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_content_copy_black_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_content_copy_black_24dp.png
index dc8c85cce..9a9e5706f 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_content_copy_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_content_copy_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_delete_white_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_delete_white_24dp.png
new file mode 100644
index 000000000..4a9f76947
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_delete_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_expand_less_black_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_expand_less_black_24dp.png
index 35e3b426f..57139a78a 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_expand_less_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_expand_less_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_expand_more_black_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_expand_more_black_24dp.png
index ed993f35d..9625f148f 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_expand_more_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_expand_more_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_extension_black_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_extension_black_24dp.png
new file mode 100644
index 000000000..e711e87e2
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_extension_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_folder_white_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_folder_white_24dp.png
index 9f5c75609..02ea533a8 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_folder_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_folder_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_help_black_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_help_black_24dp.png
index 05be749c3..374fafd7f 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_help_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_help_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_ic_stat_tor.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_ic_stat_tor.png
new file mode 100644
index 000000000..b6e040adb
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_ic_stat_tor.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_lock_black_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_lock_black_24dp.png
index 0888c6173..de5029591 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_lock_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_lock_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_lock_white_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_lock_white_24dp.png
index b94735ecb..cd4f04aa1 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_lock_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_lock_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png
new file mode 100644
index 000000000..bb6aef1d0
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_mode_edit_white_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_mode_edit_white_24dp.png
index 3ee3e1720..595ff10ac 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_mode_edit_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_mode_edit_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png
index 164385d04..57c9fa546 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png
index cd16fdd50..ffa7be933 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_reorder_grey_500_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_reorder_grey_500_24dp.png
new file mode 100644
index 000000000..58fe2c52e
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_reorder_grey_500_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_repeat_white_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_repeat_white_24dp.png
index 612e73458..81c5be793 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_repeat_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_repeat_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_save_white_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_save_white_24dp.png
index 8c9e9cec0..dd3f10664 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_save_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_save_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_search_white_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_search_white_24dp.png
index a2fc5b2e7..bbfbc96cb 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_search_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_search_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_settings_black_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_settings_black_24dp.png
index b16209fc1..acf1ddf85 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_settings_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_settings_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_settings_ethernet_black_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_settings_ethernet_black_24dp.png
new file mode 100644
index 000000000..c97350f3f
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_settings_ethernet_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_share_black_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_share_black_24dp.png
index 2839b1352..20ba48063 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_share_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_share_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_stat_notify_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_stat_notify_24dp.png
new file mode 100644
index 000000000..2b7f41971
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_stat_notify_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/uid_mail_bad.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_stat_retyped_bad.png
index dc20b0f03..dc20b0f03 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/uid_mail_bad.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_stat_retyped_bad.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/uid_mail_ok.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_stat_retyped_ok.png
index 76944469c..76944469c 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/uid_mail_ok.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_stat_retyped_ok.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_stat_tor.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_stat_tor.png
new file mode 100644
index 000000000..b6e040adb
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_stat_tor.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_stat_tor_off.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_stat_tor_off.png
new file mode 100644
index 000000000..e72d7ca4b
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_stat_tor_off.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_sync_black_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_sync_black_24dp.png
new file mode 100644
index 000000000..a5ebdbd07
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_sync_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_vpn_key_black_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_vpn_key_black_24dp.png
index c900b8ec7..e05e76ffc 100644
--- a/OpenKeychain/src/main/res/drawable-hdpi/ic_vpn_key_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_vpn_key_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_add_white_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_add_white_24dp.png
new file mode 100644
index 000000000..3856041d7
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_add_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_apps_black_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_apps_black_24dp.png
index 5b1fd7766..aaadd5c46 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_apps_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_apps_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_arrow_back_white_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_arrow_back_white_24dp.png
index 9e662f6d4..d571552fb 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_arrow_back_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_arrow_back_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_check_circle_black_48dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_check_circle_black_48dp.png
new file mode 100644
index 000000000..86bf38e98
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_check_circle_black_48dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_check_white_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_check_white_24dp.png
index e91f9048b..dfcb55d02 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_check_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_check_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_close_black_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_close_black_24dp.png
index 4ebf8a227..40a1a84e3 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_close_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_close_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_close_white_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_close_white_24dp.png
index e80681aeb..af7f8288d 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_close_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_close_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_cloud_black_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_cloud_black_24dp.png
new file mode 100644
index 000000000..633105072
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_cloud_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_content_copy_black_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_content_copy_black_24dp.png
index 4ad9e552d..c94cc28f1 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_content_copy_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_content_copy_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_delete_white_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_delete_white_24dp.png
new file mode 100644
index 000000000..e2f5f3555
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_delete_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_expand_less_black_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_expand_less_black_24dp.png
index a5ab2c5d3..08c16a328 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_expand_less_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_expand_less_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_expand_more_black_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_expand_more_black_24dp.png
index 73fc3b422..feb85a775 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_expand_more_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_expand_more_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_extension_black_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_extension_black_24dp.png
new file mode 100644
index 000000000..075d6ca15
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_extension_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_folder_white_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_folder_white_24dp.png
index 1c5797c9e..831d723ba 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_folder_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_folder_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_help_black_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_help_black_24dp.png
index f3743dc20..f6e789ba1 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_help_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_help_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_ic_stat_tor.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_ic_stat_tor.png
new file mode 100644
index 000000000..49b475fcb
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_ic_stat_tor.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_lock_black_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_lock_black_24dp.png
index d18d4b667..3a4a1586e 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_lock_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_lock_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_lock_white_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_lock_white_24dp.png
index 381b6a118..1127f87f7 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_lock_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_lock_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png
new file mode 100644
index 000000000..01d681697
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_mode_edit_white_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_mode_edit_white_24dp.png
index 85cff0b91..12b09f1d9 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_mode_edit_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_mode_edit_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_play_arrow_white_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_play_arrow_white_24dp.png
index 8d1e433a5..c61e948bb 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_play_arrow_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_play_arrow_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png
index 235c84f1e..97e42b525 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_reorder_grey_500_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_reorder_grey_500_24dp.png
new file mode 100644
index 000000000..d6abf0936
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_reorder_grey_500_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_repeat_white_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_repeat_white_24dp.png
index 8a2b641ca..b1c2e04ab 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_repeat_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_repeat_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_save_white_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_save_white_24dp.png
index bb26bc075..015062ed3 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_save_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_save_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_search_white_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_search_white_24dp.png
index dff1e3a8a..faefc59c8 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_search_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_search_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_settings_black_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_settings_black_24dp.png
index 3405c951d..c59419c02 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_settings_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_settings_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_settings_ethernet_black_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_settings_ethernet_black_24dp.png
new file mode 100644
index 000000000..a9efbf176
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_settings_ethernet_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_share_black_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_share_black_24dp.png
index f0ff945b8..f02d360aa 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_share_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_share_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_stat_notify_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_stat_notify_24dp.png
new file mode 100644
index 000000000..c24b41148
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_stat_notify_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/uid_mail_bad.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_stat_retyped_bad.png
index 262d53027..262d53027 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/uid_mail_bad.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_stat_retyped_bad.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/uid_mail_ok.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_stat_retyped_ok.png
index e16ec810a..e16ec810a 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/uid_mail_ok.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_stat_retyped_ok.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_stat_tor.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_stat_tor.png
new file mode 100644
index 000000000..49b475fcb
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_stat_tor.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_stat_tor_off.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_stat_tor_off.png
new file mode 100644
index 000000000..9d6447799
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_stat_tor_off.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_sync_black_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_sync_black_24dp.png
new file mode 100644
index 000000000..9685e8e16
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_sync_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_vpn_key_black_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_vpn_key_black_24dp.png
index 5e8781731..f7ac2a184 100644
--- a/OpenKeychain/src/main/res/drawable-mdpi/ic_vpn_key_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_vpn_key_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_add_white_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_add_white_24dp.png
new file mode 100644
index 000000000..67bb598e5
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_add_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_apps_black_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_apps_black_24dp.png
index c8187799b..22f12110e 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_apps_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_apps_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.png
index addbfc886..ce5b878b0 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_check_circle_black_48dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_check_circle_black_48dp.png
new file mode 100644
index 000000000..872b578c8
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_check_circle_black_48dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_check_white_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_check_white_24dp.png
index e5024472a..3b2b65d26 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_check_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_check_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_close_black_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_close_black_24dp.png
index ed2b2525f..6bc437298 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_close_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_close_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_close_white_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_close_white_24dp.png
index 76e07f097..b7c7ffd0e 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_close_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_close_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_cloud_black_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_cloud_black_24dp.png
new file mode 100644
index 000000000..b688b3146
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_cloud_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_content_copy_black_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_content_copy_black_24dp.png
index c6f0e6b85..1cf76a960 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_content_copy_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_content_copy_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_delete_white_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_delete_white_24dp.png
new file mode 100644
index 000000000..388b5b060
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_delete_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_expand_less_black_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_expand_less_black_24dp.png
index 47c7b52a1..323360ead 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_expand_less_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_expand_less_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_expand_more_black_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_expand_more_black_24dp.png
index 45d30d999..d3ee65e9a 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_expand_more_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_expand_more_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_extension_black_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_extension_black_24dp.png
new file mode 100644
index 000000000..8f5d4e64d
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_extension_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_folder_white_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_folder_white_24dp.png
index e5f54cef0..71a5a137c 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_folder_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_folder_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_help_black_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_help_black_24dp.png
index 87095ef26..d3542c6bc 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_help_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_help_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_ic_stat_tor.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_ic_stat_tor.png
new file mode 100644
index 000000000..e76c09f8d
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_ic_stat_tor.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_lock_black_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_lock_black_24dp.png
index 8856c70df..2a166945a 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_lock_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_lock_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png
index c5e9d0b49..ad8d91a99 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_light.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_light.png
new file mode 100644
index 000000000..930ca8d95
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_light.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_mode_edit_white_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_mode_edit_white_24dp.png
index 7f0ea51bf..5a06bff5a 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_mode_edit_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_mode_edit_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png
index a55d19922..a3c80e73d 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png
index 5f89fc257..1989184b1 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_reorder_grey_500_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_reorder_grey_500_24dp.png
new file mode 100644
index 000000000..0af0cb76d
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_reorder_grey_500_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_repeat_white_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_repeat_white_24dp.png
index 729220066..ad8b8c0df 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_repeat_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_repeat_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_save_white_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_save_white_24dp.png
index aa0332092..adda09575 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_save_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_save_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_search_white_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_search_white_24dp.png
index 043759acd..bfc3e3939 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_search_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_search_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_settings_black_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_settings_black_24dp.png
index 2b775b646..e84e188a1 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_settings_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_settings_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_settings_ethernet_black_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_settings_ethernet_black_24dp.png
new file mode 100644
index 000000000..306338a77
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_settings_ethernet_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_share_black_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_share_black_24dp.png
index 4b3675766..81c80b700 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_share_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_share_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_notify_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_notify_24dp.png
new file mode 100644
index 000000000..b9198e624
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_notify_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/uid_mail_bad.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_retyped_bad.png
index fc7ba24a3..fc7ba24a3 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/uid_mail_bad.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_retyped_bad.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/uid_mail_ok.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_retyped_ok.png
index e2aef1177..e2aef1177 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/uid_mail_ok.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_retyped_ok.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_tor.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_tor.png
new file mode 100644
index 000000000..e76c09f8d
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_tor.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_tor_off.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_tor_off.png
new file mode 100644
index 000000000..5b1777c8e
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_stat_tor_off.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_sync_black_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_sync_black_24dp.png
new file mode 100644
index 000000000..860a5db38
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_sync_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_vpn_key_black_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_vpn_key_black_24dp.png
index ccbdce4ab..a8761c90a 100644
--- a/OpenKeychain/src/main/res/drawable-xhdpi/ic_vpn_key_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_vpn_key_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png
new file mode 100644
index 000000000..0fdced8fc
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_apps_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_apps_black_24dp.png
index 626543b47..6d0fe2c00 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_apps_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_apps_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png
index 4057cc545..746d77579 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_check_circle_black_48dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_check_circle_black_48dp.png
new file mode 100644
index 000000000..6d3b6449c
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_check_circle_black_48dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png
index 6e03d54cf..2c2ad771f 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_check_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_close_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_close_black_24dp.png
index 08f59ea1e..51b4401ca 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_close_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_close_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png
index 0eb9d8b08..6b717e0dd 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_cloud_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_cloud_black_24dp.png
new file mode 100644
index 000000000..13c994e7d
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_cloud_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_content_copy_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_content_copy_black_24dp.png
index 081fbec5b..074ea8807 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_content_copy_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_content_copy_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_delete_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_delete_white_24dp.png
new file mode 100644
index 000000000..3fcdfdb55
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_delete_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_expand_less_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_expand_less_black_24dp.png
index 0470e3f02..ee92f4ecd 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_expand_less_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_expand_less_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_expand_more_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_expand_more_black_24dp.png
index aadd04af6..5cd142c1d 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_expand_more_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_expand_more_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_extension_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_extension_black_24dp.png
new file mode 100644
index 000000000..25c6154a1
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_extension_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_folder_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_folder_white_24dp.png
index 0d1ac4876..b93d5a1e4 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_folder_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_folder_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_help_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_help_black_24dp.png
index f32d7e708..645822e83 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_help_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_help_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_ic_stat_tor.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_ic_stat_tor.png
new file mode 100644
index 000000000..5fdedd3b9
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_ic_stat_tor.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_lock_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_lock_black_24dp.png
index c49d420e0..a7caa2db8 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_lock_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_lock_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png
index 0dcada814..0e52c7c75 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_mode_edit_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_mode_edit_white_24dp.png
index 34ec7092f..02e19d045 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_mode_edit_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_mode_edit_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png
index 043acd808..547ef30aa 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png
index 72128fe69..1692d8a24 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_reorder_grey_500_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_reorder_grey_500_24dp.png
new file mode 100644
index 000000000..1d9c88d10
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_reorder_grey_500_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_repeat_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_repeat_white_24dp.png
index 63f8de50f..5de7a2951 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_repeat_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_repeat_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_save_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_save_white_24dp.png
index 6c87e1358..3e0ce1a5f 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_save_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_save_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png
index 0bbeab150..abbb98951 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_settings_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_settings_black_24dp.png
index 47f0e0d82..3023ff8da 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_settings_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_settings_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_settings_ethernet_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_settings_ethernet_black_24dp.png
new file mode 100644
index 000000000..2b31aa022
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_settings_ethernet_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_share_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_share_black_24dp.png
index 09d4df6af..784933ad5 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_share_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_share_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_notify_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_notify_24dp.png
new file mode 100644
index 000000000..8ff2b8041
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_notify_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/uid_mail_bad.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_retyped_bad.png
index bc71d3a65..bc71d3a65 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/uid_mail_bad.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_retyped_bad.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/uid_mail_ok.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_retyped_ok.png
index 501a75d63..501a75d63 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/uid_mail_ok.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_retyped_ok.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_tor.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_tor.png
new file mode 100644
index 000000000..5fdedd3b9
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_tor.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_tor_off.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_tor_off.png
new file mode 100644
index 000000000..2fb280a1b
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_stat_tor_off.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_sync_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_sync_black_24dp.png
new file mode 100644
index 000000000..f7990080d
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_sync_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_vpn_key_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_vpn_key_black_24dp.png
index 736bde37a..16b1e4e24 100644
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_vpn_key_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_vpn_key_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png
new file mode 100644
index 000000000..d64c22e9e
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_apps_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_apps_black_24dp.png
index d12d2e796..bfbe789c3 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_apps_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_apps_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.png
index 02f2f6fe8..fb06e1d48 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_check_circle_black_48dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_check_circle_black_48dp.png
new file mode 100644
index 000000000..0e3b23964
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_check_circle_black_48dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png
index 87892840e..d670618c7 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_check_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_close_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_close_black_24dp.png
index c5d79caff..df42feecb 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_close_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_close_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png
index 7b2a480a0..396419219 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_cloud_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_cloud_black_24dp.png
new file mode 100644
index 000000000..9ac14e86a
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_cloud_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_content_copy_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_content_copy_black_24dp.png
index 04c07fb56..1f6af72d0 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_content_copy_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_content_copy_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_delete_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_delete_white_24dp.png
new file mode 100644
index 000000000..8d322aa9b
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_delete_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_expand_less_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_expand_less_black_24dp.png
index 08ae54533..99c6e3e1c 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_expand_less_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_expand_less_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_expand_more_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_expand_more_black_24dp.png
index 228b2a982..ad852e3e6 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_expand_more_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_expand_more_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_extension_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_extension_black_24dp.png
new file mode 100644
index 000000000..73067affc
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_extension_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_folder_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_folder_white_24dp.png
index 7a3c198ee..a1afbe9da 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_folder_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_folder_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_help_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_help_black_24dp.png
index a4286b54c..7c4823055 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_help_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_help_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_ic_stat_tor.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_ic_stat_tor.png
new file mode 100644
index 000000000..306b94793
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_ic_stat_tor.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_lock_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_lock_black_24dp.png
index db080df4e..e66dd967d 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_lock_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_lock_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_lock_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_lock_white_24dp.png
index a70c55b7e..a55147be1 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_lock_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_lock_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_mode_edit_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_mode_edit_white_24dp.png
index 9380370f4..d6668a051 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_mode_edit_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_mode_edit_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png
index 7cc008475..be5c062b5 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_refresh_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_refresh_white_24dp.png
index d271d8e03..f5beca251 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_refresh_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_refresh_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_repeat_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_repeat_white_24dp.png
index f3c284330..c7f3072ee 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_repeat_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_repeat_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_save_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_save_white_24dp.png
index 51998492c..bd80bf1f7 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_save_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_save_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png
index 70c21baf7..dd5adfc7f 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_settings_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_settings_black_24dp.png
index bce161d00..476d5c978 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_settings_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_settings_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_settings_ethernet_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_settings_ethernet_black_24dp.png
new file mode 100644
index 000000000..e3848e5f2
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_settings_ethernet_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_share_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_share_black_24dp.png
index 0fe15fc05..5a8544ce5 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_share_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_share_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_stat_notify_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_stat_notify_24dp.png
new file mode 100644
index 000000000..9dad48842
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_stat_notify_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_stat_tor.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_stat_tor.png
new file mode 100644
index 000000000..306b94793
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_stat_tor.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_stat_tor_off.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_stat_tor_off.png
new file mode 100644
index 000000000..317c86717
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_stat_tor_off.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_sync_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_sync_black_24dp.png
new file mode 100644
index 000000000..b9f56f327
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_sync_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_vpn_key_black_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_vpn_key_black_24dp.png
index 3451d9855..f04166536 100644
--- a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_vpn_key_black_24dp.png
+++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_vpn_key_black_24dp.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable/cardview_header.xml b/OpenKeychain/src/main/res/drawable/cardview_header.xml
index 9bab96ea9..213c78277 100644
--- a/OpenKeychain/src/main/res/drawable/cardview_header.xml
+++ b/OpenKeychain/src/main/res/drawable/cardview_header.xml
@@ -6,6 +6,6 @@
android:height="1dp"
android:width="1000dp" />
- <solid android:color="@color/bg_gray" />
+ <solid android:color="#808080" />
-</shape> \ No newline at end of file
+</shape>
diff --git a/OpenKeychain/src/main/res/drawable/divider.xml b/OpenKeychain/src/main/res/drawable/divider.xml
index b0695f89a..b3800c459 100644
--- a/OpenKeychain/src/main/res/drawable/divider.xml
+++ b/OpenKeychain/src/main/res/drawable/divider.xml
@@ -6,6 +6,6 @@
android:height="1px"
android:width="1000dp" />
- <solid android:color="@color/bg_gray" />
+ <solid android:color="#eeeeee" />
</shape> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/drawable/fab_label_background.xml b/OpenKeychain/src/main/res/drawable/fab_label_background.xml
index 0d8c05b11..b2d5954d8 100644
--- a/OpenKeychain/src/main/res/drawable/fab_label_background.xml
+++ b/OpenKeychain/src/main/res/drawable/fab_label_background.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@color/black_semi_transparent"/>
+ <!-- using an attribute for color will crash on API <= 20. Refer to http://stackoverflow.com/a/13471695/3000919 -->
+ <solid android:color="#b2000000"/>
<padding
android:left="16dp"
android:top="4dp"
@@ -8,4 +9,4 @@
android:bottom="4dp"/>
<corners
android:radius="2dp"/>
-</shape> \ No newline at end of file
+</shape>
diff --git a/OpenKeychain/src/main/res/drawable/scrim_bottom.xml b/OpenKeychain/src/main/res/drawable/scrim_bottom.xml
new file mode 100644
index 000000000..b0c203835
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable/scrim_bottom.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:angle="90"
+ android:startColor="@color/translucent_scrim_bottom"
+ android:centerColor="@color/translucent_scrim_bottom_center"
+ android:endColor="@android:color/transparent"/>
+</shape> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/drawable/section_header.xml b/OpenKeychain/src/main/res/drawable/section_header_dark.xml
index 04d3d4957..e60fc085d 100644
--- a/OpenKeychain/src/main/res/drawable/section_header.xml
+++ b/OpenKeychain/src/main/res/drawable/section_header_dark.xml
@@ -6,6 +6,6 @@
android:height="2dp"
android:width="1000dp" />
- <solid android:color="@color/header_text" />
+ <solid android:color="#d0d0d0" />
-</shape> \ No newline at end of file
+</shape>
diff --git a/OpenKeychain/src/main/res/drawable/section_header_light.xml b/OpenKeychain/src/main/res/drawable/section_header_light.xml
new file mode 100644
index 000000000..a47a57ce3
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable/section_header_light.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle" >
+
+ <size
+ android:height="2dp"
+ android:width="1000dp" />
+
+ <solid android:color="#212121" />
+
+</shape>
diff --git a/OpenKeychain/src/main/res/drawable/selector_transparent_button.xml b/OpenKeychain/src/main/res/drawable/selector_transparent_button.xml
deleted file mode 100644
index ed856f281..000000000
--- a/OpenKeychain/src/main/res/drawable/selector_transparent_button.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!-- Borderless Buttons for API < 11, see http://stackoverflow.com/a/14663170 -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true" android:drawable="@color/bg_gray" />
- <item android:drawable="@android:color/transparent" />
-</selector> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/drawable/yubikey_phone.png b/OpenKeychain/src/main/res/drawable/yubikey_phone.png
index 6a03501c6..3cdfbba0f 100644
--- a/OpenKeychain/src/main/res/drawable/yubikey_phone.png
+++ b/OpenKeychain/src/main/res/drawable/yubikey_phone.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/layout/add_user_id_dialog.xml b/OpenKeychain/src/main/res/layout/add_user_id_dialog.xml
index ffb7493f6..b3969a2bd 100644
--- a/OpenKeychain/src/main/res/layout/add_user_id_dialog.xml
+++ b/OpenKeychain/src/main/res/layout/add_user_id_dialog.xml
@@ -29,7 +29,7 @@
android:id="@+id/add_user_id_comment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textColor="@color/tertiary_text_light"
+ android:textColor="?attr/colorTertiaryText"
android:singleLine="true"
android:lines="1"
android:maxLines="1"
diff --git a/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml b/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml
index c3f6e33cb..9557bf61e 100644
--- a/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml
+++ b/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml
@@ -29,7 +29,7 @@
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:overScrollMode="always"
- app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
tools:ignore="UnusedAttribute"
android:transitionGroup="false"
@@ -117,7 +117,7 @@
android:layout_height="wrap_content"
android:elevation="4dp"
fab:fab_icon="@drawable/ic_play_arrow_white_24dp"
- fab:fab_colorNormal="@color/fab"
- fab:fab_colorPressed="@color/fab_pressed" />
+ fab:fab_colorNormal="?attr/colorFab"
+ fab:fab_colorPressed="?attr/colorFabPressed" />
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/api_app_settings_fragment.xml b/OpenKeychain/src/main/res/layout/api_app_settings_fragment.xml
index 1ea0a520d..20abb3965 100644
--- a/OpenKeychain/src/main/res/layout/api_app_settings_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/api_app_settings_fragment.xml
@@ -53,11 +53,11 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/api_settings_package_signature"
+ android:text="@string/api_settings_package_certificate"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
- android:id="@+id/api_app_settings_package_signature"
+ android:id="@+id/api_app_settings_package_certificate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Base64 encoded hash of signature"
diff --git a/OpenKeychain/src/main/res/layout/backup_fragment.xml b/OpenKeychain/src/main/res/layout/backup_fragment.xml
new file mode 100644
index 000000000..96fba954b
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/backup_fragment.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="24dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="8dip"
+ android:text="@string/backup_text"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:text="@string/backup_section" />
+
+ <TextView
+ android:id="@+id/backup_all"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clickable="true"
+ android:drawablePadding="8dp"
+ android:drawableRight="@drawable/ic_save_grey_24dp"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:text="@string/backup_all"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@+id/backup_public_keys"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clickable="true"
+ android:drawablePadding="8dp"
+ android:drawableRight="@drawable/ic_save_grey_24dp"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:text="@string/backup_public_keys"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginBottom="8dp"
+ android:background="?android:attr/listDivider" />
+ </LinearLayout>
+
+</ScrollView> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/certify_fingerprint_fragment.xml b/OpenKeychain/src/main/res/layout/certify_fingerprint_fragment.xml
index 9b6b35012..239cdcc95 100644
--- a/OpenKeychain/src/main/res/layout/certify_fingerprint_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/certify_fingerprint_fragment.xml
@@ -16,6 +16,7 @@
android:padding="16dp">
<TextView
+ android:id="@+id/certify_fingerprint_intro"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
@@ -28,7 +29,7 @@
android:id="@+id/certify_fingerprint_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:cardBackgroundColor="@android:color/white"
+ app:cardBackgroundColor="?attr/colorCardViewBackground"
app:cardUseCompatPadding="true"
app:cardCornerRadius="4dp"
android:layout_gravity="top">
@@ -151,4 +152,4 @@
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/create_key_email_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_email_fragment.xml
index 17cfe54ac..d4ece38ac 100644
--- a/OpenKeychain/src/main/res/layout/create_key_email_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/create_key_email_fragment.xml
@@ -51,7 +51,7 @@
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
- android:background="@color/holo_gray_bright"
+ android:background="?attr/colorButtonRow"
android:id="@+id/create_key_buttons">
<TextView
@@ -88,4 +88,4 @@
android:clickable="true"
style="?android:attr/borderlessButtonStyle" />
</LinearLayout>
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml
index e085fcb09..9a6c33f82 100644
--- a/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml
@@ -28,7 +28,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:text="@string/label_name"
- android:textColor="@color/tertiary_text_light"
+ android:textColor="?attr/colorTertiaryText"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
@@ -44,7 +44,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:text="@string/label_email"
- android:textColor="@color/tertiary_text_light"
+ android:textColor="?attr/colorTertiaryText"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
@@ -87,7 +87,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/create_key_rsa"
- android:textColor="@color/android_green_light"
+ android:textColor="?attr/colorPrimary"
android:textAppearance="?android:attr/textAppearanceMedium"
android:minHeight="?android:attr/listPreferredItemHeight"
android:clickable="true"
@@ -129,7 +129,7 @@
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp"
android:text="@string/create_key_final_robot_text"
- android:textColor="@color/android_green_light"
+ android:textColor="?attr/colorPrimary"
android:textAppearance="?android:attr/textAppearanceMedium"
android:drawableLeft="@drawable/create_key_robot"
android:drawablePadding="8dp" />
@@ -144,7 +144,7 @@
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
- android:background="@color/holo_gray_bright"
+ android:background="?attr/colorButtonRow"
android:id="@+id/create_key_buttons">
<TextView
@@ -182,4 +182,4 @@
style="?android:attr/borderlessButtonStyle" />
</LinearLayout>
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/create_key_name_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_name_fragment.xml
index a2f81f74c..7b8ba3fc1 100644
--- a/OpenKeychain/src/main/res/layout/create_key_name_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/create_key_name_fragment.xml
@@ -45,7 +45,7 @@
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
- android:background="@color/holo_gray_bright"
+ android:background="?attr/colorButtonRow"
android:id="@+id/create_key_buttons">
<TextView
@@ -82,4 +82,4 @@
android:clickable="true"
style="?android:attr/borderlessButtonStyle" />
</LinearLayout>
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/create_key_passphrase_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_passphrase_fragment.xml
index 48b86765a..9d10bbe70 100644
--- a/OpenKeychain/src/main/res/layout/create_key_passphrase_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/create_key_passphrase_fragment.xml
@@ -34,7 +34,8 @@
android:inputType="textPassword"
android:hint="@string/label_passphrase"
android:ems="10"
- android:layout_gravity="center_horizontal" />
+ android:layout_gravity="center_horizontal"
+ />
<EditText
android:id="@+id/create_key_passphrase_again"
@@ -63,7 +64,7 @@
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
- android:background="@color/holo_gray_bright"
+ android:background="?attr/colorButtonRow"
android:id="@+id/create_key_buttons">
<TextView
@@ -100,4 +101,4 @@
android:clickable="true"
style="?android:attr/borderlessButtonStyle" />
</LinearLayout>
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/create_key_start_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_start_fragment.xml
index 2db147475..20c434c02 100644
--- a/OpenKeychain/src/main/res/layout/create_key_start_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/create_key_start_fragment.xml
@@ -30,7 +30,7 @@
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
- android:background="@color/holo_gray_bright"
+ android:background="?attr/colorButtonRow"
android:id="@+id/create_key_buttons">
<TextView
diff --git a/OpenKeychain/src/main/res/layout/create_yubi_key_blank_fragment.xml b/OpenKeychain/src/main/res/layout/create_yubi_key_blank_fragment.xml
new file mode 100644
index 000000000..ca203d0b5
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/create_yubi_key_blank_fragment.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:layout_above="@+id/create_key_buttons">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginLeft="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/first_time_blank_yubikey" />
+
+ </LinearLayout>
+ </ScrollView>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:background="?attr/colorButtonRow"
+ android:id="@+id/create_key_buttons">
+
+ <TextView
+ android:id="@+id/create_key_back_button"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/btn_back"
+ android:textAllCaps="true"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:drawableLeft="@drawable/ic_chevron_left_grey_24dp"
+ android:drawablePadding="8dp"
+ android:gravity="left|center_vertical"
+ android:clickable="true"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/create_key_next_button"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/first_time_blank_yubikey_yes"
+ android:textAllCaps="true"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:drawableRight="@drawable/ic_chevron_right_grey_24dp"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical|right"
+ android:clickable="true"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_gravity="center_vertical" />
+ </LinearLayout>
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/create_yubikey_import_fragment.xml b/OpenKeychain/src/main/res/layout/create_yubi_key_import_fragment.xml
index e70188e49..838ee37b4 100644
--- a/OpenKeychain/src/main/res/layout/create_yubikey_import_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/create_yubi_key_import_fragment.xml
@@ -84,7 +84,7 @@
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
- android:background="@color/holo_gray_bright"
+ android:background="?attr/colorButtonRow"
android:id="@+id/create_key_buttons">
<TextView
@@ -123,4 +123,4 @@
</LinearLayout>
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/create_yubi_key_pin_fragment.xml b/OpenKeychain/src/main/res/layout/create_yubi_key_pin_fragment.xml
new file mode 100644
index 000000000..393ec76d4
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/create_yubi_key_pin_fragment.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<RelativeLayout 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">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:layout_above="@+id/create_key_buttons">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginLeft="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/create_key_yubi_key_pin_text" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginLeft="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/create_key_yubi_key_pin" />
+
+ <TextView
+ android:id="@+id/create_yubi_key_pin"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginLeft="8dp"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ tools:text="123456" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginLeft="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/create_key_yubi_key_admin_pin" />
+
+ <TextView
+ android:id="@+id/create_yubi_key_admin_pin"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginLeft="8dp"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ tools:text="12345678" />
+
+ </LinearLayout>
+ </ScrollView>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:background="?attr/colorButtonRow"
+ android:id="@+id/create_key_buttons">
+
+ <TextView
+ android:id="@+id/create_key_back_button"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/btn_back"
+ android:textAllCaps="true"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:drawableLeft="@drawable/ic_chevron_left_grey_24dp"
+ android:drawablePadding="8dp"
+ android:gravity="left|center_vertical"
+ android:clickable="true"
+ style="?android:attr/borderlessButtonStyle" />
+
+ <TextView
+ android:id="@+id/create_key_next_button"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/btn_next"
+ android:textAllCaps="true"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:drawableRight="@drawable/ic_chevron_right_grey_24dp"
+ android:drawablePadding="8dp"
+ android:gravity="right|center_vertical"
+ android:clickable="true"
+ style="?android:attr/borderlessButtonStyle" />
+ </LinearLayout>
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/create_yubi_key_pin_repeat_fragment.xml b/OpenKeychain/src/main/res/layout/create_yubi_key_pin_repeat_fragment.xml
new file mode 100644
index 000000000..d233398ca
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/create_yubi_key_pin_repeat_fragment.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_above="@+id/create_key_buttons"
+ android:fillViewport="true">
+
+ <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_marginLeft="8dp"
+ android:layout_marginTop="16dp"
+ android:text="@string/create_key_yubi_key_pin_repeat_text"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginTop="16dp"
+ android:text="@string/create_key_yubi_key_pin"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <EditText
+ android:id="@+id/create_yubi_key_pin_repeat"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="8dp"
+ android:ems="10"
+ android:hint="@string/create_key_yubi_key_pin_repeat"
+ android:inputType="numberPassword" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginTop="16dp"
+ android:text="@string/create_key_yubi_key_admin_pin"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <EditText
+ android:id="@+id/create_yubi_key_admin_pin_repeat"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="8dp"
+ android:ems="10"
+ android:hint="@string/create_key_yubi_key_admin_pin_repeat"
+ android:inputType="numberPassword" />
+
+ </LinearLayout>
+ </ScrollView>
+
+ <LinearLayout
+ android:id="@+id/create_key_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:background="?attr/colorButtonRow"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/create_key_back_button"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:clickable="true"
+ android:drawableLeft="@drawable/ic_chevron_left_grey_24dp"
+ android:drawablePadding="8dp"
+ android:gravity="left|center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:text="@string/btn_back"
+ android:textAllCaps="true"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:id="@+id/create_key_next_button"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:clickable="true"
+ android:drawablePadding="8dp"
+ android:drawableRight="@drawable/ic_chevron_right_grey_24dp"
+ android:gravity="right|center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:text="@string/btn_next"
+ android:textAllCaps="true"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ </LinearLayout>
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/create_yubikey_wait_fragment.xml b/OpenKeychain/src/main/res/layout/create_yubi_key_wait_fragment.xml
index 4e4b53118..a000dc82e 100644
--- a/OpenKeychain/src/main/res/layout/create_yubikey_wait_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/create_yubi_key_wait_fragment.xml
@@ -40,7 +40,7 @@
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
- android:background="@color/holo_gray_bright"
+ android:background="?attr/colorButtonRow"
android:id="@+id/create_key_buttons">
<TextView
@@ -79,4 +79,4 @@
</LinearLayout>
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/decrypt_files_activity.xml b/OpenKeychain/src/main/res/layout/decrypt_files_activity.xml
index 6c8a2e859..3d214dbf6 100644
--- a/OpenKeychain/src/main/res/layout/decrypt_files_activity.xml
+++ b/OpenKeychain/src/main/res/layout/decrypt_files_activity.xml
@@ -5,7 +5,7 @@
<include
android:id="@+id/toolbar_include"
- layout="@layout/toolbar_result_decrypt" />
+ layout="@layout/toolbar_standalone_white" />
<!--
fitsSystemWindows and layout_marginTop from
diff --git a/OpenKeychain/src/main/res/layout/decrypt_files_fragment.xml b/OpenKeychain/src/main/res/layout/decrypt_files_fragment.xml
deleted file mode 100644
index 22ee7e09f..000000000
--- a/OpenKeychain/src/main/res/layout/decrypt_files_fragment.xml
+++ /dev/null
@@ -1,149 +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">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <ScrollView
- android:fillViewport="true"
- android:paddingTop="8dp"
- android:layout_width="match_parent"
- android:scrollbars="vertical"
- android:layout_height="0dp"
- android:layout_weight="1">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <View
- android:id="@+id/status_divider"
- android:layout_height="1dip"
- android:layout_width="match_parent"
- android:background="?android:attr/listDivider" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingTop="4dp"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:orientation="horizontal"
-
- android:id="@+id/decrypt_files_browse"
- android:clickable="true"
- android:background="?android:selectableItemBackground">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:paddingLeft="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:text="@string/label_file_colon"
- android:gravity="center_vertical" />
-
- <TextView
- android:id="@+id/decrypt_files_filename"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:hint="@string/filemanager_title_open"
- android:drawableRight="@drawable/ic_folder_grey_24dp"
- android:drawablePadding="8dp"
- android:gravity="center_vertical" />
- </LinearLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider"
- android:layout_marginBottom="8dp" />
-
- <CheckBox
- android:id="@+id/decrypt_files_delete_after_decryption"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/label_delete_after_decryption" />
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <TextView
- android:id="@+id/decrypt_files_action_decrypt"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:text="@string/btn_decrypt_verify_file"
- android:clickable="true"
- android:background="?android:selectableItemBackground"
- android:drawableRight="@drawable/ic_save_grey_24dp"
- android:drawablePadding="8dp"
- android:gravity="center_vertical"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider"
- android:layout_above="@+id/decrypt_files_action_decrypt" />
-
- </RelativeLayout>
- </LinearLayout>
- </LinearLayout>
- </ScrollView>
- </LinearLayout>
-
- <!-- TODO: Use this layout later to hide file list -->
- <LinearLayout
- android:visibility="gone"
- android:id="@+id/decrypt_content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"></LinearLayout>
-
- <LinearLayout
- android:visibility="gone"
- android:id="@+id/decrypt_error_overlay"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:gravity="center_vertical">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:text="@string/decrypt_invalid_text"
- android:padding="16dp"
- android:layout_gravity="center"
- android:textColor="@color/android_red_light" />
-
- <Button
- android:id="@+id/decrypt_error_overlay_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@drawable/button_edgy"
- android:textColor="@color/android_red_light"
- android:text="@string/decrypt_invalid_button"
- android:layout_gravity="center_horizontal" />
- </LinearLayout>
-</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/decrypt_files_input_fragment.xml b/OpenKeychain/src/main/res/layout/decrypt_files_input_fragment.xml
new file mode 100644
index 000000000..b7e70ce10
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/decrypt_files_input_fragment.xml
@@ -0,0 +1,83 @@
+<?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:paddingTop="4dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <View
+ android:id="@+id/status_divider"
+ android:layout_height="1dip"
+ android:layout_width="match_parent"
+ android:background="?android:attr/listDivider" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal"
+ android:id="@+id/decrypt_files_browse"
+ android:clickable="true"
+ android:background="?android:selectableItemBackground">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingLeft="4dp"
+ android:paddingRight="4dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/label_file_colon"
+ android:gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/decrypt_files_filename"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:hint="@string/filemanager_title_open"
+ android:drawableRight="@drawable/ic_folder_grey_24dp"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical" />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"
+ android:layout_marginBottom="8dp" />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/decrypt_files_action_decrypt"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:text="@string/btn_decrypt_verify_file"
+ android:clickable="true"
+ android:background="?android:selectableItemBackground"
+ android:drawableRight="@drawable/ic_save_grey_24dp"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"
+ android:layout_above="@+id/decrypt_files_action_decrypt" />
+
+ </RelativeLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/decrypt_files_list_fragment.xml b/OpenKeychain/src/main/res/layout/decrypt_files_list_fragment.xml
new file mode 100644
index 000000000..d9cde05f8
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/decrypt_files_list_fragment.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/decrypt_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/decrypted_files_list"
+ android:scrollbars="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:visibility="gone"
+ android:id="@+id/decrypt_error_overlay"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center_vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/decrypt_invalid_text"
+ android:padding="16dp"
+ android:layout_gravity="center"
+ android:textColor="@color/android_red_light" />
+
+ <Button
+ android:id="@+id/decrypt_error_overlay_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/button_edgy"
+ android:textColor="@color/android_red_light"
+ android:text="@string/decrypt_invalid_button"
+ android:layout_gravity="center_horizontal" />
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/decrypt_list_entry.xml b/OpenKeychain/src/main/res/layout/decrypt_list_entry.xml
new file mode 100644
index 000000000..048595dd8
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/decrypt_list_entry.xml
@@ -0,0 +1,323 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v7.widget.CardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:custom="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/card_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="4dp"
+ custom:cardBackgroundColor="?attr/colorCardViewBackground"
+ custom:cardElevation="2dp"
+ custom:cardUseCompatPadding="true"
+ custom:cardCornerRadius="4dp"
+ >
+
+ <org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:paddingRight="8dp"
+ android:paddingLeft="8dp"
+ android:inAnimation="@anim/fade_in"
+ android:outAnimation="@anim/fade_out"
+ android:id="@+id/view_animator"
+ android:measureAllChildren="false"
+ custom:initialView="0"
+ android:minHeight="?listPreferredItemHeightSmall"
+ android:animateLayoutChanges="true"
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <ProgressBar
+ android:id="@+id/progress"
+ android:layout_width="30dp"
+ android:layout_height="30dp"
+ android:padding="4dp"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/progress_msg"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
+ android:layout_gravity="center_vertical"
+ android:text="@string/progress_processing"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+
+ </LinearLayout>
+ <!-- -->
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingRight="4dp"
+ android:paddingLeft="4dp"
+ tools:ignore="UseCompoundDrawables">
+
+ <ImageView
+ android:id="@+id/result_encryption_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/status_lock_open_24dp"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/result_encryption_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_marginLeft="8dp"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:text=""
+ tools:text="Encryption status text" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingRight="4dp"
+ android:paddingLeft="4dp"
+ tools:ignore="UseCompoundDrawables">
+
+ <ImageView
+ android:id="@+id/result_signature_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/status_signature_unverified_cutout_24dp"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/result_signature_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_marginLeft="8dp"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:text=""
+ tools:text="Signature status text" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/result_signature_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clickable="true"
+ android:background="?android:selectableItemBackground"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingRight="4dp"
+ android:paddingLeft="4dp"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/result_signature_name"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text=""
+ tools:text="Alice" />
+
+ <TextView
+ android:id="@+id/result_signature_email"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:text=""
+ tools:text="alice@example.com" />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="1dip"
+ android:layout_height="match_parent"
+ android:gravity="right"
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="8dp"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@+id/result_signature_action"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:drawableRight="@drawable/ic_vpn_key_grey_24dp"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:text=""
+ tools:text="Show"
+ />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ tools:layout_height="2dip"
+ android:background="?android:attr/listDivider" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/file"
+ android:clickable="true"
+ android:background="?android:selectableItemBackground"
+ >
+
+ <ImageView
+ android:id="@+id/thumbnail"
+ android:layout_gravity="center_vertical"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:scaleType="center"
+ android:padding="6dp"
+ android:src="@drawable/ic_doc_generic_am" />
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/filename"
+ android:maxLines="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:ellipsize="end"
+ android:text=""
+ tools:text="filename.jpg" />
+
+ <TextView
+ android:id="@+id/filesize"
+ android:maxLines="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textSize="12sp"
+ android:ellipsize="end"
+ android:text=""
+ tools:text="14kb" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/context_menu"
+ android:scaleType="center"
+ android:layout_width="36dip"
+ android:layout_height="48dip"
+ android:clickable="true"
+ android:background="?android:selectableItemBackground"
+ android:src="@drawable/ic_menu_moreoverflow_normal_holo_light" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:ignore="UseCompoundDrawables">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:src="@drawable/status_signature_invalid_cutout_24dp"
+ android:tint="@color/android_red_light"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/result_error_msg"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:text=""
+ android:layout_gravity="center_vertical"
+ tools:text="Error processing data!" />
+
+ <ImageView
+ android:id="@+id/result_error_log"
+ android:scaleType="center"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:clickable="true"
+ android:padding="6dp"
+ android:background="?android:selectableItemBackground"
+ android:src="@drawable/ic_view_list_grey_24dp"
+ android:layout_gravity="center_vertical" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
+ android:layout_gravity="center_vertical"
+ android:drawablePadding="12dp"
+ android:text="@string/msg_cancelled"
+ android:drawableLeft="@drawable/status_signature_invalid_cutout_24dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+
+ <ImageView
+ android:id="@+id/cancel_retry"
+ android:scaleType="center"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:clickable="true"
+ android:padding="6dp"
+ android:background="?android:selectableItemBackground"
+ android:src="@drawable/ic_repeat_grey_24dp"
+ android:layout_gravity="center_vertical" />
+
+ </LinearLayout>
+
+ </org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
+
+</android.support.v7.widget.CardView>
diff --git a/OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml b/OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml
index 9167a2859..f6237ec69 100644
--- a/OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/decrypt_text_fragment.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
+ xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
+ android:layout_height="match_parent">
<LinearLayout
android:visibility="gone"
@@ -30,6 +31,7 @@
android:layout_height="wrap_content"
android:gravity="top"
android:hint=""
+ tools:text="This is the plaintext"
android:textIsSelectable="true" />
</ScrollView>
@@ -62,4 +64,5 @@
android:text="@string/decrypt_invalid_button"
android:layout_gravity="center_horizontal" />
</LinearLayout>
-</LinearLayout>
+
+</org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
diff --git a/OpenKeychain/src/main/res/layout/del_rev_dialog.xml b/OpenKeychain/src/main/res/layout/del_rev_dialog.xml
new file mode 100644
index 000000000..053f98627
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/del_rev_dialog.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="16dp"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/del_rev_dialog_message"
+ android:textAppearance="@style/TextAppearance.AppCompat.Medium"
+ android:layout_marginBottom="16dp"/>
+
+ <Spinner
+ android:id="@+id/spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:drawSelectorOnTop="true"
+ android:entries="@array/rev_del_dialog_entries"/>
+
+</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/drawer_custom_header.xml b/OpenKeychain/src/main/res/layout/drawer_custom_header.xml
deleted file mode 100644
index 86465db98..000000000
--- a/OpenKeychain/src/main/res/layout/drawer_custom_header.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center"
- android:background="@color/primary">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:text="@string/app_name"
- android:textColor="@color/white"
- android:layout_gravity="center_horizontal" />
-</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/encrypt_asymmetric_fragment.xml b/OpenKeychain/src/main/res/layout/encrypt_asymmetric_fragment.xml
index aa295050a..56647ec65 100644
--- a/OpenKeychain/src/main/res/layout/encrypt_asymmetric_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/encrypt_asymmetric_fragment.xml
@@ -3,39 +3,104 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
+ android:paddingTop="4dp"
android:paddingBottom="4dp"
android:paddingRight="16dp"
android:paddingLeft="16dp">
- <org.sufficientlysecure.keychain.ui.widget.EncryptKeyCompletionView
- android:id="@+id/recipient_list"
+ <LinearLayout
android:layout_width="match_parent"
- android:minHeight="56dip"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:padding="0dp"
+ android:layout_margin="0dp">
+
+ <ViewAnimator
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/result_encryption_icon"
+ android:layout_gravity="center_vertical"
+ android:layout_marginRight="4dp"
+ android:layout_marginEnd="4dp"
+ android:paddingBottom="12dp"
+ android:inAnimation="@anim/fade_in"
+ android:outAnimation="@anim/fade_out">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/status_lock_open_24dp" />
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/status_lock_closed_24dp" />
+
+ </ViewAnimator>
+
+ <org.sufficientlysecure.keychain.ui.widget.EncryptKeyCompletionView
+ android:id="@+id/recipient_list"
+ android:layout_width="match_parent"
+ android:minHeight="56dip"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical" />
+
+ </LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="0dp"
- android:layout_margin="0dp"
- style="@android:style/Widget.EditText">
+ android:layout_margin="0dp">
- <TextView
- android:paddingLeft="8dp"
+ <ViewAnimator
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:text="@string/label_asymmetric_from"
- android:paddingRight="8dp"/>
+ android:id="@+id/result_signature_icon"
+ android:layout_gravity="center_vertical"
+ android:layout_marginRight="4dp"
+ android:layout_marginEnd="4dp"
+ android:inAnimation="@anim/fade_in"
+ android:outAnimation="@anim/fade_out">
- <org.sufficientlysecure.keychain.ui.widget.SignKeySpinner
- android:id="@+id/sign"
- android:minHeight="56dip"
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/status_signature_unverified_cutout_24dp"
+ />
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/status_signature_verified_cutout_24dp"
+ />
+
+ </ViewAnimator>
+
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
+ android:padding="0dp"
+ android:layout_margin="0dp"
+ style="@android:style/Widget.EditText">
+
+ <TextView
+ android:paddingLeft="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/label_asymmetric_from"
+ android:paddingRight="8dp"/>
+
+ <org.sufficientlysecure.keychain.ui.widget.SignKeySpinner
+ android:id="@+id/sign"
+ android:minHeight="56dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical" />
+
+ </LinearLayout>
</LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/encrypt_decrypt_overview_fragment.xml b/OpenKeychain/src/main/res/layout/encrypt_decrypt_overview_fragment.xml
index 6f76ada72..7bd919abc 100644
--- a/OpenKeychain/src/main/res/layout/encrypt_decrypt_overview_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/encrypt_decrypt_overview_fragment.xml
@@ -1,126 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:paddingTop="4dp"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
+<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:orientation="vertical">
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:text="@string/section_encrypt" />
-
- <TextView
- android:id="@+id/encrypt_files"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:clickable="true"
- style="?android:attr/borderlessButtonStyle"
- android:text="@string/btn_encrypt_files"
- android:drawableRight="@drawable/ic_folder_grey_24dp"
- android:drawablePadding="8dp"
- android:gravity="center_vertical" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider" />
-
- <TextView
- android:id="@+id/encrypt_text"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:clickable="true"
- style="?android:attr/borderlessButtonStyle"
- android:text="@string/btn_encrypt_text"
- android:drawableRight="@drawable/ic_comment_text_grey600_24dp"
- android:drawablePadding="8dp"
- android:gravity="center_vertical" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider" />
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:text="@string/section_decrypt" />
-
- <TextView
- android:id="@+id/decrypt_files"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:clickable="true"
- style="?android:attr/borderlessButtonStyle"
- android:text="@string/btn_decrypt_files"
- android:drawableRight="@drawable/ic_folder_grey_24dp"
- android:drawablePadding="8dp"
- android:gravity="center_vertical" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider" />
+ android:layout_height="match_parent">
<LinearLayout
- android:id="@+id/decrypt_from_clipboard"
android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:clickable="true"
- style="?android:attr/borderlessButtonStyle"
- android:orientation="horizontal"
- android:paddingLeft="8dp"
- android:paddingRight="0dp">
+ android:layout_height="wrap_content"
+ android:paddingTop="4dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical"
+ >
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:text="@string/section_encrypt" />
+
+ <TextView
+ android:id="@+id/encrypt_files"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:clickable="true"
+ style="?android:attr/borderlessButtonStyle"
+ android:text="@string/btn_encrypt_files"
+ android:drawableRight="@drawable/ic_folder_grey_24dp"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@+id/encrypt_text"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:clickable="true"
+ style="?android:attr/borderlessButtonStyle"
+ android:text="@string/btn_encrypt_text"
+ android:drawableRight="@drawable/ic_comment_text_grey600_24dp"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:text="@string/section_decrypt" />
+
+ <TextView
+ android:id="@+id/decrypt_files"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:clickable="true"
+ style="?android:attr/borderlessButtonStyle"
+ android:text="@string/btn_decrypt_files"
+ android:drawableRight="@drawable/ic_folder_grey_24dp"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
<LinearLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:paddingRight="4dp"
- android:gravity="center_vertical"
- android:orientation="vertical">
+ android:id="@+id/decrypt_from_clipboard"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:clickable="true"
+ style="?android:attr/borderlessButtonStyle"
+ android:orientation="horizontal"
+ android:paddingLeft="8dp"
+ android:paddingRight="0dp"
+ tools:ignore="UseCompoundDrawables">
<TextView
android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="match_parent"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
android:text="@string/btn_decrypt_clipboard" />
- </LinearLayout>
+ <ImageView
+ android:id="@+id/clipboard_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:padding="8dp"
+ android:src="@drawable/ic_content_paste_grey_24dp"
+ android:layout_gravity="center_vertical" />
- <ImageView
- android:id="@+id/clipboard_icon"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:padding="8dp"
- android:src="@drawable/ic_content_paste_grey_24dp"
- android:layout_gravity="center_vertical" />
+ </LinearLayout>
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"
+ android:layout_marginBottom="8dp" />
</LinearLayout>
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider"
- android:layout_marginBottom="8dp" />
-
-</LinearLayout> \ No newline at end of file
+</ScrollView>
diff --git a/OpenKeychain/src/main/res/layout/encrypt_text_fragment.xml b/OpenKeychain/src/main/res/layout/encrypt_text_fragment.xml
index 7645918f4..13b85dd88 100644
--- a/OpenKeychain/src/main/res/layout/encrypt_text_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/encrypt_text_fragment.xml
@@ -6,15 +6,16 @@
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
android:fillViewport="true">
<EditText
android:id="@+id/encrypt_text_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="4dp"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
android:gravity="top"
android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
android:hint="@string/encrypt_content_edit_text_hint" />
diff --git a/OpenKeychain/src/main/res/layout/file_list_entry.xml b/OpenKeychain/src/main/res/layout/file_list_entry.xml
index 7f0e1e89e..e1b03f8ae 100644
--- a/OpenKeychain/src/main/res/layout/file_list_entry.xml
+++ b/OpenKeychain/src/main/res/layout/file_list_entry.xml
@@ -55,4 +55,4 @@
android:clickable="true"
android:layout_centerVertical="true"
android:background="?android:selectableItemBackground" />
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/foldable_linearlayout.xml b/OpenKeychain/src/main/res/layout/foldable_linearlayout.xml
index 773a9d416..a7b1e6a53 100644
--- a/OpenKeychain/src/main/res/layout/foldable_linearlayout.xml
+++ b/OpenKeychain/src/main/res/layout/foldable_linearlayout.xml
@@ -25,7 +25,7 @@
android:layout_height="wrap_content"
android:text="@string/none"
android:layout_gravity="center_vertical"
- android:textColor="@color/header_text" />
+ android:textColor="?attr/colorHeaderText" />
</LinearLayout>
<LinearLayout
@@ -34,4 +34,4 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone" />
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/help_about_fragment.xml b/OpenKeychain/src/main/res/layout/help_about_fragment.xml
index 6afab2e12..dbcd97bfd 100644
--- a/OpenKeychain/src/main/res/layout/help_about_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/help_about_fragment.xml
@@ -1,7 +1,11 @@
<?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="match_parent" >
+ android:layout_height="match_parent"
+ android:paddingBottom="0dp"
+ android:paddingLeft="0dp"
+ android:paddingRight="0dp"
+ android:paddingTop="0dp">
<LinearLayout
android:layout_width="match_parent"
@@ -11,17 +15,17 @@
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
- android:scrollbars="vertical" >
+ android:scrollbars="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="horizontal" >
+ android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:orientation="vertical" >
+ android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
@@ -34,7 +38,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical" >
+ android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
@@ -51,12 +55,13 @@
</LinearLayout>
</LinearLayout>
- <org.sufficientlysecure.htmltextview.HtmlTextView
+ <org.sufficientlysecure.htmltextview.HtmlTextView
android:id="@+id/help_about_text"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="16dp"
android:textAppearance="@android:style/TextAppearance.Small" />
</LinearLayout>
-</ScrollView> \ No newline at end of file
+</ScrollView>
diff --git a/OpenKeychain/src/main/res/layout/import_keys_activity.xml b/OpenKeychain/src/main/res/layout/import_keys_activity.xml
index 332b95ce5..28bb8a0b8 100644
--- a/OpenKeychain/src/main/res/layout/import_keys_activity.xml
+++ b/OpenKeychain/src/main/res/layout/import_keys_activity.xml
@@ -25,21 +25,11 @@
android:id="@+id/import_keys_top_container"
android:layout_width="match_parent"
android:layout_height="64dp"
- android:orientation="vertical"
- android:background="@android:color/white" />
+ android:orientation="vertical" />
<View
android:layout_width="match_parent"
- android:layout_height="2dip"
- android:background="?android:attr/listDivider" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="16dp" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="2dip"
+ android:layout_height="1dip"
android:background="?android:attr/listDivider" />
<FrameLayout
@@ -47,22 +37,18 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
- android:layout_weight="1"
- android:background="@android:color/white" />
+ android:layout_weight="1" />
<RelativeLayout
android:id="@+id/import_footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@android:color/white">
+ android:orientation="vertical">
<View
android:id="@+id/import_divider"
android:layout_width="match_parent"
android:layout_height="1dip"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
android:background="?android:attr/listDivider" />
<TextView
@@ -91,4 +77,4 @@
</RelativeLayout>
</LinearLayout>
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/key_list_content.xml b/OpenKeychain/src/main/res/layout/key_list_content.xml
index bd0239da7..146367082 100644
--- a/OpenKeychain/src/main/res/layout/key_list_content.xml
+++ b/OpenKeychain/src/main/res/layout/key_list_content.xml
@@ -23,7 +23,7 @@
<!--<LinearLayout
android:orientation="vertical"
- android:background="@color/holo_gray_bright"
+ android:background="?attr/colorButtonRow"
android:layout_width="match_parent"
android:layout_height="wrap_content">
diff --git a/OpenKeychain/src/main/res/layout/key_list_fragment.xml b/OpenKeychain/src/main/res/layout/key_list_fragment.xml
index ea3426f90..26cedd362 100644
--- a/OpenKeychain/src/main/res/layout/key_list_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/key_list_fragment.xml
@@ -45,8 +45,8 @@
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
- fab:fab_addButtonColorNormal="@color/primary"
- fab:fab_addButtonColorPressed="@color/primary_dark"
+ fab:fab_addButtonColorNormal="?attr/colorPrimary"
+ fab:fab_addButtonColorPressed="?attr/colorPrimaryDark"
fab:fab_addButtonSize="normal"
fab:fab_addButtonPlusIconColor="@color/icons"
fab:fab_expandDirection="up"
@@ -61,9 +61,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_icon="@drawable/ic_qrcode_white_24dp"
- fab:fab_colorNormal="@color/primary"
- fab:fab_colorPressed="@color/primary_dark"
- fab:fab_title="Scan QR Code"
+ fab:fab_colorNormal="?attr/colorPrimary"
+ fab:fab_colorPressed="?attr/colorPrimaryDark"
+ fab:fab_title="@string/key_list_fab_qr_code"
fab:fab_size="mini" />
<com.getbase.floatingactionbutton.FloatingActionButton
@@ -71,9 +71,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_icon="@drawable/ic_cloud_search_24dp"
- fab:fab_colorNormal="@color/primary"
- fab:fab_colorPressed="@color/primary_dark"
- fab:fab_title="Search Cloud"
+ fab:fab_colorNormal="?attr/colorPrimary"
+ fab:fab_colorPressed="?attr/colorPrimaryDark"
+ fab:fab_title="@string/key_list_fab_search"
fab:fab_size="mini" />
<com.getbase.floatingactionbutton.FloatingActionButton
@@ -81,10 +81,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_icon="@drawable/ic_folder_white_24dp"
- fab:fab_colorNormal="@color/primary"
- fab:fab_colorPressed="@color/primary_dark"
- fab:fab_title="Import from File"
+ fab:fab_colorNormal="?attr/colorPrimary"
+ fab:fab_colorPressed="?attr/colorPrimaryDark"
+ fab:fab_title="@string/key_list_fab_import"
fab:fab_size="mini" />
</com.getbase.floatingactionbutton.FloatingActionsMenu>
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/key_list_item.xml b/OpenKeychain/src/main/res/layout/key_list_item.xml
index 6078b898f..80be0ec34 100644
--- a/OpenKeychain/src/main/res/layout/key_list_item.xml
+++ b/OpenKeychain/src/main/res/layout/key_list_item.xml
@@ -11,6 +11,55 @@
android:focusable="false">
<LinearLayout
+ android:id="@+id/key_list_item_dummy"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:focusable="true"
+ android:visibility="gone"
+ android:background="?android:selectableItemBackground"
+ tools:visibility="visible">
+
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingLeft="8dp"
+ android:paddingRight="4dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:text="You don't have any keys yet!"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:text="Click here to create or import one."
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:padding="16dp"
+ android:src="@drawable/ic_key_plus_grey600_24dp"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/key_list_item_data"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
@@ -20,7 +69,8 @@
android:paddingLeft="8dp"
android:paddingRight="4dp"
android:paddingTop="4dp"
- android:paddingBottom="4dp">
+ android:paddingBottom="4dp"
+ tools:visibility="gone">
<TextView
android:id="@+id/key_list_item_name"
diff --git a/OpenKeychain/src/main/res/layout/key_list_selectable_item.xml b/OpenKeychain/src/main/res/layout/key_list_selectable_item.xml
new file mode 100644
index 000000000..311eaabd6
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/key_list_selectable_item.xml
@@ -0,0 +1,22 @@
+<?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_vertical"
+ android:singleLine="true"
+ android:orientation="horizontal"
+ android:descendantFocusability="blocksDescendants"
+ android:focusable="false">
+
+ <CheckBox
+ android:id="@+id/selected"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="false"
+ android:focusable="false"
+ android:focusableInTouchMode="false"
+ android:layout_margin="6dp" />
+
+ <include layout="@layout/key_list_item" />
+
+</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/key_server_preference.xml b/OpenKeychain/src/main/res/layout/key_server_preference.xml
index 5319a3ec0..570d57d9b 100644
--- a/OpenKeychain/src/main/res/layout/key_server_preference.xml
+++ b/OpenKeychain/src/main/res/layout/key_server_preference.xml
@@ -1,94 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<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">
<include
android:id="@+id/toolbar_include"
layout="@layout/toolbar_standalone" />
- <LinearLayout
- android:layout_below="@id/toolbar_include"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical">
-
- <LinearLayout
- android:id="@+id/text_layout"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:orientation="horizontal">
-
- <LinearLayout
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_marginBottom="6sp"
- android:layout_marginLeft="16sp"
- android:layout_marginRight="6sp"
- android:layout_marginTop="6sp"
- android:layout_weight="1"
- android:background="@android:drawable/menuitem_background"
- android:orientation="vertical"
- android:focusable="true">
-
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:focusable="true"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceLarge" />
-
- <TextView
- android:id="@+id/summary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </LinearLayout>
-
- <Button
- android:id="@+id/rotate"
- android:layout_width="wrap_content"
- android:layout_height="31dp"
- android:layout_gravity="center_vertical"
- android:layout_marginLeft="4dip"
- android:layout_marginRight="6dip"
- android:text="rotate"
- android:textColor="#ffffffff"
- android:textStyle="bold"
- android:background="@drawable/button_rounded_blue" />
-
- <ImageButton
- android:id="@+id/add"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_margin="10dp"
- android:src="@drawable/plus"
- android:background="@drawable/button_rounded_green" />
- </LinearLayout>
-
- <View
- android:id="@+id/separator"
- android:layout_width="fill_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider" />
-
- <ScrollView
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- android:orientation="vertical">
-
- <LinearLayout
- android:id="@+id/editors"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" />
- </ScrollView>
-
- </LinearLayout>
-
-</RelativeLayout> \ No newline at end of file
+ <TextView
+ android:layout_marginLeft="6sp"
+ android:layout_marginRight="6sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:text="@string/label_keyservers_title"/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="6sp"
+ android:layout_marginRight="6sp"
+ android:layout_marginBottom="6sp"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="@string/label_keyserver_settings_hint"/>
+
+ <View
+ style="@style/Divider"/>
+
+ <FrameLayout
+ android:id="@+id/keyserver_settings_fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/keyspinner_item.xml b/OpenKeychain/src/main/res/layout/keyspinner_item.xml
deleted file mode 100644
index eea81eba5..000000000
--- a/OpenKeychain/src/main/res/layout/keyspinner_item.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:singleLine="true"
- android:orientation="horizontal"
- android:descendantFocusability="blocksDescendants"
- android:focusable="false"
- android:minHeight="44dip">
-
- <LinearLayout
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_weight="1"
- android:focusable="true"
- android:orientation="vertical"
- android:paddingLeft="8dp"
- android:paddingRight="4dp">
-
- <TextView
- android:id="@+id/keyspinner_key_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/label_main_user_id"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <TextView
- android:id="@+id/keyspinner_key_email"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:text="user@example.com"
- android:textAppearance="?android:attr/textAppearanceSmall" />
-
- <TextView
- android:id="@+id/keyspinner_duplicate"
- android:text="creation: bla"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </LinearLayout>
-
- <ImageView
- android:id="@+id/keyspinner_key_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:src="@drawable/status_signature_revoked_cutout_24dp"
- android:paddingLeft="16dp"
- android:paddingRight="16dp" />
-
-</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/keyspinner_item_none.xml b/OpenKeychain/src/main/res/layout/keyspinner_item_none.xml
new file mode 100644
index 000000000..a7a7a10df
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/keyspinner_item_none.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:orientation="horizontal"
+ android:minHeight="44dip"
+ android:paddingLeft="8dp"
+ android:paddingRight="4dp">
+
+ <TextView
+ android:id="@+id/keyspinner_key_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ tools:text="@string/choice_none"
+ />
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/linked_id_item.xml b/OpenKeychain/src/main/res/layout/linked_id_item.xml
index 4846bd0a4..d6e437dd5 100644
--- a/OpenKeychain/src/main/res/layout/linked_id_item.xml
+++ b/OpenKeychain/src/main/res/layout/linked_id_item.xml
@@ -40,7 +40,6 @@
android:id="@+id/linked_id_comment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textColor="@color/tertiary_text_light"
tools:text="comment"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
diff --git a/OpenKeychain/src/main/res/layout/log_display_item.xml b/OpenKeychain/src/main/res/layout/log_display_item.xml
index d35413185..25249003f 100644
--- a/OpenKeychain/src/main/res/layout/log_display_item.xml
+++ b/OpenKeychain/src/main/res/layout/log_display_item.xml
@@ -15,7 +15,7 @@
android:layout_height="match_parent"
android:id="@+id/log_img"
android:minWidth="10dp"
- android:background="@color/bg_gray" />
+ android:background="?attr/colorLogBackground" />
<TextView
android:layout_width="0dp"
@@ -53,7 +53,7 @@
android:layout_height="match_parent"
android:id="@+id/log_second_img"
android:minWidth="10dp"
- android:background="@color/bg_gray" />
+ android:background="?attr/colorLogBackground" />
<TextView
android:layout_width="0dp"
@@ -67,4 +67,4 @@
android:layout_gravity="center_vertical"/>
</LinearLayout>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/main_activity.xml b/OpenKeychain/src/main/res/layout/main_activity.xml
index 45df0df71..e65969227 100644
--- a/OpenKeychain/src/main/res/layout/main_activity.xml
+++ b/OpenKeychain/src/main/res/layout/main_activity.xml
@@ -13,7 +13,7 @@
android:elevation="4dp"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
- app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
tools:ignore="UnusedAttribute" />
diff --git a/OpenKeychain/src/main/res/layout/nfc_activity.xml b/OpenKeychain/src/main/res/layout/nfc_activity.xml
deleted file mode 100644
index 9acd0676c..000000000
--- a/OpenKeychain/src/main/res/layout/nfc_activity.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <include
- android:id="@+id/toolbar_include"
- layout="@layout/toolbar_standalone" />
-
- <LinearLayout
- android:layout_below="@id/toolbar_include"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:paddingTop="16dp"
- android:paddingBottom="16dp">
-
- <TextView
- android:text="@string/nfc_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@android:style/TextAppearance.Large"
- android:id="@+id/nfc_text"
- android:gravity="center"
- android:layout_gravity="center" />
-
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:src="@drawable/yubikey_phone" />
-
- </LinearLayout>
-</RelativeLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/nfc_operation_activity.xml b/OpenKeychain/src/main/res/layout/nfc_operation_activity.xml
new file mode 100644
index 000000000..9a9738825
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/nfc_operation_activity.xml
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:custom="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
+ android:id="@+id/view_animator"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:animateLayoutChanges="true"
+ android:inAnimation="@anim/fade_in"
+ android:measureAllChildren="false"
+ android:minHeight="?listPreferredItemHeightSmall"
+ android:outAnimation="@anim/fade_out"
+ custom:initialView="3">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/nfc_activity_0_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="24dp"
+ android:layout_marginRight="24dp"
+ android:layout_marginTop="24dp"
+ android:text="@string/nfc_text"
+ android:textAppearance="@android:style/TextAppearance.Medium" />
+
+ <ImageView
+ android:id="@+id/nfc_activity_0_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_below="@+id/nfc_activity_0_text"
+ android:layout_margin="24dp"
+ android:adjustViewBounds="true"
+ android:background="@android:color/transparent"
+ android:src="@drawable/yubikey_phone" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/nfc_activity_1_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="24dp"
+ android:layout_marginRight="24dp"
+ android:layout_marginTop="24dp"
+ android:text="@string/nfc_wait"
+ android:textAppearance="@android:style/TextAppearance.Medium" />
+
+ <ProgressBar
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:padding="8dp" />
+
+ <!-- placeholder to retain dialog size -->
+ <ImageView
+ android:id="@+id/nfc_activity_1_placeholder"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_below="@+id/nfc_activity_1_text"
+ android:layout_margin="24dp"
+ android:adjustViewBounds="true"
+ android:background="@android:color/transparent"
+ android:src="@drawable/yubikey_phone"
+ android:visibility="invisible" />
+
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/nfc_activity_2_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="24dp"
+ android:layout_marginRight="24dp"
+ android:layout_marginTop="24dp"
+ android:text="@string/nfc_finished"
+ android:textAppearance="@android:style/TextAppearance.Medium" />
+
+ <!-- placeholder to retain dialog size -->
+ <ImageView
+ android:id="@+id/nfc_activity_2_placeholder"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_below="@+id/nfc_activity_2_text"
+ android:layout_margin="24dp"
+ android:adjustViewBounds="true"
+ android:background="@android:color/transparent"
+ android:src="@drawable/yubikey_phone"
+ android:visibility="invisible" />
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:adjustViewBounds="true"
+ android:background="@android:color/transparent"
+ android:src="@drawable/ic_check_circle_black_48dp" />
+
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/nfc_activity_3_text_placeholder"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignEnd="@+id/nfc_activity_3_placeholder"
+ android:layout_alignRight="@+id/nfc_activity_3_placeholder"
+ android:layout_marginLeft="24dp"
+ android:layout_marginTop="24dp"
+ android:text=""
+ android:textAppearance="@android:style/TextAppearance.Medium" />
+
+ <!-- placeholder to retain dialog size -->
+ <ImageView
+ android:id="@+id/nfc_activity_3_placeholder"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_below="@+id/nfc_activity_3_text_placeholder"
+ android:layout_margin="24dp"
+ android:adjustViewBounds="true"
+ android:background="@android:color/transparent"
+ android:src="@drawable/yubikey_phone"
+ android:visibility="invisible" />
+
+ <TextView
+ android:id="@+id/nfc_activity_3_error_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:layout_margin="24dp"
+ android:textAppearance="@android:style/TextAppearance.Medium"
+ android:textColor="@color/android_red_dark"
+ tools:text="Error text" />
+
+ <Button
+ android:id="@+id/nfc_activity_3_error_try_again"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/nfc_activity_3_placeholder"
+ android:layout_margin="8dp"
+ android:text="@string/error_nfc_try_again" />
+
+ </RelativeLayout>
+
+ </org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/passphrase_repeat_dialog.xml b/OpenKeychain/src/main/res/layout/passphrase_repeat_dialog.xml
index ffc5266b5..9ce8f01fa 100644
--- a/OpenKeychain/src/main/res/layout/passphrase_repeat_dialog.xml
+++ b/OpenKeychain/src/main/res/layout/passphrase_repeat_dialog.xml
@@ -40,9 +40,9 @@
android:layout_gravity="end|center_vertical"
custom:strength="medium"
custom:showGuides="false"
- custom:color_fail="@color/android_red_light"
- custom:color_weak="@color/android_orange_light"
- custom:color_strong="@color/android_green_light" />
+ custom:color_fail="@color/password_strength_low"
+ custom:color_weak="@color/password_strength_medium"
+ custom:color_strong="@color/password_strength_high" />
</FrameLayout>
@@ -57,4 +57,4 @@
android:ems="10"
android:layout_gravity="center_horizontal" />
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/preference_toolbar_activity.xml b/OpenKeychain/src/main/res/layout/preference_toolbar.xml
index f17bc30bc..2f54b954e 100644
--- a/OpenKeychain/src/main/res/layout/preference_toolbar_activity.xml
+++ b/OpenKeychain/src/main/res/layout/preference_toolbar.xml
@@ -1,7 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:orientation="vertical">
<include
android:id="@+id/toolbar_include"
diff --git a/OpenKeychain/src/main/res/layout/recipient_selection_list_entry.xml b/OpenKeychain/src/main/res/layout/recipient_selection_list_entry.xml
index a9e86057c..2b39cf54c 100644
--- a/OpenKeychain/src/main/res/layout/recipient_selection_list_entry.xml
+++ b/OpenKeychain/src/main/res/layout/recipient_selection_list_entry.xml
@@ -60,4 +60,4 @@
tools:src="@drawable/status_signature_revoked_cutout_24dp"
/>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/settings_keyserver_fragment.xml b/OpenKeychain/src/main/res/layout/settings_keyserver_fragment.xml
new file mode 100644
index 000000000..57c81a433
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/settings_keyserver_fragment.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<android.support.v7.widget.RecyclerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyserver_recycler_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" /> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/settings_keyserver_item.xml b/OpenKeychain/src/main/res/layout/settings_keyserver_item.xml
new file mode 100644
index 000000000..338104bda
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/settings_keyserver_item.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/outer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?listPreferredItemHeight">
+
+ <LinearLayout
+ android:id="@+id/keyserver_layout"
+ android:padding="6sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toLeftOf="@+id/drag_handle"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/selected_keyserver_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/label_selected_keyserver_title"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ android:visibility="gone"/>
+
+ <TextView
+ android:id="@+id/keyserver_tv"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/drag_handle"
+ android:layout_width="?listPreferredItemHeight"
+ android:layout_height="?listPreferredItemHeight"
+ android:scaleType="center"
+ android:layout_centerVertical="true"
+ android:layout_alignParentRight="true"
+ android:src="@drawable/ic_reorder_grey_500_24dp" />
+
+ <View
+ android:layout_alignParentBottom="true"
+ style="@style/Divider"/>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/toolbar_inner_layout.xml b/OpenKeychain/src/main/res/layout/toolbar_inner_layout.xml
index 047225394..b3228ea06 100644
--- a/OpenKeychain/src/main/res/layout/toolbar_inner_layout.xml
+++ b/OpenKeychain/src/main/res/layout/toolbar_inner_layout.xml
@@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:background="@color/transparent"
- app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
tools:ignore="UnusedAttribute" />
</merge> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/toolbar_inner_layout_white.xml b/OpenKeychain/src/main/res/layout/toolbar_inner_layout_white.xml
index a626efb09..477967edd 100644
--- a/OpenKeychain/src/main/res/layout/toolbar_inner_layout_white.xml
+++ b/OpenKeychain/src/main/res/layout/toolbar_inner_layout_white.xml
@@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:background="@color/transparent"
- app:theme="@style/ThemeOverlay.AppCompat.ActionBar"
+ android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
tools:ignore="UnusedAttribute" />
</merge> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/toolbar_result_decrypt.xml b/OpenKeychain/src/main/res/layout/toolbar_result_decrypt.xml
index fe421449a..1afa76b60 100644
--- a/OpenKeychain/src/main/res/layout/toolbar_result_decrypt.xml
+++ b/OpenKeychain/src/main/res/layout/toolbar_result_decrypt.xml
@@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/toolbar_include"
android:elevation="4dp"
- android:background="@color/white"
+ android:background="?attr/colorBrightToolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
@@ -26,8 +26,7 @@
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="4dp"
- android:paddingBottom="4dp"
- android:animateLayoutChanges="true">
+ android:paddingBottom="4dp">
<LinearLayout
android:orientation="horizontal"
diff --git a/OpenKeychain/src/main/res/layout/toolbar_standalone_white.xml b/OpenKeychain/src/main/res/layout/toolbar_standalone_white.xml
index d4269c2ba..8fddfe4ba 100644
--- a/OpenKeychain/src/main/res/layout/toolbar_standalone_white.xml
+++ b/OpenKeychain/src/main/res/layout/toolbar_standalone_white.xml
@@ -2,7 +2,7 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toolbar_include"
android:elevation="4dp"
- android:background="@color/white"
+ android:background="?attr/colorBrightToolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
diff --git a/OpenKeychain/src/main/res/layout/toolbar_tabs.xml b/OpenKeychain/src/main/res/layout/toolbar_tabs.xml
index 91efda682..ed42ef52e 100644
--- a/OpenKeychain/src/main/res/layout/toolbar_tabs.xml
+++ b/OpenKeychain/src/main/res/layout/toolbar_tabs.xml
@@ -17,8 +17,8 @@
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
- android:textColor="@color/tab_text"
- app:pstsTextColorSelected="@color/tab_text_selected"
- app:pstsIndicatorColor="@color/tab_indicator" />
+ android:textColor="?attr/colorTabText"
+ app:pstsTextColorSelected="?attr/colorTabTextSelected"
+ app:pstsIndicatorColor="?attr/colorTabIndicator" />
</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/view_key_activity.xml b/OpenKeychain/src/main/res/layout/view_key_activity.xml
index 4dbd793ea..3913122bc 100644
--- a/OpenKeychain/src/main/res/layout/view_key_activity.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_activity.xml
@@ -1,197 +1,225 @@
<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- xmlns:fab="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/main_content"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true">
- <RelativeLayout
- android:id="@+id/toolbar_big"
+ <android.support.design.widget.AppBarLayout
+ android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
- android:layout_height="@dimen/huge_toolbar"
- android:elevation="4dp"
- android:background="?attr/colorPrimary"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/view_key_photo"
- android:visibility="gone"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_below="@+id/status_bar"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:src="@drawable/ic_launcher"
- android:baselineAlignBottom="false"
- android:cropToPadding="false"
- android:focusable="false"
- android:adjustViewBounds="false"
- android:layout_alignParentTop="false"
- android:scaleType="centerCrop" />
-
- <ImageView
- android:id="@+id/status_bar"
- android:layout_width="match_parent"
- android:layout_height="@dimen/statusbar_height"
- android:background="?attr/colorPrimary" />
+ android:layout_height="wrap_content"
+ android:background="@color/primary"
+ android:fitsSystemWindows="true"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
- <android.support.v7.widget.Toolbar
- android:id="@+id/toolbar"
- android:background="@color/transparent"
- android:layout_below="@+id/status_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?attr/actionBarSize"
- android:overScrollMode="always"
- app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
- app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
- tools:ignore="UnusedAttribute"
- android:transitionGroup="false"
- android:touchscreenBlocksFocus="false" />
-
- <TextView
- android:id="@+id/view_key_name"
+ <android.support.design.widget.CollapsingToolbarLayout
+ android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="48dp"
- android:layout_marginStart="48dp"
- android:layout_marginRight="48dp"
- android:layout_marginEnd="48dp"
- android:text=""
- android:textColor="@color/icons"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:layout_above="@+id/view_key_status" />
-
- <TextView
- android:id="@+id/view_key_status"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="48dp"
- android:layout_marginStart="48dp"
- android:layout_marginRight="48dp"
- android:layout_marginEnd="48dp"
- android:text=""
- android:textColor="@color/tab_text"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_above="@+id/toolbar2" />
-
- <LinearLayout
- android:id="@+id/toolbar2"
- android:orientation="horizontal"
- android:layout_alignParentBottom="true"
- android:layout_width="wrap_content"
- android:minHeight="?attr/actionBarSize"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_marginLeft="32dp"
- android:layout_alignParentRight="true"
- android:layout_alignParentEnd="true">
-
- <ImageButton
- android:id="@+id/view_key_action_encrypt_files"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:visibility="invisible"
- style="?android:attr/borderlessButtonStyle"
- android:src="@drawable/ic_action_encrypt_file_24dp" />
-
- <ImageButton
- android:id="@+id/view_key_action_encrypt_text"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:visibility="invisible"
- style="?android:attr/borderlessButtonStyle"
- android:src="@drawable/ic_action_encrypt_text_24dp" />
-
- <ImageButton
- android:id="@+id/view_key_action_nfc"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:visibility="invisible"
- style="?android:attr/borderlessButtonStyle"
- android:src="@drawable/ic_nfc_white_24dp" />
-
- </LinearLayout>
-
- <ImageView
- android:id="@+id/view_key_status_image"
- android:layout_width="96dp"
- android:visibility="invisible"
- android:src="@drawable/status_signature_unverified_cutout_96dp"
- android:layout_height="96dp"
- android:layout_above="@id/toolbar2"
- android:layout_alignParentRight="true"
- android:layout_alignParentEnd="true"
- android:layout_marginRight="16dp" />
-
- <android.support.v7.widget.CardView
- android:id="@+id/view_key_qr_code_layout"
- android:transitionName="qr_code"
- android:visibility="visible"
- android:layout_above="@id/toolbar2"
- android:layout_alignParentRight="true"
- android:layout_alignParentEnd="true"
- android:layout_marginRight="20dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clickable="true"
- android:foreground="?android:attr/selectableItemBackground"
- card_view:cardBackgroundColor="@android:color/white"
- card_view:cardElevation="2dp"
- card_view:cardUseCompatPadding="true"
- card_view:cardCornerRadius="4dp">
-
- <ImageView
- android:id="@+id/view_key_qr_code"
- android:layout_width="96dp"
- android:layout_height="96dp" />
- </android.support.v7.widget.CardView>
-
- </RelativeLayout>
-
- <LinearLayout
- android:id="@+id/body"
- android:layout_below="@id/toolbar_big"
- android:orientation="vertical"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
+ app:contentScrim="@color/primary"
+ app:expandedTitleMarginBottom="102dp"
+ app:expandedTitleMarginEnd="128dp"
+ app:expandedTitleMarginStart="47dp"
+ app:expandedTitleTextAppearance="@style/TextAppearance.AppCompat.Headline"
+ app:layout_scrollFlags="scroll|exitUntilCollapsed">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="238dp"
+ android:fitsSystemWindows="true"
+ app:layout_collapseMode="parallax">
+
+ <FrameLayout
+ android:id="@+id/view_key_photo_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="false"
+ android:fitsSystemWindows="true"
+ android:visibility="gone">
+
+ <ImageView
+ android:id="@+id/view_key_photo"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:adjustViewBounds="false"
+ android:baselineAlignBottom="false"
+ android:cropToPadding="false"
+ android:fitsSystemWindows="true"
+ android:focusable="false"
+ android:scaleType="centerCrop"
+ android:src="@drawable/ic_launcher" />
+
+ <!-- text protection scrim -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="bottom"
+ android:background="@drawable/scrim_bottom" />
+
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/view_key_status"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/toolbar2"
+ android:layout_marginEnd="48dp"
+ android:layout_marginLeft="48dp"
+ android:layout_marginRight="48dp"
+ android:layout_marginStart="48dp"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?attr/colorTabText"
+ tools:text="My Key" />
+
+ <LinearLayout
+ android:id="@+id/toolbar2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginLeft="32dp"
+ android:minHeight="?attr/actionBarSize"
+ android:orientation="horizontal">
+
+ <ImageButton
+ android:id="@+id/view_key_action_encrypt_files"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:src="@drawable/ic_action_encrypt_file_24dp"
+ android:visibility="invisible"
+ tools:visibility="visible" />
+
+ <ImageButton
+ android:id="@+id/view_key_action_encrypt_text"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:src="@drawable/ic_action_encrypt_text_24dp"
+ android:visibility="invisible"
+ tools:visibility="visible" />
+
+ <ImageButton
+ android:id="@+id/view_key_action_nfc"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:src="@drawable/ic_nfc_white_24dp"
+ android:visibility="invisible"
+ tools:visibility="visible" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/view_key_status_image"
+ android:layout_width="96dp"
+ android:layout_height="96dp"
+ android:layout_above="@id/toolbar2"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_marginRight="16dp"
+ android:src="@drawable/status_signature_unverified_cutout_96dp"
+ android:visibility="invisible"
+ tools:visibility="visible" />
+
+ <android.support.v7.widget.CardView
+ android:id="@+id/view_key_qr_code_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_above="@id/toolbar2"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_marginRight="20dp"
+ android:clickable="true"
+ android:foreground="?android:attr/selectableItemBackground"
+ android:transitionName="qr_code"
+ android:visibility="visible"
+ app:layout_collapseMode="parallax"
+ card_view:cardBackgroundColor="@android:color/white"
+ card_view:cardCornerRadius="4dp"
+ card_view:cardElevation="2dp"
+ card_view:cardUseCompatPadding="true"
+ tools:visibility="invisible">
+
+ <ImageView
+ android:id="@+id/view_key_qr_code"
+ android:layout_width="96dp"
+ android:layout_height="96dp" />
+ </android.support.v7.widget.CardView>
+
+ </RelativeLayout>
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:elevation="4dp"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+ android:touchscreenBlocksFocus="false"
+ android:transitionGroup="false"
+ app:layout_collapseMode="pin"
+ app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
+ tools:ignore="UnusedAttribute" />
+ </android.support.design.widget.CollapsingToolbarLayout>
+
+ </android.support.design.widget.AppBarLayout>
+
+ <android.support.v4.widget.FlingNestedScrollView
+ android:id="@+id/scroll"
android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider"
- android:visibility="gone"
- android:id="@+id/view_key_status_divider" />
+ android:layout_height="match_parent"
+ android:layout_gravity="fill_vertical"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior">
- <FrameLayout
+ <LinearLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:orientation="vertical">
<FrameLayout
android:id="@+id/view_key_fragment"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content" />
+
+ <FrameLayout
+ android:id="@+id/view_key_keybase_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <!--
+ placeholder to improve the scrolling.
+ Somehow the content must be large enough to enable scrolling on NestedScrollView
+ -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="230dp"
android:orientation="vertical" />
- </FrameLayout>
- </LinearLayout>
+ </LinearLayout>
+
+ </android.support.v4.widget.FlingNestedScrollView>
- <com.getbase.floatingactionbutton.FloatingActionButton
+ <android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
- android:layout_alignBottom="@id/toolbar_big"
- android:layout_alignParentRight="true"
- android:layout_marginRight="20dp"
- android:layout_marginBottom="-40dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_margin="24dp"
+ android:src="@drawable/ic_qrcode_white_24dp"
android:visibility="invisible"
- android:elevation="4dp"
- fab:fab_icon="@drawable/ic_qrcode_white_24dp"
- fab:fab_colorNormal="@color/fab"
- fab:fab_colorPressed="@color/fab_pressed" />
+ app:layout_anchor="@id/app_bar_layout"
+ app:layout_anchorGravity="bottom|right|end"
+ tools:visibility="visible" />
-</RelativeLayout> \ No newline at end of file
+</android.support.design.widget.CoordinatorLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/view_key_adv_certs_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_adv_certs_fragment.xml
index d512477aa..3b9215d50 100644
--- a/OpenKeychain/src/main/res/layout/view_key_adv_certs_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_adv_certs_fragment.xml
@@ -10,7 +10,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@color/holo_gray_bright"
+ android:background="?attr/colorButtonRow"
android:padding="8dp"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="@string/certs_text"
@@ -45,4 +45,4 @@
</FrameLayout>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/view_key_adv_keybase_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_adv_keybase_fragment.xml
index b6679524f..75d56e092 100644
--- a/OpenKeychain/src/main/res/layout/view_key_adv_keybase_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_adv_keybase_fragment.xml
@@ -1,126 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@color/holo_gray_bright"
- android:padding="8dp"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:text="@string/key_trust_header_text"
- android:gravity="center_horizontal" />
-
- <View
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingBottom="16dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="16dp">
+
+ <android.support.v7.widget.CardView
+ android:id="@+id/card_view"
android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider" />
-
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ card_view:cardBackgroundColor="?attr/colorCardViewBackground"
+ card_view:cardCornerRadius="4dp"
+ card_view:cardElevation="2dp"
+ card_view:cardUseCompatPadding="true">
- <!-- focusable and related properties to workaround http://stackoverflow.com/q/16182331-->
- <LinearLayout
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
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="0dp"
- android:layout_marginTop="8dp"
- android:text="@string/section_should_you_trust"
- android:layout_weight="1" />
+ android:orientation="vertical">
<TextView
- android:id="@+id/view_key_trust_readout"
+ style="@style/CardViewHeader"
android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_marginTop="14dp"
- android:layout_marginLeft="8dp"
- android:layout_weight="1"
- style="?android:attr/textAppearanceMedium" />
+ android:layout_height="wrap_content"
+ android:text="@string/section_keybase_proofs" />
<TextView
- style="@style/SectionHeader"
android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_cloud_evidence"
- android:layout_weight="1" />
+ android:layout_height="wrap_content"
+ android:background="?attr/colorButtonRow"
+ android:gravity="center_horizontal"
+ android:padding="8dp"
+ android:text="@string/key_trust_header_text"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
<LinearLayout
- android:id="@+id/view_key_trust_search_cloud"
android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:clickable="true"
- android:paddingRight="4dp"
- style="?android:attr/borderlessButtonStyle"
- android:orientation="horizontal">
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingBottom="16dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="16dp">
<TextView
- android:paddingLeft="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:text="@string/key_trust_start_cloud_search"
- android:layout_weight="1"
- android:gravity="center_vertical" />
+ android:id="@+id/view_key_trust_cloud_narrative"
+ style="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginBottom="14dp"
+ android:layout_marginLeft="8dp"
+ android:layout_weight="1" />
+
+ <TableLayout
+ android:id="@+id/view_key_proof_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
- <ImageView
+ <TextView
+ android:id="@+id/view_key_proof_verify_header"
+ style="@style/SectionHeader"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:padding="8dp"
- android:src="@drawable/ic_action_search_cloud"
- android:layout_gravity="center_vertical" />
-
- </LinearLayout>
-
-
- <TextView
- android:id="@+id/view_key_trust_cloud_narrative"
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_marginTop="14dp"
- android:layout_marginBottom="14dp"
- android:layout_marginLeft="8dp"
- android:layout_weight="1"
- style="?android:attr/textAppearanceMedium" />
-
- <TableLayout
- android:id="@+id/view_key_proof_list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="0dp"
+ android:layout_marginTop="16dp"
+ android:layout_weight="1"
+ android:text="@string/section_proof_details" />
- <TextView
- android:id="@+id/view_key_proof_verify_header"
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_marginTop="16dp"
- android:text="@string/section_proof_details"
- android:layout_weight="1" />
+ <TextView
+ android:id="@+id/view_key_proof_verify_detail"
+ style="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginLeft="8dp"
+ android:layout_marginTop="14dp"
+ android:layout_weight="1" />
- <TextView
- android:id="@+id/view_key_proof_verify_detail"
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_marginTop="14dp"
- android:layout_marginLeft="8dp"
- android:layout_weight="1"
- style="?android:attr/textAppearanceMedium" />
+ </LinearLayout>
</LinearLayout>
- </ScrollView>
+ </android.support.v7.widget.CardView>
+
</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/view_key_adv_keybase_proof.xml b/OpenKeychain/src/main/res/layout/view_key_adv_keybase_proof.xml
index 0ffd151c1..033282305 100644
--- a/OpenKeychain/src/main/res/layout/view_key_adv_keybase_proof.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_adv_keybase_proof.xml
@@ -7,7 +7,7 @@
android:layout_height="wrap_content"
android:paddingLeft="6dip"
android:text="1."
- style="?android:attr/textAppearanceMedium" />
+ style="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/proof_text"
@@ -15,5 +15,5 @@
android:layout_height="wrap_content"
android:paddingLeft="6dip"
android:text="Posts to twitter as Timbray"
- style="?android:attr/textAppearanceMedium" />
+ style="?android:attr/textAppearanceSmall" />
</TableRow>
diff --git a/OpenKeychain/src/main/res/layout/view_key_adv_share_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_adv_share_fragment.xml
index c08d66cc1..d82506e74 100644
--- a/OpenKeychain/src/main/res/layout/view_key_adv_share_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_adv_share_fragment.xml
@@ -126,6 +126,23 @@
android:background="?android:attr/listDivider" />
<ImageButton
+ android:id="@+id/view_key_action_key_export"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:padding="8dp"
+ android:src="@drawable/ic_save_grey_24dp"
+ android:layout_gravity="center_vertical"
+ android:background="?android:selectableItemBackground" />
+
+ <View
+ android:layout_width="1dip"
+ android:layout_height="match_parent"
+ android:gravity="right"
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="8dp"
+ android:background="?android:attr/listDivider" />
+
+ <ImageButton
android:id="@+id/view_key_action_key_nfc"
android:layout_width="wrap_content"
android:layout_height="match_parent"
diff --git a/OpenKeychain/src/main/res/layout/view_key_adv_user_id_item.xml b/OpenKeychain/src/main/res/layout/view_key_adv_user_id_item.xml
index 1c55a1446..a9ebe43d3 100644
--- a/OpenKeychain/src/main/res/layout/view_key_adv_user_id_item.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_adv_user_id_item.xml
@@ -34,7 +34,7 @@
android:id="@+id/user_id_item_comment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="@color/tertiary_text_light"
+ android:textColor="?attr/colorTertiaryText"
android:text="comment"
android:textAppearance="?android:attr/textAppearanceSmall" />
diff --git a/OpenKeychain/src/main/res/layout/view_key_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_fragment.xml
index 294ac59a7..f060636a2 100644
--- a/OpenKeychain/src/main/res/layout/view_key_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_fragment.xml
@@ -1,152 +1,148 @@
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout
+ android:layout_height="wrap_content"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:paddingBottom="16dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="16dp">
+
+ <android.support.v7.widget.CardView
+ android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingTop="16dp"
- android:paddingBottom="16dp"
- android:paddingLeft="16dp"
- android:paddingRight="16dp">
-
- <android.support.v7.widget.CardView
- android:id="@+id/card_identities"
- android:layout_gravity="center"
+ android:layout_gravity="center"
+ card_view:cardBackgroundColor="?attr/colorCardViewBackground"
+ card_view:cardCornerRadius="4dp"
+ card_view:cardElevation="2dp"
+ card_view:cardUseCompatPadding="true">
+
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- card_view:cardBackgroundColor="@android:color/white"
- card_view:cardElevation="2dp"
- card_view:cardUseCompatPadding="true"
- card_view:cardCornerRadius="4dp">
+ android:orientation="vertical">
- <LinearLayout
- android:layout_width="match_parent"
+ <TextView
+ style="@style/CardViewHeader"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:text="@string/section_user_ids" />
- <TextView
- style="@style/CardViewHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/section_user_ids"/>
+ <org.sufficientlysecure.keychain.ui.widget.FixedListView
+ android:id="@+id/view_key_user_ids"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp" />
- <org.sufficientlysecure.keychain.ui.widget.FixedListView
- android:id="@+id/view_key_user_ids"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"/>
- </LinearLayout>
+ </LinearLayout>
- </android.support.v7.widget.CardView>
+ </android.support.v7.widget.CardView>
- <android.support.v7.widget.CardView
- android:id="@+id/card_linked_ids"
- android:transitionName="card_linked_ids"
- android:layout_gravity="center"
+ <android.support.v7.widget.CardView
+ android:id="@+id/card_linked_ids"
+ android:transitionName="card_linked_ids"
+ android:layout_gravity="center"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ tools:visibility="visible"
+ card_view:cardBackgroundColor="@android:color/white"
+ card_view:cardElevation="2dp"
+ card_view:cardUseCompatPadding="true"
+ card_view:cardCornerRadius="4dp">
+
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:visibility="gone"
- tools:visibility="visible"
- card_view:cardBackgroundColor="@android:color/white"
- card_view:cardElevation="2dp"
- card_view:cardUseCompatPadding="true"
- card_view:cardCornerRadius="4dp">
+ android:orientation="vertical">
- <LinearLayout
- android:layout_width="match_parent"
+ <TextView
+ style="@style/CardViewHeader"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView
- style="@style/CardViewHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/section_linked_identities" />
-
- <org.sufficientlysecure.keychain.ui.widget.FixedListView
- android:id="@+id/view_key_linked_ids"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp" />
-
- <TextView
- android:id="@+id/view_key_linked_ids_expander"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="4dp"
- android:layout_marginBottom="4dp"
- android:gravity="center_vertical"
- android:drawableTop="@drawable/divider"
- android:drawableRight="@drawable/ic_expand_more_black_24dp"
- android:drawableEnd="@drawable/ic_expand_more_black_24dp"
- android:drawablePadding="3dp"
- android:clickable="true"
- android:textColor="@color/tertiary_text_light"
- android:text="@string/linked_ids_more_unknown"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:background="?android:selectableItemBackground"
- android:visibility="gone"
- tools:visibility="visible"
- />
+ android:text="@string/section_linked_identities" />
- </LinearLayout>
-
- </android.support.v7.widget.CardView>
+ <org.sufficientlysecure.keychain.ui.widget.FixedListView
+ android:id="@+id/view_key_linked_ids"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp" />
- <android.support.v7.widget.CardView
- android:id="@+id/linked_system_contact_card"
- android:layout_gravity="center"
+ <TextView
+ android:id="@+id/view_key_linked_ids_expander"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:gravity="center_vertical"
+ android:drawableTop="@drawable/divider"
+ android:drawableRight="@drawable/ic_expand_more_black_24dp"
+ android:drawableEnd="@drawable/ic_expand_more_black_24dp"
+ android:drawablePadding="3dp"
+ android:clickable="true"
+ android:text="@string/linked_ids_more_unknown"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:background="?android:selectableItemBackground"
+ android:visibility="gone"
+ tools:visibility="visible"
+ />
+
+ </LinearLayout>
+
+ </android.support.v7.widget.CardView>
+
+ <android.support.v7.widget.CardView
+ android:id="@+id/linked_system_contact_card"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:visibility="gone"
+ card_view:cardBackgroundColor="?attr/colorCardViewBackground"
+ card_view:cardCornerRadius="4dp"
+ card_view:cardElevation="2dp"
+ card_view:cardUseCompatPadding="true">
+
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:visibility="gone"
- card_view:cardBackgroundColor="@android:color/white"
- card_view:cardElevation="2dp"
- card_view:cardUseCompatPadding="true"
- card_view:cardCornerRadius="4dp">
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/CardViewHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/section_linked_system_contact" />
<LinearLayout
+ android:id='@+id/system_contact_layout'
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:background="?android:selectableItemBackground"
+ android:clickable="true"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/system_contact_picture"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_margin="6dp"
+ android:scaleType="centerCrop"
+ android:src="@drawable/ic_person_grey_48dp" />
<TextView
- style="@style/CardViewHeader"
+ android:id="@+id/system_contact_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/section_linked_system_contact"/>
-
- <LinearLayout
- android:id='@+id/system_contact_layout'
- android:clickable="true"
- android:background="?android:selectableItemBackground"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/system_contact_picture"
- android:layout_margin="6dp"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:scaleType="centerCrop"
- android:src="@drawable/ic_person_grey_48dp"/>
-
- <TextView
- android:id="@+id/system_contact_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:text="@string/view_key_fragment_no_system_contact"/>
- </LinearLayout>
+ android:layout_gravity="center_vertical"
+ android:text="@string/view_key_fragment_no_system_contact"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
- </android.support.v7.widget.CardView>
- </LinearLayout>
+ </LinearLayout>
+
+ </android.support.v7.widget.CardView>
-</ScrollView>
+</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/view_key_yubikey.xml b/OpenKeychain/src/main/res/layout/view_key_yubikey.xml
index 83272ef4e..57f93493d 100644
--- a/OpenKeychain/src/main/res/layout/view_key_yubikey.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_yubikey.xml
@@ -18,7 +18,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:transitionName="card"
- card_view:cardBackgroundColor="@android:color/white"
+ card_view:cardBackgroundColor="?attr/colorCardViewBackground"
card_view:cardElevation="2dp"
card_view:cardUseCompatPadding="true"
card_view:cardCornerRadius="4dp"
diff --git a/OpenKeychain/src/main/res/menu/decrypt_item_context_menu.xml b/OpenKeychain/src/main/res/menu/decrypt_item_context_menu.xml
new file mode 100644
index 000000000..97bf65c0d
--- /dev/null
+++ b/OpenKeychain/src/main/res/menu/decrypt_item_context_menu.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item
+ android:id="@+id/view_log"
+ android:title="@string/btn_view_log"
+ android:icon="@drawable/ic_view_list_grey_24dp"
+ />
+
+ <item
+ android:id="@+id/decrypt_save"
+ android:title="@string/btn_save_file"
+ android:icon="@drawable/ic_action_encrypt_file_24dp"
+ />
+
+ <item
+ android:id="@+id/decrypt_delete"
+ android:title="@string/btn_delete_original"
+ android:icon="@drawable/ic_delete_grey_24dp"
+ />
+
+</menu> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/menu/encrypt_activity.xml b/OpenKeychain/src/main/res/menu/encrypt_activity.xml
new file mode 100644
index 000000000..c71e426c5
--- /dev/null
+++ b/OpenKeychain/src/main/res/menu/encrypt_activity.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item
+ android:id="@+id/check_use_symmetric"
+ android:title="@string/label_symmetric"
+ android:checkable="true"
+ android:orderInCategory="1" />
+
+</menu> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/menu/encrypt_file_fragment.xml b/OpenKeychain/src/main/res/menu/encrypt_file_fragment.xml
index d6ed726fa..e719487a7 100644
--- a/OpenKeychain/src/main/res/menu/encrypt_file_fragment.xml
+++ b/OpenKeychain/src/main/res/menu/encrypt_file_fragment.xml
@@ -6,39 +6,45 @@
android:id="@+id/encrypt_save"
android:title="@string/btn_encrypt_save_file"
android:icon="@drawable/ic_action_encrypt_save_24dp"
- app:showAsAction="always" />
+ android:orderInCategory="0"
+ app:showAsAction="ifRoom" />
<item
android:id="@+id/encrypt_share"
android:title="@string/btn_encrypt_share_file"
android:icon="@drawable/ic_action_encrypt_share_24dp"
- app:showAsAction="always" />
+ android:orderInCategory="0"
+ app:showAsAction="ifRoom" />
<item
- android:id="@+id/check_use_symmetric"
- android:title="@string/label_symmetric"
- android:checkable="true" />
+ android:id="@+id/encrypt_copy"
+ android:title="@string/btn_copy_encrypted_signed"
+ android:icon="@drawable/ic_action_encrypt_copy_24dp"
+ android:orderInCategory="0"
+ app:showAsAction="ifRoom" />
<item
android:id="@+id/check_delete_after_encrypt"
android:title="@string/label_delete_after_encryption"
+ android:orderInCategory="1"
android:checkable="true" />
<item
android:id="@+id/check_enable_compression"
android:title="@string/label_enable_compression"
- android:checked="true"
+ android:orderInCategory="1"
android:checkable="true" />
<item
android:id="@+id/check_encrypt_filenames"
android:title="@string/label_encrypt_filenames"
- android:checked="true"
+ android:orderInCategory="1"
android:checkable="true" />
<item
android:id="@+id/check_use_armor"
android:title="@string/label_file_ascii_armor"
+ android:orderInCategory="1"
android:checkable="true" />
<!--<item-->
diff --git a/OpenKeychain/src/main/res/menu/encrypt_text_fragment.xml b/OpenKeychain/src/main/res/menu/encrypt_text_fragment.xml
index 5a262fdd8..80b78457d 100644
--- a/OpenKeychain/src/main/res/menu/encrypt_text_fragment.xml
+++ b/OpenKeychain/src/main/res/menu/encrypt_text_fragment.xml
@@ -6,22 +6,20 @@
android:id="@+id/encrypt_copy"
android:title="@string/btn_copy_encrypted_signed"
android:icon="@drawable/ic_action_encrypt_copy_24dp"
- app:showAsAction="always" />
+ android:orderInCategory="1"
+ app:showAsAction="ifRoom" />
<item
android:id="@+id/encrypt_share"
android:title="@string/btn_share_encrypted_signed"
android:icon="@drawable/ic_action_encrypt_share_24dp"
- app:showAsAction="always" />
-
- <item
- android:id="@+id/check_use_symmetric"
- android:title="@string/label_symmetric"
- android:checkable="true" />
+ android:orderInCategory="1"
+ app:showAsAction="ifRoom" />
<item
android:id="@+id/check_enable_compression"
android:title="@string/label_enable_compression"
+ android:orderInCategory="1"
android:checked="true"
android:checkable="true" />
diff --git a/OpenKeychain/src/main/res/menu/key_list.xml b/OpenKeychain/src/main/res/menu/key_list.xml
index c4797d5f7..d3a2bd0fd 100644
--- a/OpenKeychain/src/main/res/menu/key_list.xml
+++ b/OpenKeychain/src/main/res/menu/key_list.xml
@@ -10,11 +10,6 @@
app:showAsAction="collapseActionView|always" />
<item
- android:id="@+id/menu_key_list_export"
- android:title="@string/menu_export_all_keys"
- app:showAsAction="never" />
-
- <item
android:id="@+id/menu_key_list_create"
android:title="@string/menu_manage_keys"
app:showAsAction="never" />
diff --git a/OpenKeychain/src/main/res/menu/key_list_multi.xml b/OpenKeychain/src/main/res/menu/key_list_multi.xml
index 7fdf4a5c1..26bc9f98f 100644
--- a/OpenKeychain/src/main/res/menu/key_list_multi.xml
+++ b/OpenKeychain/src/main/res/menu/key_list_multi.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:tools="http://schemas.android.com/tools"
- xmlns:android="http://schemas.android.com/apk/res/android">
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
<item
android:id="@+id/menu_key_list_multi_encrypt"
@@ -8,21 +8,8 @@
android:title="@string/menu_encrypt_to" />
<item
- android:id="@+id/menu_key_list_multi_export"
- android:showAsAction="never"
- tools:ignore="AppCompatResource"
- android:title="@string/menu_export_key" />
-
- <item
android:id="@+id/menu_key_list_multi_delete"
- android:showAsAction="never"
- tools:ignore="AppCompatResource"
+ android:icon="@drawable/ic_delete_white_24dp"
android:title="@string/menu_delete_key" />
- <item
- android:id="@+id/menu_key_list_multi_select_all"
- android:showAsAction="never"
- tools:ignore="AppCompatResource"
- android:title="@string/menu_select_all" />
-
</menu>
diff --git a/OpenKeychain/src/main/res/menu/key_view.xml b/OpenKeychain/src/main/res/menu/key_view.xml
index 3d1b02958..14ea099f4 100644
--- a/OpenKeychain/src/main/res/menu/key_view.xml
+++ b/OpenKeychain/src/main/res/menu/key_view.xml
@@ -38,8 +38,14 @@
android:title="@string/menu_certify_fingerprint" />
<item
+ android:id="@+id/menu_key_view_certify_fingerprint_word"
+ app:showAsAction="never"
+ android:visible="false"
+ android:title="@string/menu_certify_fingerprint_word" />
+
+ <item
android:id="@+id/menu_key_view_add_linked_identity"
app:showAsAction="never"
android:title="@string/menu_linked_add_identity" />
-</menu> \ No newline at end of file
+</menu>
diff --git a/OpenKeychain/src/main/res/menu/keyserver_pref_menu.xml b/OpenKeychain/src/main/res/menu/keyserver_pref_menu.xml
new file mode 100644
index 000000000..c4002a682
--- /dev/null
+++ b/OpenKeychain/src/main/res/menu/keyserver_pref_menu.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/menu_add_keyserver"
+ android:title="@string/menu_search"
+ android:icon="@drawable/ic_add_white_24dp"
+ app:showAsAction="always" />
+</menu> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-bg/help_changelog.md b/OpenKeychain/src/main/res/raw-bg/help_changelog.md
deleted file mode 100644
index ab7fa8773..000000000
--- a/OpenKeychain/src/main/res/raw-bg/help_changelog.md
+++ /dev/null
@@ -1,269 +0,0 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-
-## 3.2
-
- * Material design
- * Integration of QR Scanner (New permissions required)
- * Improved key creation wizard
- * Fix missing contacts after sync
- * Requires Android 4
- * Redesigned key screen
- * Simplify crypto preferences, better selection of secure ciphers
- * API: Detached signatures, free selection of signing key,...
- * Fix: Some valid keys were shown revoked or expired
- * Don't accept signatures by expired or revoked subkeys
- * Keybase.io support in advanced view
-
-
-## 3.1.2
-
- * Fix key export to files (now for real)
-
-
-## 3.1.1
-
- * Fix key export to files (they were written partially)
- * Fix crash on Android 2.3
-
-
-## 3.1
-
- * Fix crash on Android 5
- * New certify screen
- * Secure Exchange directly from key list (SafeSlinger library)
- * New QR Code program flow
- * Redesigned decrypt screen
- * New icon usage and colors
- * Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
-
-
-## 3.0.1
-
- * Better handling of large key imports
- * Improved subkey selection
-
-
-## 3.0
-
- * Full support for Yubikey signature generation and decryption!
- * Propose installable compatible apps in apps list
- * New design for decryption screens
- * Many fixes for key import, also fixes stripped keys
- * Honor and display key authenticate flags
- * User interface to generate custom keys
- * Fixing user id revocation certificates
- * New cloud search (searches over traditional keyservers and keybase.io)
- * Support for stripping keys inside OpenKeychain
-
-
-## 2.9.2
-
- * Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
-
-
-## 2.9.1
-
- * Split encrypt screen into two
- * Fix key flags handling (now supporting Mailvelope 0.7 keys)
- * Improved passphrase handling
- * Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
- * Fix usage of stripped keys
- * SHA256 as default for compatibility
- * Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
- * OpenPGP API now handles revoked/expired keys and returns all user ids
-
-
-## 2.9
-
- * Fixing crashes introduced in v2.8
- * Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
-
-
-## 2.8
-
- * So many bugs have been fixed in this release that we focus on the main new features
- * Key edit: awesome new design, key revocation
- * Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
- * New first time screen
- * New key creation screen: autocompletion of name and email based on your personal Android accounts
- * File encryption: awesome new design, support for encrypting multiple files
- * New icons to show status of key (by Brennan Novak)
- * Important bug fix: Importing of large key collections from a file is now possible
- * Notification showing cached passphrases
- * Keys are connected to Android's contacts
-
-This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar
-
-## 2.7
-
- * Purple! (Dominik, Vincent)
- * New key view design (Dominik, Vincent)
- * New flat Android buttons (Dominik, Vincent)
- * API fixes (Dominik)
- * Keybase.io import (Tim Bray)
-
-
-## 2.6.1
-
- * Some fixes for regression bugs
-
-
-## 2.6
-
- * Key certifications (thanks to Vincent Breitmoser)
- * Support for GnuPG partial secret keys (thanks to Vincent Breitmoser)
- * New design for signature verification
- * Custom key length (thanks to Greg Witczak)
- * Fix share-functionality from other apps
-
-
-## 2.5
-
- * Fix decryption of symmetric OpenPGP messages/files
- * Refactored key edit screen (thanks to Ash Hughes)
- * New modern design for encrypt/decrypt screens
- * OpenPGP API version 3 (multiple api accounts, internal fixes, key lookup)
-
-
-## 2.4
-Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
-Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
-Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.
-
- * New unified key list
- * Colorized key fingerprint
- * Support for keyserver ports
- * Deactivate possibility to generate weak keys
- * Much more internal work on the API
- * Certify user ids
- * Keyserver query based on machine-readable output
- * Lock navigation drawer on tablets
- * Suggestions for emails on creation of keys
- * Search in public key lists
- * And much more improvements and fixes…
-
-
-## 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
- * New public key view
- * Bug fixes for importing of keys
- * Key cross-certification (thanks to Ash Hughes)
- * Handle UTF-8 passwords properly (thanks to Ash Hughes)
- * First version with new languages (thanks to the contributors on Transifex)
- * Sharing of keys via QR Codes fixed and improved
- * Package signature verification for API
-
-
-## 2.1.1
-
- * API Updates, preparation for K-9 Mail integration
-
-
-## 2.1
-
- * Lots of bug fixes
- * New API for developers
- * PRNG bug fix by Google
-
-
-## 2.0
-
- * Complete redesign
- * Share public keys via QR codes, NFC beam
- * Sign keys
- * Upload keys to server
- * Fixes import issues
- * New AIDL API
-
-
-## 1.0.8
-
- * Basic keyserver support
- * App2sd
- * More choices for passphrase cache: 1, 2, 4, 8, hours
- * Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
- * Bugfixes
- * Optimizations
-
-
-## 1.0.7
-
- * Fixed problem with signature verification of texts with trailing newline
- * More options for passphrase cache time to live (20, 40, 60 mins)
-
-
-## 1.0.6
-
- * Account adding crash on Froyo fixed
- * Secure file deletion
- * Option to delete key file after import
- * Stream encryption/decryption (gallery, etc.)
- * New options (language, force v3 signatures)
- * Interface changes
- * Bugfixes
-
-
-## 1.0.5
-
- * German and Italian translation
- * Much smaller package, due to reduced BC sources
- * New preferences GUI
- * Layout adjustment for localization
- * Signature bugfix
-
-
-## 1.0.4
-
- * Fixed another crash caused by some SDK bug with query builder
-
-
-## 1.0.3
-
- * Fixed crashes during encryption/signing and possibly key export
-
-
-## 1.0.2
-
- * Filterable key lists
- * Smarter pre-selection of encryption keys
- * New Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
- * Fixes and additional features (key preselection) for K-9 Mail, new beta build available
-
-
-## 1.0.1
-
- * GMail account listing was broken in 1.0.0, fixed again
-
-
-## 1.0.0
-
- * K-9 Mail integration, APG supporting beta build of K-9 Mail
- * Support of more file managers (including ASTRO)
- * Slovenian translation
- * New database, much faster, less memory usage
- * Defined Intents and content provider for other apps
- * Bugfixes \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-cs/help_about.md b/OpenKeychain/src/main/res/raw-cs/help_about.md
index 9d635d9df..06bed0021 100644
--- a/OpenKeychain/src/main/res/raw-cs/help_about.md
+++ b/OpenKeychain/src/main/res/raw-cs/help_about.md
@@ -6,41 +6,67 @@
License: GPLv3+
-## Developers
+[//]: # (NOTE: Alphabetic ordering)
+
+## Main Developers
* Dominik Schürmann (Maintainer)
- * Art O Cathain
+ * Vincent Breitmoser
+
+## Top Contributors
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG developer)
+ * Tim Bray
+
+## Occasional Contributors
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//]: # (NOTE: Alphabetic ordering)
## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
* [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-cs/help_changelog.md b/OpenKeychain/src/main/res/raw-cs/help_changelog.md
index ab7fa8773..11d7975b1 100644
--- a/OpenKeychain/src/main/res/raw-cs/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-cs/help_changelog.md
@@ -1,9 +1,29 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+## 3.4
+
+ * Anonymous key download over Tor
+ * Proxy support
+ * Better YubiKey error handling
+
+## 3.3
+
+ * New decryption screen
+ * Decryption of multiple files at once
+ * Better handling of YubiKey errors
+
## 3.2
+ * First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
* Material design
- * Integration of QR Scanner (New permissions required)
+ * Integration of QR Code Scanning (New permissions required)
* Improved key creation wizard
* Fix missing contacts after sync
* Requires Android 4
@@ -13,6 +33,7 @@
* Fix: Some valid keys were shown revoked or expired
* Don't accept signatures by expired or revoked subkeys
* Keybase.io support in advanced view
+ * Method to update all keys at once
## 3.1.2
@@ -35,7 +56,7 @@
* Redesigned decrypt screen
* New icon usage and colors
* Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * Experimental YubiKey support: Subkey IDs are now checked correctly
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Full support for Yubikey signature generation and decryption!
* Propose installable compatible apps in apps list
* New design for decryption screens
* Many fixes for key import, also fixes stripped keys
@@ -55,12 +75,13 @@
* Fixing user id revocation certificates
* New cloud search (searches over traditional keyservers and keybase.io)
* Support for stripping keys inside OpenKeychain
+ * Experimental YubiKey support: Support for signature generation and decryption
## 2.9.2
* Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ * Experimental YubiKey support: Decryption now working via API
## 2.9.1
@@ -69,7 +90,7 @@
* Fix key flags handling (now supporting Mailvelope 0.7 keys)
* Improved passphrase handling
* Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
* Fix usage of stripped keys
* SHA256 as default for compatibility
* Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Fixing crashes introduced in v2.8
* Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
+ * Experimental YubiKey support: Only signing with imported keys
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-de/help_about.md b/OpenKeychain/src/main/res/raw-de/help_about.md
index ec2501ee6..c19b9d8a6 100644
--- a/OpenKeychain/src/main/res/raw-de/help_about.md
+++ b/OpenKeychain/src/main/res/raw-de/help_about.md
@@ -1,4 +1,4 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (Beachte: Bitte schreibe jeden Satz in eine eigene Zeile, Transifex wird jede Zeile in ein eigenes Übesetzungsfeld setzen!)
[http://www.openkeychain.org](http://www.openkeychain.org)
@@ -6,41 +6,67 @@
Lizenz: GPLv3+
-## Entwickler
+[//]: # (Beachte: alphabethisch Sortiert)
+
+## Hauptentwickler
* Dominik Schürmann (Leitender Entwickler)
- * Art O Cathain
+ * Vincent Breitmoser
+
+## Haupt-Mitwirkende
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG-Entwickler)
+ * Tim Bray
+
+## gelegentlich Mitwirkende
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//]: # (Beachte: alphabethisch Sortiert)
## Bibliotheken
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11-Lizenz)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT-Lizenz)
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache-Lizenz v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache-Lizenz v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache-Lizenz v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache-Lizenz v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache-Lizenz v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache-Lizenz v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache-Lizenz v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache-Lizenz v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache-Lizenz v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache-Lizenz v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT-Lizenz)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT-Lizenz)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11-Lizenz)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache-Lizenz v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache-Lizenz v2)
* [ZXing](https://github.com/zxing/zxing) (Apache-Lizenz v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache-Lizenz v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material-Design)</a> (Apache-Lizenz v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache-Lizenz v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT-Lizenz)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache-Lizenz v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache-Lizenz v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache-Lizenz v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache-Lizenz v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-de/help_certification.md b/OpenKeychain/src/main/res/raw-de/help_certification.md
index 5089301f3..58cf9f6b3 100644
--- a/OpenKeychain/src/main/res/raw-de/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-de/help_certification.md
@@ -1,28 +1,28 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (Beachte: Bitte schreibe jeden Satz in eine eigene Zeile, Transifex wird jede Zeile in ein eigenes Übesetzungsfeld setzen!)
## Schlüsselbestätigung
-Without confirmation, you cannot be sure if a key really corresponds to a specific person.
-The simplest way to confirm a key is by scanning the QR Code or exchanging it via NFC.
-To confirm keys between more than two persons, we suggest to use the key exchange method available for your keys.
+Ohne Bestätigung können sie nicht wissen, ob ein Schlüssel wirklich zu einer bestimmten Person gehört.
+Der einfachste Weg einen Schlüssel zu bestätigen ist den QR Code zu scannen oder ihn mit NFC auszutauschen.
+Um Schlüssel zwischen mehr als zwei Personen zu bestätigen empfehlen wir die Schlüsseltausch Methode zu benutzen, welche für ihren Schlüssel verfügbar ist.
## Schlüsselstatus
<img src="status_signature_verified_cutout_24dp"/>
-Confirmed: You have already confirmed this key, e.g., by scanning the QR Code.
+Bestätigt: Sie haben bereits den Schlüssel bestätigt, zum Beispiel durch das Scannen eines QR Codes.
<img src="status_signature_unverified_cutout_24dp"/>
-Unconfirmed: This key has not been confirmed yet. You cannot be sure if the key really corresponds to a specific person.
+Unbestätigt: Diese Key wurde noch nicht beglaubigt. Sie können nicht sicher sein, ob dieser Key wirklich zu einer bestimmten Person gehört.
<img src="status_signature_expired_cutout_24dp"/>
-Expired: This key is no longer valid. Only the owner can extend its validity.
+Abgelaufen: Der Key ist nicht mehr gültig. Nur der Besitzer kann die Gültigkeit des Schlüssels verlängern.
<img src="status_signature_revoked_cutout_24dp"/>
-Revoked: This key is no longer valid. It has been revoked by its owner.
+Widerrufen: Dieser Schlüssel ist nicht mehr gültig. Er wurde von dem Besitzer widerrufen.
## Erweiterte Informationen
-A "key confirmation" in OpenKeychain is implemented by creating a certification according to the OpenPGP standard.
-This certification is a ["generic certification (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) described in the standard by:
-"The issuer of this certification does not make any particular assertion as to how well the certifier has checked that the owner of the key is in fact the person described by the User ID."
+Eine "Schlüsselbestätigung" in OpenKeychain ist implementiert mit dem Erstellen eines Zertifikats bei Beachtung von dem OpenPGP Standard.
+Diese Zertifizierung ist eine ["generische Zertifizierung (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1), im Standard beschrieben durch:
+"Der Ersteller dieser Zertifizierung macht keine Behauptungen darüber, wie gut der Zertifizierer geprüft hat, dass der Schlüsselinhaber tatsächlich die Person ist, die in der Benutzer-ID beschrieben wird."
-Traditionally, certifications (also with higher certification levels, such as "positive certifications" (0x13)) are organized in OpenPGP's Web of Trust.
-Our model of key confirmation is a much simpler concept to avoid common usability problems related to this Web of Trust.
-We assume that keys are verified only to a certain degree that is still usable enough to be executed "on the go".
-We also do not implement (potentially transitive) trust signatures or an ownertrust database like in GnuPG.
-Furthermore, keys which contain at least one user ID certified by a trusted key will be marked as "confirmed" in the key listings. \ No newline at end of file
+Normalerweise sind Zertifizierungen (auch höherer Zertifizierungsniveaus, wie z.B. "positive Zertifizierungen" (0x13)) im OpenPGP-Web of Trust organisiert.
+Unser Modell der Schlüsselbestätigung ist ein viel einfacheres Konzept, um allgemeine Nutzbarkeitsprobleme zu vermeiden, die mit diesem Web of Trust verknüpft sind.
+Wir nehmen an, dass Schlüssel nur bis zu einem gewissen Maß bestätigt werden, dass immer noch nutzbar genug ist, um es unterwegs benutzen zu können.
+Wir implementieren auch nicht (möglicherweise transitive) Vertrauenssignaturen oder eine Besitzervertrauensdatenbank wie in GnuPG.
+Weiter werden Schlüssel, die mindestens eine Benutzer-ID enthalten, die von einem vertrauten Schlüssel unterschrieben ist, in der Schlüsselliste als "bestätigt" markiert. \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-de/help_changelog.md b/OpenKeychain/src/main/res/raw-de/help_changelog.md
index 171c92403..91f2ffedc 100644
--- a/OpenKeychain/src/main/res/raw-de/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-de/help_changelog.md
@@ -1,28 +1,49 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTITZ: Bitte setze jeden Satz in eine eigene Zeile, Transifex setzt jede Zeile in ein eigenes Übersetzungsfeld!)
+
+## 3.5
+
+ * Schlüsselwiderruf bei Schlüssellöschung
+ * Verbesserte Kontrollen für unsichere Verschlüsselung
+ * Behoben: OpenKeychain nach Abschluss des Anfängerassistenten nicht schließen
+ * API: Version 8
+
+## 3.4
+
+ * Anonymer Schlüsseldownload über Tor
+ * Proxyunterstützung
+ * Bessere YubiKey Fehlerbehandlung
+
+## 3.3
+
+ * Neuer Entschlüsselungsbildschirm
+ * Entschlüsselung mehrerer Dateien gleichzeitig
+ * Bessere Behandlung von YubiKey-Fehlern
## 3.2
+ * Erste Version mit kompletter YubiKey-Unterstützung in der Benutzeroberfläche: Schlüssel bearbeiten, YubiKey mit Schlüsseln verbinden,…
* Material-Design
* QR-Scanner-Integration (benötigt neue Berechtigungen)
* Schlüsselerzeugungsassistent verbessert
* Fehlende Kontakte nach Synchronisierung behoben
* Benötigt Android 4
- * Redesigned key screen
- * Simplify crypto preferences, better selection of secure ciphers
- * API: Detached signatures, free selection of signing key,...
- * Fix: Some valid keys were shown revoked or expired
- * Don't accept signatures by expired or revoked subkeys
- * Keybase.io support in advanced view
+ * Neuer Schlüsselbildschirm
+ * Krypto-Einstellungen vereinfacht, bessere Auswahl sicherer Verschlüsselungsverfahren
+ * API: abgetrennte Signaturen, freie Wahl des Signaturschlüssels,...
+ * Behoben: Einige gültige Schlüssel wurden als widerrufen oder abgelaufen angezeigt
+ * Akzeptiert keine Signaturen abgelaufener oder widerrufener Unterschlüssel
+ * Keybase.io-Unterstützung in der erweiterten Ansicht
+ * Möglichkeit, alle Schlüssel auf einmal zu aktualisieren
## 3.1.2
- * Fix key export to files (now for real)
+ * Behoben: Schlüsselexport zu Datei (jetzt wirklich)
## 3.1.1
- * Fix key export to files (they were written partially)
+ * Schlüsselexport zu Datei repariert (sie wurden nur teilweise geschrieben)
* Absturz unter Android 2.3 behoben
@@ -30,199 +51,199 @@
* Absturz unter Android 5 behoben
* Neuer Beglaubigungsbildschirm
- * Secure Exchange directly from key list (SafeSlinger library)
+ * Sicherer Austausch direkt aus der Schlüsselliste (SafeSlinger-Bibliothek)
* Neuer Programmablauf für QR-Codes
- * Redesigned decrypt screen
+ * Neugestalteter Entschlüsselungsbildschirm
* Verwendung neuer Symbole und Farben
- * Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * Behoben: Import geheimer Schlüssel aus Symantec Encryption Desktop
+ * Experimentelle YubiKey-Unterstützung: Unterschlüssel-IDs werden jetzt richtig geprüft
## 3.0.1
- * Better handling of large key imports
- * Improved subkey selection
+ * Bessere Verarbeitung von großen Schlüsselimporten
+ * Verbesserte Unterschlüsselauswahl
## 3.0
- * Full support for Yubikey signature generation and decryption!
- * Propose installable compatible apps in apps list
- * New design for decryption screens
- * Many fixes for key import, also fixes stripped keys
- * Honor and display key authenticate flags
- * User interface to generate custom keys
- * Fixing user id revocation certificates
- * New cloud search (searches over traditional keyservers and keybase.io)
- * Support for stripping keys inside OpenKeychain
+ * Kompatible, installierbare Apps in der App-Liste vorschlagen
+ * Neues Design für Entschlüsselungsbildschirme
+ * Viele Fehler beim Schlüsselimport behoben, auch bei gekürzten Schlüsseln
+ * Schlüsselauthentifikations-Attribute berücksichtigen und anzeigen
+ * Benutzeroberfläche zum Erzeugen benutzerdefinierter Schlüssel
+ * Benutzer-ID-Widerrufszertifikate repariert
+ * Neue Cloud-Suche (sucht über traditionelle Schlüsselserver und über keybase.io)
+ * Unterstützung für das Kürzen von Schlüsseln innerhalb von OpenKeychain
+ * Experimentelle YubiKey-Unterstützung: Unterstützung für Signaturerzeugung und Entschlüsselung
## 2.9.2
- * Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ * Repariere Schlüssel, die in 2.9.1 beschädigt wurden
+ * Experimentelle YubiKey-Unterstützung: Entschlüsselung funktioniert nun via API
## 2.9.1
- * Split encrypt screen into two
- * Fix key flags handling (now supporting Mailvelope 0.7 keys)
- * Improved passphrase handling
- * Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
- * Fix usage of stripped keys
+ * Verschlüsselungsbildschirm in zwei Bildschirme aufgeteilt
+ * Behoben: Fehler bei Handhabung von Schlüsselattributen (unterstützt nun Schlüssel aus Mailvelope 0.7)
+ * Handhabung von Passwörtern verbessert
+ * Schlüsselaustausch mit SafeSlinger
+ * Experimentelle YubiKey-Unterstützung: Einstellung zum Erlauben anderer PINs, derzeit funktioniert nur die Beglaubigung über die OpenPGP API, nicht innerhalb von OpenKeychain
+ * Nutzung gekürzter Schlüssel repariert
* Standardmäßig SHA256 aufgrund von Kompatibilität
- * Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
- * OpenPGP API now handles revoked/expired keys and returns all user ids
+ * Intent-API hat sich geändert, siehe https://github.com/open-keychain/open-keychain/wiki/Intent-API
+ * OpenPGP-API bearbeitet ab jetzt widerrufene/abgelaufene Schlüssel und gibt alle Benutzer-IDs zurück
## 2.9
- * Fixing crashes introduced in v2.8
+ * Behebt Abstürze aus v2.8
* Experimentelle ECC-Unterstützung
- * Experimental Yubikey support (signing-only with imported keys)
+ * Experimentelle YubiKey-Unterstützung (nur Unterschreiben mit importierten Schlüsseln)
## 2.8
- * So many bugs have been fixed in this release that we focus on the main new features
- * Key edit: awesome new design, key revocation
- * Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
+ * In diesem Release wurden so viele Fehler behoben, dass wir uns lieber auf die neuen Funktionen konzentrieren
+ * Schlüsselbearbeitung: Fantastisches neues Design, Schlüsselwiderruf
+ * Schlüsselimport: Fantastisches neues Design, sichere Verbindungen zu Keyservern über hkps, Namensauflösung der Schlüsselserver über DNS SRV-Einträge
* Neuer Bildschirm bei der ersten Öffnung
- * New key creation screen: autocompletion of name and email based on your personal Android accounts
- * File encryption: awesome new design, support for encrypting multiple files
- * New icons to show status of key (by Brennan Novak)
- * Important bug fix: Importing of large key collections from a file is now possible
- * Notification showing cached passphrases
- * Keys are connected to Android's contacts
+ * Neuer Schlüsselerstellungsbildschirm: Autovervollständigung von Name und E-Mail basierend auf Deinen persönlichen Android-Konten
+ * Dateiverschlüsselung: fantastisches neues Design, Unterstützung für die Verschlüsselung mehrerer Dateien
+ * Neue Symbile zum Anzeigen des Schlüsselstatus (von Brennan Novak)
+ * Wichtiger Fehler behoben: Import großer Schlüsselsammlungen aus einer Datei ist nun möglich
+ * Benachrichtigung, die zwischengespeicherte Passwörter anzeigt
+ * Schlüssel sind mit den Kontakten verbunden
-This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar
+Dieser Release wäre ohne die Arbeit von Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray und Thialfihar nicht möglich gewesen
## 2.7
* Lila! (Dominik, Vincent)
- * New key view design (Dominik, Vincent)
- * New flat Android buttons (Dominik, Vincent)
+ * Neues Schlüsselansichtsdesign (Dominik, Vincent)
+ * Neue flache Android-Knöpfe (Dominik, Vincent)
* API-Korrekturen (Dominik)
* Import aus keybase.io (Tim Bray)
## 2.6.1
- * Some fixes for regression bugs
+ * Einige Korrekturen für Regressionsfehler
## 2.6
- * Key certifications (thanks to Vincent Breitmoser)
- * Support for GnuPG partial secret keys (thanks to Vincent Breitmoser)
- * New design for signature verification
+ * Schlüsselbeglaubigungen (dank Vincent Breitmoser)
+ * Unterstützung für GnuPG-Teilschlüssel (danke an Vincent Breitmoser)
+ * Neues Design für Signaturprüfung
* Benutzerdefinierte Schlüssellänge (Dank an Greg Witczak)
- * Fix share-functionality from other apps
+ * Fehler bei der Teilen-Funktionalität aus anderen Apps behoben
## 2.5
- * Fix decryption of symmetric OpenPGP messages/files
- * Refactored key edit screen (thanks to Ash Hughes)
- * New modern design for encrypt/decrypt screens
- * OpenPGP API version 3 (multiple api accounts, internal fixes, key lookup)
+ * Fehler bei der Entschlüsselung symmetrischer OpenPGP-Nachrichten/Dateien behoben
+ * Umgestaltung des Schlüsselbearbeitungsbildschirms (Dank an Ash Hughes)
+ * Neues modernes Design für Verschlüsselungs-/Entschlüsselungsbildschirme
+ * OpenPGP-API Version 3 (mehrere API-Konten, interne Fehlerbehebungen, Schlüsselsuche)
## 2.4
-Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
-Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+Dank an alle Bewerber bei Google Summer of Code 2014, welche diesen Release funktionsreich und fehlerfrei gemacht haben!
+Neben einigen kleinen Fehlerbehebungen wurden bemerkenswert viele Patches durch die folgenden Personen beigesteuert (alphabetisch sortiert):
Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.
- * New unified key list
- * Colorized key fingerprint
- * Support for keyserver ports
- * Deactivate possibility to generate weak keys
- * Much more internal work on the API
- * Certify user ids
- * Keyserver query based on machine-readable output
- * Lock navigation drawer on tablets
- * Suggestions for emails on creation of keys
+ * Neue einheitliche Schlüsselliste
+ * Eingefärbter Schlüsselfingerabdruck
+ * Unterstützt Schlüsselserver ports
+ * Deaktiviere Möglichkeit unsichere Schlüssel zu erstellen
+ * Viel mehr interne Arbeit an der API
+ * Benutzerkennungen beglaubigen
+ * Schlüsselserver-Suchanfrage basierend auf maschinenlesbarer Ausgabe
+ * "Navigation Drawer" auf Tablets sperren
+ * Vorschläge für E-Mails bei Schlüsselerzeugung
* Suchen in öffentlichen Schlüssellisten
- * And much more improvements and fixes…
+ * Und viele weitere Verbesserungen und Fehlerbehebungen...
## 2.3.1
- * Hotfix for crash when upgrading from old versions
+ * Hotfix für Absturz beim Aktualisieren von alten Versionen
## 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
+ * Kein unnötiger Export öffentlicher Schlüssel beim Export der geheimen Schlüssel (Dank an Ash Hughes)
+ * Behoben: Setzen des Schlüsselablaufdatums (Dank an Ash Hughes)
+ * Weitere interne Fehlerbehebungen für das Editieren von Schlüsseln (Dank an Ash Hughes)
+ * Schlüsselserverabfrage direkt aus dem Importierungsbildschirm
+ * Behoben: Layout und Dialogstil auf Android 2.2-3.0
+ * Behoben: Absturz bei leeren Benutzer-IDs
+ * Absturz und leere Listen nach der Rückkehr vom Signierbildschirm behoben
+ * Bouncy Castle (Kryptographie-Bibliothek) von 1.47 auf 1.50 aktualisiert und aus Quellcode kompiliert
+ * Hochladen des Schlüssels aus dem Signierbildschirm behoben
## 2.2
- * New design with navigation drawer
- * New public key list design
+ * Neues Design mit "Navigation Drawer"
+ * Neues Design der Liste öffentlicher Schlüssel
* Neue Ansicht für öffentliche Schlüssel
- * Bug fixes for importing of keys
- * Key cross-certification (thanks to Ash Hughes)
- * Handle UTF-8 passwords properly (thanks to Ash Hughes)
- * First version with new languages (thanks to the contributors on Transifex)
- * Sharing of keys via QR Codes fixed and improved
- * Package signature verification for API
+ * Fehler beim Importieren von Schlüsseln behoben
+ * Schlüsselbeglaubigung über Kreuz (Dank an Ash Hughes)
+ * Korrekte Verarbeitung von UTF-8-Passwörtern (Dank an Ash Hughes)
+ * Erste Version mit neuen Sprachen (Dank an die Unterstützer auf Transifex)
+ * Teilen von Schlüsseln über QR-Codes behoben und verbessert
+ * Paket-Signaturprüfung für API
## 2.1.1
- * API Updates, preparation for K-9 Mail integration
+ * API-Aktualisierungen, Vorbereitung für die K-9-Mail-Integration
## 2.1
- * Lots of bug fixes
+ * Viele Fehlerbehebungen
* Neue API für Entwickler
- * PRNG bug fix by Google
+ * PRNG-Fehlerbehebung von Google
## 2.0
- * Complete redesign
- * Share public keys via QR codes, NFC beam
+ * Komplette Neugestaltung
+ * Öffentliche Schlüssel über QR-Codes oder NFC-Beam teilen
* Schlüssel unterschreiben
* Schlüssel auf den Server hochladen
- * Fixes import issues
+ * Behebt Importprobleme
* Neue AIDL-API
## 1.0.8
* Grundlegende Schlüsselserverunterstützung
- * App2sd
- * More choices for passphrase cache: 1, 2, 4, 8, hours
- * Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
+ * App2SD
+ * Mehr Auswahlmöglichkeiten für den Passwortzwischenspeicher: 1, 2, 4, 8, Stunden
+ * Übersetzungen: Norwegisch (Dank an Sander Danielsen), Chinesisch (Dank an Zhang Fredrick)
* Fehlerbehebungen
* Optimierungen
## 1.0.7
- * Fixed problem with signature verification of texts with trailing newline
- * More options for passphrase cache time to live (20, 40, 60 mins)
+ * Problem mit Signaturprüfung von Texten mit angehängtem Zeilenvorschub behoben
+ * Mehr Optionen für die Länge der Passwortzwischenspeicherung (20, 40, 60 Minuten)
## 1.0.6
- * Account adding crash on Froyo fixed
+ * Absturz bei Kontoerstellung unter Froyo behoben
* Sichere Dateilöschung
- * Option to delete key file after import
+ * Option zum Löschen der Schlüsseldatei nach dem Import
* Streamverschlüsselung/-entschlüsselung (Galerie, usw.)
- * New options (language, force v3 signatures)
+ * Neue Optionen (Sprache, v3-Signaturen erzwingen)
* Oberflächenänderungen
* Fehlerbehebungen
@@ -230,40 +251,40 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
## 1.0.5
* Deutsche und Italienische Übersetzung
- * Much smaller package, due to reduced BC sources
+ * Viel kleineres Paket durch reduzierte BC-Quellen
* Neues Einstellungen-Benutzeroberfläche
- * Layout adjustment for localization
- * Signature bugfix
+ * Anpassung der Anordnung für Übersetzungen
+ Fehler in der Signatur behoben
## 1.0.4
- * Fixed another crash caused by some SDK bug with query builder
+ * Weiteren Absturz durch einen SDK-Fehler mit "query builder" behoben
## 1.0.3
- * Fixed crashes during encryption/signing and possibly key export
+ * Abstürze während Verschlüsselung/Beglaubigung und möglicherweise Schlüsselexport behoben
## 1.0.2
* Filterbare Schlüsselliste
- * Smarter pre-selection of encryption keys
- * New Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
- * Fixes and additional features (key preselection) for K-9 Mail, new beta build available
+ * Intelligentere Vorauswahl von Verschlüsselungsschlüsseln
+ * Neue Absichtsbehandlung für VIEW und SEND, ermöglicht das Ver-/Entschlüsseln von Dateien aus Dateimanagern
+ * Fehlerbehebungen und zusätzliche Funktionen (Schlüsselvorauswahl) für K-9 Mail, neuer Beta-Build verfügbar
## 1.0.1
- * GMail account listing was broken in 1.0.0, fixed again
+ * GMail-Konto-Auflistung war fehlerhaft in 1.0.0, erneut behoben
## 1.0.0
- * K-9 Mail integration, APG supporting beta build of K-9 Mail
+ * K-9Mail-Integration, APG-unterstützendes Beta-Build von K-9 Mail
* Unterstützung von mehr Dateimanagern (einschließlich ASTRO)
* Slowenische Übersetzung
* Neue Datenbank, viel schneller, weniger Speicherbelegung
- * Defined Intents and content provider for other apps
+ * Definierte Absichten und Inhaltsanbieter für andere Apps
* Fehlerbehebungen \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-de/help_start.md b/OpenKeychain/src/main/res/raw-de/help_start.md
index b51aebf62..81adbf136 100644
--- a/OpenKeychain/src/main/res/raw-de/help_start.md
+++ b/OpenKeychain/src/main/res/raw-de/help_start.md
@@ -2,15 +2,15 @@
## Wie kann ich OpenKeychain in K-9 Mail nutzen?
Um OpenKeychain mit K-9 Mail zu nutzen, bitte folgenden Schritten folgen:
- 1. Open K-9 Mail and long-tap on the account you want to use OpenKeychain with.
- 2. Select "Account settings", scroll to the very bottom and click "Cryptography".
- 3. Klick auf "OpenPGP Provider" und OpenKeychain in der Liste auswählen.
+ 1. Öffne K-9 Mail und drücke lange auf den Account, mit dem du OpenKeychain nutzen willst.
+ 2. Wähle "Kontoeinstellungen", blättere ganz nach unten und klicke auf "Kryptographie".
+ 3. Drücke auf "OpenPGP-Provider" und wähle OpenKeychain aus der Liste aus.
-## Ich habe einen Fehler in OpenKeychail gefunden!
-Please report the bug using the [issue tracker of OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
+## Ich habe einen Fehler in OpenKeychain gefunden!
+Bitte melde den Fehler mithilfe des [Fehlertrackers von OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
-## Unterstützen
-If you want to help us developing OpenKeychain by contributing code [follow our small guide on Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
+## Mitwirken
+Wenn du uns bei der Entwicklung von OpenKeychain, z.B. durch das Beisteuern von Code, helfen willst, [schaue dir unsere Kurzanleitung auf GitHub an](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
## Übersetzungen
-Help translating OpenKeychain! Everybody can participate at [OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
+Hilf mit, OpenKeychain zu übersetzen! Jeder kann mitmachen, [besuche OpenKeychain auf Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-es/help_about.md b/OpenKeychain/src/main/res/raw-es/help_about.md
index bb925e6bd..194adfd3a 100644
--- a/OpenKeychain/src/main/res/raw-es/help_about.md
+++ b/OpenKeychain/src/main/res/raw-es/help_about.md
@@ -1,4 +1,4 @@
-[//]: # (NOTA: ¡Por favor ponga cada frase en su propia línea, Transifex pone cada línea en su propio campo de traducción!)
+[//]: # (NOTA: ¡Ponga cada frase en su propia línea, Transifex pone cada línea en su propio campo de traducción!)
[http://www.openkeychain.org](http://www.openkeychain.org)
@@ -6,41 +6,67 @@
Licencia: GPLv3+
-## Desarrolladores
+[//]: # (NOTA: Orden alfabético)
+
+## Desarrolladores principales
* Dominik Schürmann (Mantenedor)
- * Art O Cathain
+ * Vincent Breitmoser
+
+## Máximos contribuidores
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (desarrollador APG)
+ * Tim Bray
+
+## Contribuidores ocasionales
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//]: # (NOTA: Orden alfabético)
## Librerías
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (Licencia MIT X11)
- * [Librería SafeSlinger Exchange](https://github.com/SafeSlingerProject/exchange-android) (Licencia MIT)
* [Librerías de Soporte Android](http://developer.android.com/tools/support-library/index.html) (Licencia Apache v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Licencia Apache v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Licencia Apache v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Licencia Apache v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Licencia Apache v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Licencia Apache v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Licencia Apache v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Licencia Apache v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Licencia Apache v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Licencia Apache v2)
+ * [Librería SafeSlinger Exchange](https://github.com/SafeSlingerProject/exchange-android) (Licencia MIT)
+ * [Snackbar](https://github.com/nispok/snackbar) (Licencia MIT)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (Licencia MIT X11)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Licencia Apache v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Licencia Apache v2)
* [ZXing](https://github.com/zxing/zxing) (Licencia Apache v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Licencia Apache v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Diseño estilo Material)</a> (Licencia Apache v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Licencia Apache v2)
- * [Snackbar](https://github.com/nispok/snackbar) (Licencia MIT)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Licencia Apache v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Licencia Apache v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Licencia Apache v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Licencia Apache v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-es/help_changelog.md b/OpenKeychain/src/main/res/raw-es/help_changelog.md
index 4306f3f08..49855890d 100644
--- a/OpenKeychain/src/main/res/raw-es/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-es/help_changelog.md
@@ -1,9 +1,29 @@
[//]: # (NOTA: ¡Por favor ponga cada frase en su propia línea, Transifex pone cada línea en su propio campo de traducción!)
+## 3.5
+
+ * Revocación de clave al borrar clave
+ * Comprobaciones mejoradas de criptografía no segura
+ * Reparacion: No cierra OpenKeychain tras completar el asistente de primera ejecución
+ * API: Versión 8
+
+## 3.4
+
+ * Descarga de clave anónima sobre Tor
+ * Soporte para proxy
+ * Manejo de errores de YubiKey mejorado
+
+## 3.3
+
+ * Nueva pantalla de descifrado
+ * Descifrado de múltiples ficheros a la vez
+ * Mejor manejo de errores de YubiKey
+
## 3.2
+ * Primera versión con soporte para YubiKey completo disponible desde la interfaz de usuario: Editar claves, ligar YubiKey a claves...
* Material design (estilo)
- * Integración de QR Scanner (se requieren nuevos permisos)
+ * Integración de Escaneado de Código QR (se requieren nuevos permisos)
* Asistente de creación de clave mejorado
* Repara contactos perdidos después de la sincronización
* Requiere Android 4
@@ -13,6 +33,7 @@
* Reparación: Algunas claves válidas se mostraron revocadas o caducadas
* No acepte algo firmado por subclaves caducadas o revocadas
* Soporte para Keybase.io en la vista avanzada
+ * Método para actualizar todas las claves a la vez
## 3.1.2
@@ -35,7 +56,7 @@
* Pantalla de descifrado rediseñada
* Nuevo uso y colores del icono
* Repara la importación de claves secretas (privadas) desde Symantec Encryption Desktop
- * Las identificaciones de subclaves en Yubikeys ahora se comprueban correctamente
+ * Soporte experimental para YubiKey: Las identificaciones (IDs) de subclaves ahora se comprueban correctamente.
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * ¡Soporte completo para generación de firma y descifrado de Yubikey!
* Propone aplicaciones instalables compatibles en la lista de aplicaciones
* Nuevo diseño para pantallas de descifrado
* Muchas reparaciones para la importación de claves, también repara claves desnudas
@@ -55,12 +75,13 @@
* Repara certificados de revocación de identificación de usuario
* Nueva búsqueda en la nube (busca sobre servidores de claves tradicionales y keybase.io)
* Soporte para desvestir claves dentro de OpenKeychain
+ * Soporte experimental para YubiKey: Soporte para generación de firma y descifrado
## 2.9.2
* Repara claves rotas en la versión 2.9.1
- * El descifrado de Yubikey ahora funciona vía API
+ * Soporte experimental para YubiKey: El descifrado ahora funciona vía API
## 2.9.1
@@ -69,7 +90,7 @@
* Repara el manejo de los indicativos de claves (ahora soporta claves de Mailvelope 0.7)
* Manejo de frase-contraseña mejorado
* Compartición de claves vía SafeSlinger
- * Yubikey: Preferencia para permitir otros PINs, actualmente sólo funciona firmando mediante la API de OpenPGP, no dentro de OpenKeychain
+ * Soporte experimental para YubiKey: Preferencia para permitir otros PINs, actualmente sólo funciona firmando vía API OpenPGP, no desde dentro de OpenKeychain
* Repara el uso de claves desnudas
* SHA256 por defecto para compatibilidad
* La API de Intent ha cambiado, vea https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Repara caídas introducidas en la versión 2.8
* Soporte para ECC (criptografía de curva elíptica) experimental
- * Soporte experimental para Yubikey (sólo-firmante con claves importadas)
+ * Soporte experimental para YubiKey: Firmando sólo con claves importadas
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-et/help_about.md b/OpenKeychain/src/main/res/raw-et/help_about.md
index 9d635d9df..06bed0021 100644
--- a/OpenKeychain/src/main/res/raw-et/help_about.md
+++ b/OpenKeychain/src/main/res/raw-et/help_about.md
@@ -6,41 +6,67 @@
License: GPLv3+
-## Developers
+[//]: # (NOTE: Alphabetic ordering)
+
+## Main Developers
* Dominik Schürmann (Maintainer)
- * Art O Cathain
+ * Vincent Breitmoser
+
+## Top Contributors
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG developer)
+ * Tim Bray
+
+## Occasional Contributors
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//]: # (NOTE: Alphabetic ordering)
## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
* [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-et/help_changelog.md b/OpenKeychain/src/main/res/raw-et/help_changelog.md
index ab7fa8773..11d7975b1 100644
--- a/OpenKeychain/src/main/res/raw-et/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-et/help_changelog.md
@@ -1,9 +1,29 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+## 3.4
+
+ * Anonymous key download over Tor
+ * Proxy support
+ * Better YubiKey error handling
+
+## 3.3
+
+ * New decryption screen
+ * Decryption of multiple files at once
+ * Better handling of YubiKey errors
+
## 3.2
+ * First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
* Material design
- * Integration of QR Scanner (New permissions required)
+ * Integration of QR Code Scanning (New permissions required)
* Improved key creation wizard
* Fix missing contacts after sync
* Requires Android 4
@@ -13,6 +33,7 @@
* Fix: Some valid keys were shown revoked or expired
* Don't accept signatures by expired or revoked subkeys
* Keybase.io support in advanced view
+ * Method to update all keys at once
## 3.1.2
@@ -35,7 +56,7 @@
* Redesigned decrypt screen
* New icon usage and colors
* Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * Experimental YubiKey support: Subkey IDs are now checked correctly
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Full support for Yubikey signature generation and decryption!
* Propose installable compatible apps in apps list
* New design for decryption screens
* Many fixes for key import, also fixes stripped keys
@@ -55,12 +75,13 @@
* Fixing user id revocation certificates
* New cloud search (searches over traditional keyservers and keybase.io)
* Support for stripping keys inside OpenKeychain
+ * Experimental YubiKey support: Support for signature generation and decryption
## 2.9.2
* Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ * Experimental YubiKey support: Decryption now working via API
## 2.9.1
@@ -69,7 +90,7 @@
* Fix key flags handling (now supporting Mailvelope 0.7 keys)
* Improved passphrase handling
* Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
* Fix usage of stripped keys
* SHA256 as default for compatibility
* Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Fixing crashes introduced in v2.8
* Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
+ * Experimental YubiKey support: Only signing with imported keys
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-eu/help_about.md b/OpenKeychain/src/main/res/raw-eu/help_about.md
index 9d635d9df..09a7737d4 100644
--- a/OpenKeychain/src/main/res/raw-eu/help_about.md
+++ b/OpenKeychain/src/main/res/raw-eu/help_about.md
@@ -1,46 +1,72 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (OHARRA: Meseez jarri esaldi bakoitza bere lerroan, Transifex-ek lerroak bere itzulpen eremuan jartzen ditu!)
[http://www.openkeychain.org](http://www.openkeychain.org)
-[OpenKeychain](http://www.openkeychain.org) is an OpenPGP implementation for Android.
+[OpenKeychain](http://www.openkeychain.org) Android-rako OpenPGP egokitzapen bat da.
-License: GPLv3+
+Baimena: GPLv3+
-## Developers
- * Dominik Schürmann (Maintainer)
- * Art O Cathain
+[//]: # (NOTE: Alphabetic ordering)
+
+## Garatzaile Nagusiak
+ * Dominik Schürmann (Mantentzaileak)
+ * Vincent Breitmoser
+
+## Top Contributors
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG garatzailea)
+ * Tim Bray
+
+## Occasional Contributors
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//]: # (NOTE: Alphabetic ordering)
-## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
- * [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
- * [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
- * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
- * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
- * [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
+## Liburutegiak
+ * [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache Baimena v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache Baimena v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache Baimena v2)
+ * [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache Baimena v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache Baimena v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache Baimena v2)
+ * [MiniDNS](https://github.com/rtreffer/minidns) (Apache Baimena v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Diseinua) (Apache Baimena 2 bertsioa)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT Baimena)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT Baimena)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 Baimena)
+ * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache Baimena v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache Baimena v2)
+ * [ZXing](https://github.com/zxing/zxing) (Apache Baimena v2)
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache Baimena v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-eu/help_certification.md b/OpenKeychain/src/main/res/raw-eu/help_certification.md
index 3518adf73..8e72cc063 100644
--- a/OpenKeychain/src/main/res/raw-eu/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-eu/help_certification.md
@@ -1,28 +1,28 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (OHARRA: Meseez jarri esaldi bakoitza bere lerroan, Transifex-ek lerroak bere itzulpen eremuan jartzen ditu!)
-## Key Confirmation
-Without confirmation, you cannot be sure if a key really corresponds to a specific person.
-The simplest way to confirm a key is by scanning the QR Code or exchanging it via NFC.
-To confirm keys between more than two persons, we suggest to use the key exchange method available for your keys.
+## Giltza Baieztapena
+Baieztapenik gabe, ezin zara zihur egon giltza bat egitan norbanako zehatz batena den.
+Giltza bat baieztatzeko bide arruntena QR Kodea eskaneatzea edo hura NFC bidez trukatzea da.
+Giltzak bi norbanako baino gehiagoren artean baieztatzeko, zure giltzentzat eskuragarrai dagoen giltza trukea metodoa erabiltzea gomendatzen dugu.
-## Key Status
+## Giltza Egoera
<img src="status_signature_verified_cutout_24dp"/>
-Confirmed: You have already confirmed this key, e.g., by scanning the QR Code.
+Baieztatuta: Giltza hau jadanik baieztatuta duzu, adib. QR Kodea eskaneatuz.
<img src="status_signature_unverified_cutout_24dp"/>
-Unconfirmed: This key has not been confirmed yet. You cannot be sure if the key really corresponds to a specific person.
+Baieztatugabe: Giltza hau oraindik ez da baieztatu. Ezin zara zihur egon giltza egitan norbanako zehatz batena den.
<img src="status_signature_expired_cutout_24dp"/>
-Expired: This key is no longer valid. Only the owner can extend its validity.
+Iraungitua: Giltza hau aurrerantzean ez da baliozkoa. Jabeak bakarrik luzatu dezake bere baliozkotasuna.
<img src="status_signature_revoked_cutout_24dp"/>
-Revoked: This key is no longer valid. It has been revoked by its owner.
+Ukatua: Giltza hau aurrerantzean ez da baliozkoa. Bere jabeak ukatua izan da.
-## Advanced Information
-A "key confirmation" in OpenKeychain is implemented by creating a certification according to the OpenPGP standard.
-This certification is a ["generic certification (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) described in the standard by:
-"The issuer of this certification does not make any particular assertion as to how well the certifier has checked that the owner of the key is in fact the person described by the User ID."
+## Argibide Aurreratuak
+OpenKeychain-en "giltza baieztapen" bat OpenPGP estandarraren araberako egiaztagiri bat sortuz egokitzen da.
+Egiaztapen hau da ["egiaztapen generikoa (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) estandarrean azaltzen duena honek:
+"Egiaztagiri honen jaulkitzaileak ez du inolako baieztapen berezirik egin egiaztatzaileak zein ongi egiaztatu duen Erabiltzaile ID-ak azaltzen duen norbanakoa egitan den giltzaren jabea."
-Traditionally, certifications (also with higher certification levels, such as "positive certifications" (0x13)) are organized in OpenPGP's Web of Trust.
-Our model of key confirmation is a much simpler concept to avoid common usability problems related to this Web of Trust.
-We assume that keys are verified only to a certain degree that is still usable enough to be executed "on the go".
-We also do not implement (potentially transitive) trust signatures or an ownertrust database like in GnuPG.
-Furthermore, keys which contain at least one user ID certified by a trusted key will be marked as "confirmed" in the key listings. \ No newline at end of file
+Arrunt, egiaztagiriak (baita egiaztagiritze maila handienekoak, "egiaztagiritze positiboak" bezalakoak (0x13)) OpenPGP-ren Fidagarritasun Webean daude antolatuta.
+Gure giltza baieztapena adigai askoz errazagoa da Fidagarritasun Webaren erabiltze arrunteko arazoak saihesteko.
+Onartzen dugu giltzak maila batean bakarrik daudela egiaztatuta oraindik nahikoa erabilgarria dena "joanean" exekutatuak izateko.
+Ez dugu ezartzen ere (potentzialki transitiboa) sinadura fidagarriak edo jabe-fidagarritasuneko datubase bat GnuPG-n bezala.
+Gainera, gutxienez giltza fidagarri batek egiaztatuta dauden erabiltzaile ID egiaztagiritu bat duten giltzak "baieztatua" bezala markatuko dira giltza zerrendan. \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-eu/help_changelog.md b/OpenKeychain/src/main/res/raw-eu/help_changelog.md
index ab7fa8773..d9a918b92 100644
--- a/OpenKeychain/src/main/res/raw-eu/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-eu/help_changelog.md
@@ -1,132 +1,153 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (OHARRA: Meseez jarri esaldi bakoitza bere lerroan, Transifex-ek lerroak bere itzulpen eremuan jartzen ditu!)
+
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+## 3.4
+
+ * Izengabeko giltza jeisketa Tor gain
+ * Proxy sostengua
+ * YubiKey akats kudeaketa hobea
+
+## 3.3
+
+ * Azalpen ikusleiho berria
+ * Agiri ugariren azalpena aldiberean
+ * YubiKey akatsen kudeaketa hobea
## 3.2
- * Material design
- * Integration of QR Scanner (New permissions required)
- * Improved key creation wizard
- * Fix missing contacts after sync
- * Requires Android 4
- * Redesigned key screen
- * Simplify crypto preferences, better selection of secure ciphers
- * API: Detached signatures, free selection of signing key,...
- * Fix: Some valid keys were shown revoked or expired
- * Don't accept signatures by expired or revoked subkeys
- * Keybase.io support in advanced view
+ * Lehen bertsioa YubiKey sostengu osoarekin eskuragarri erabiltzaile interfazetik: Editatu giltzak, lotu YubiKey giltzekin,...
+ Material diseinua
+ * QR Kode eskaneatzea baterapena (Baimen berriak beharrezkoak)
+ * Hobetuta giltza sortze laguntzailea
+ * Zuzenduta harreman galera aldiberetu ondoren
+ * Android 4 behar du
+ * Giltza ikusleihoaren berdiseinua
+ * Kripto hobespenen arruntzea, zifraketa seguruen hautaketa hobea
+ * API: Sinadurak deserantsita, giltza sinatuaren hautaketa askea,...
+ * Zuzenduta: Zenbait baliozko giltza ukatuta edo iraungituta erakusten dira
+ * Ez da sinadurarik onartzen iraungitutako edo ukatutako azpigiltzetatik
+ * Keybase.io sostengua ikuspegi aurreratuan
+ * Giltza guztiak batera eguneratzeko metodoa
## 3.1.2
- * Fix key export to files (now for real)
+ * Zuzenduta esportatu giltzak agirietara (orain egitan)
## 3.1.1
- * Fix key export to files (they were written partially)
- * Fix crash on Android 2.3
+ * Zuzenduta esportatu giltzak agirietara (partzialki idatzita)
+ * Zuzenduta kraskatzea Android 2.3-an
## 3.1
- * Fix crash on Android 5
- * New certify screen
- * Secure Exchange directly from key list (SafeSlinger library)
- * New QR Code program flow
- * Redesigned decrypt screen
- * New icon usage and colors
- * Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * Zuzenduta kraskatzea Android 5-ean
+ * Egiaztagiri ikusleiho berria
+ * Trukaketa Segurua zuzenean giltza zerrendatik (SafeSlinger liburutegia)
+ * QR Kode programa berria
+ * Dekriptaketa ikusleihoaren berdiseinua
+ * Ikur berria eta margoak
+ * Zuzenduta inportatu giltza sekretua Symantec Enkriptaketa Mahaigainetik
+ * YubiKey sostengu esperimentala: Azpigiltza ID-ak orain zuzen egiaztatzen dira
## 3.0.1
- * Better handling of large key imports
- * Improved subkey selection
+ * Kudeaketa hobea giltza inportatze handietarako
+ * Hobetuta azpigiltza hautapena
## 3.0
- * Full support for Yubikey signature generation and decryption!
- * Propose installable compatible apps in apps list
- * New design for decryption screens
- * Many fixes for key import, also fixes stripped keys
- * Honor and display key authenticate flags
- * User interface to generate custom keys
- * Fixing user id revocation certificates
- * New cloud search (searches over traditional keyservers and keybase.io)
- * Support for stripping keys inside OpenKeychain
+ * Eskaini bateragarritasun ezegonkorreko aplikazioak aplikazio zerrendan
+ * Diseinu berria dekriptaketa ikusleihoentzat
+ * Zuzenketa ugari giltza inportatzean, zuzenduta baita ere giltzen zuriketa
+ * Ohoretu eta erakutsi giltza egiaztapen ikurrak
+ * Erabiltzaile interfazea norbere giltzak sortzeko
+ * Zuzenduta erabiltzaile id ukatze egiaztagiriak
+ * Hodei bilaketa berria (ohiko giltza-zerbitzari eta keybase.io gain bilatzen da)
+ * Sostengua giltza zuriketarako OpenKeychain barne
+ * YubiKey sostengu esperimentala: Sostengua sinadura sortze eta dekriptaketarako
## 2.9.2
- * Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ * Zuzenduta 2.9.1-ko giltza haustea
+ * YubiKey sostengu esperimentala. Dekriptaketak orain API bidez egiten du lan
## 2.9.1
- * Split encrypt screen into two
- * Fix key flags handling (now supporting Mailvelope 0.7 keys)
- * Improved passphrase handling
- * Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Banandu enkriptaketa ikusleihoa bitan
+ * Zuzenduta giltza ikur kudeaketa (orain Mailvelope 0.7 giltzak sostengatzen dira)
+ * Hobetuta sar-esaldi kudeaketa
+ * Giltza elkarbanatzea SafeSlinger bidez
+ * YubiKey sostengu esperimentala: Hobespena beste PIN batzuk ahalbidetzeko, oraingoz OpenPGP API bidezko sinaketak besterik ez du lan egiten, ez OpenKeychain-en barne
* Fix usage of stripped keys
- * SHA256 as default for compatibility
- * Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
- * OpenPGP API now handles revoked/expired keys and returns all user ids
+ * SHA256 berezkoa bezala bateragarritasunagaitik
+ * Intent API aldatu egin da, ikusi https://github.com/open-keychain/open-keychain/wiki/Intent-API
+ * OpenPGP API-k orain ukatutako/iraungitutako giltzak kudeatzen ditu eta erabiltzaile id guztiak itzultzen ditu
## 2.9
- * Fixing crashes introduced in v2.8
- * Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
+ * v2.8-ko matxurak zuzentzen
+ * ECC sostengu esperimentala
+ * YubiKey sostengu esperimentala: Sinadura inportatutako giltzekin bakarrik
## 2.8
- * So many bugs have been fixed in this release that we focus on the main new features
- * Key edit: awesome new design, key revocation
- * Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
- * New first time screen
- * New key creation screen: autocompletion of name and email based on your personal Android accounts
- * File encryption: awesome new design, support for encrypting multiple files
- * New icons to show status of key (by Brennan Novak)
- * Important bug fix: Importing of large key collections from a file is now possible
- * Notification showing cached passphrases
- * Keys are connected to Android's contacts
+ * Akats asko zuzendu dira ezaugarri berri nagusietan foku garen argitalpen honetan.
+ * Giltza edizioa: diseinu berri zoragarria, giltza ukapena
+ * Giltza inportazioa: diseinu berri zoragarria, giltz-zerbitzari elkarketa segurua hkps bidez, giltza-zerbitzari ebazpena DNS SRV erregistroen bidez
+ * Lehen aldia ikusleiho berria
+ * Giltza sortze ikusleiho berrria: izenaren eta postaren berez-osaketa zure Android-eko kontu pertsonaletan ohinarrituta
+ * Agiri enkriptaketa: diseinu berri zoragarria, agiri ugari enkriptazeko sostengua
+ * Ikur berriak giltzaren egoera erakusteko (Brennan Novak-ek egina)
+ * Akats zuzenketa garrantzitsua: Giltza bilduma handiak inportatzea agiri batetik orain ahal da
+ * Jakinarazpenak katxeatutako sar-esaldiak erakusten ditu
+ * Giltzak Android-ren harremanetara elkartuta daude
-This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar
+Argitalpen hau ezinezkoa litzake Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray eta Thialfihar-ren lanik gabe
## 2.7
* Purple! (Dominik, Vincent)
- * New key view design (Dominik, Vincent)
- * New flat Android buttons (Dominik, Vincent)
- * API fixes (Dominik)
- * Keybase.io import (Tim Bray)
+ * Giltza ikuspegi diseinu berria (Dominik, Vincent)
+ * Android botoi lau berriak (Dominik, Vincent)
+ * API zuzenketak (Dominik)
+ * Keybase.io inportazioa (Tim Bray)
## 2.6.1
- * Some fixes for regression bugs
+ * Zenbait zuzenketa akats zaharrentzat
## 2.6
- * Key certifications (thanks to Vincent Breitmoser)
- * Support for GnuPG partial secret keys (thanks to Vincent Breitmoser)
- * New design for signature verification
- * Custom key length (thanks to Greg Witczak)
- * Fix share-functionality from other apps
+ * Giltza egiaztagiritzeak (Vincent Breitmoser-ri esker)
+ * GnuPG giltza sekretu partzialentzako sostengua (Vincent Breitmoser-ri esker)
+ * Diseinu berria sinadura egiaztapenerako
+ * Norbere giltza luzera (Greg Witczak-ri esker)
+ * Zuzenduta elkarbanatze-eginkizuna beste aplikazio batzuetatik
## 2.5
- * Fix decryption of symmetric OpenPGP messages/files
- * Refactored key edit screen (thanks to Ash Hughes)
- * New modern design for encrypt/decrypt screens
- * OpenPGP API version 3 (multiple api accounts, internal fixes, key lookup)
+ * Zuzenduta OpenPGP mezu/agiri simetrikoen dekriptaketa
+ * Berreginda giltza edizio ikusleihoa (Ash Hughes-ri esker)
+ * Diseinu moderno berria enkriptaketa/dekriptaketa ikusleihoentzat
+ * OpenPGP API bertsioa 3 (api kontu ugari, barneko zuzenketak, giltza bilaketa)
## 2.4
diff --git a/OpenKeychain/src/main/res/raw-eu/help_start.md b/OpenKeychain/src/main/res/raw-eu/help_start.md
index 4cc331942..86b6c22b4 100644
--- a/OpenKeychain/src/main/res/raw-eu/help_start.md
+++ b/OpenKeychain/src/main/res/raw-eu/help_start.md
@@ -1,16 +1,16 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (OHARRA: Meseez jarri esaldi bakoitza bere lerroan, Transifex-ek lerroak bere itzulpen eremuan jartzen ditu!)
-## How do I activate OpenKeychain in K-9 Mail?
-To use OpenKeychain with K-9 Mail, you want to follow these steps:
- 1. Open K-9 Mail and long-tap on the account you want to use OpenKeychain with.
- 2. Select "Account settings", scroll to the very bottom and click "Cryptography".
- 3. Click on "OpenPGP Provider" and select OpenKeychain from the list.
+## Nola gaitu dezaket OpenKeychain K-9 Mail-en?
+OpenKeychain K-9 Mail-ekin erabiltzeko, urrats hauek jarraitu behar dituzu:
+ 1. Ireki K-9 Mail eta egin ikutu-luzea OpenKeychain erabiltzea nahi duzun kontuan.
+ 2. Hautatu "Kontuaren ezarpenak", irristatu beheren eta klikatu "Kriptografia".
+ 3. Klikatu "OpenPGP Hornitzailea" eta hautatu OpenKeychain zerrendatik.
-## I found a bug in OpenKeychain!
-Please report the bug using the [issue tracker of OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
+## Akats bat aurkitu dut OpenKeychain-en!
+Mesedez jakinarazi akatsa erabiliz [OpenKeychain akats aztarnaria](https://github.com/openpgp-keychain/openpgp-keychain/issues).
-## Contribute
-If you want to help us developing OpenKeychain by contributing code [follow our small guide on Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
+## Lagundu
+OpenKeychain garatzen laguntzea nahi badiguzu kode ekarpenak eginez [jarraitu gure gida txikia Github-en](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
-## Translations
-Help translating OpenKeychain! Everybody can participate at [OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
+## Itzulpenak
+Lagundu OpenKeychain itzultzen! Edonork eskuhartu dezake hemen [OpenKeychain Transifex-en](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-bg/help_about.md b/OpenKeychain/src/main/res/raw-fa/help_about.md
index 9d635d9df..daecd5cbf 100644
--- a/OpenKeychain/src/main/res/raw-bg/help_about.md
+++ b/OpenKeychain/src/main/res/raw-fa/help_about.md
@@ -1,46 +1,72 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (تذکر: هر جمله در همان خط!)
[http://www.openkeychain.org](http://www.openkeychain.org)
-[OpenKeychain](http://www.openkeychain.org) is an OpenPGP implementation for Android.
+[OpenKeychain](http://www.openkeychain.org) یک نسخه از OpenPGP برای اندروید است.
-License: GPLv3+
+مجوز: GPLv3+
-## Developers
- * Dominik Schürmann (Maintainer)
- * Art O Cathain
+[//]: # (NOTE: Alphabetic ordering)
+
+توسعه‌دهنده‌گان اصلی
+ * Dominik Schürmann (نگه دارنده)
+ * Vincent Breitmoser
+
+## بیشترین مشارکت کنندگان
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (توسعه‌دهندۀ APG)
+ * Tim Bray
+
+## مشارکت کنندگان مقطعی
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
-## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+[//]: # (NOTE: Alphabetic ordering)
+
+کتابخانه‌ها
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
* [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-is/help_certification.md b/OpenKeychain/src/main/res/raw-fa/help_certification.md
index 3518adf73..94a0dc26b 100644
--- a/OpenKeychain/src/main/res/raw-is/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-fa/help_certification.md
@@ -1,7 +1,7 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (تذکر: هر جمله در همان خط!)
-## Key Confirmation
-Without confirmation, you cannot be sure if a key really corresponds to a specific person.
+## تأیید کلید
+بدون تأیید، شما نمی‌توانید مطمئن باشید که یک کلید متعلق به همان شخص است.
The simplest way to confirm a key is by scanning the QR Code or exchanging it via NFC.
To confirm keys between more than two persons, we suggest to use the key exchange method available for your keys.
diff --git a/OpenKeychain/src/main/res/raw-ro/help_changelog.md b/OpenKeychain/src/main/res/raw-fa/help_changelog.md
index ab7fa8773..e84775421 100644
--- a/OpenKeychain/src/main/res/raw-ro/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-fa/help_changelog.md
@@ -1,9 +1,29 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (تذکر: هر جمله در همان خط!)
+
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+## 3.4
+
+ * دانلود کلید به صورت مخفی توسط Tor
+ * پشتیبانی از پراکسی
+ * مدیریت بهترِ خطاهای کلید Yubi
+
+## 3.3
+
+ * New decryption screen
+ * Decryption of multiple files at once
+ * Better handling of YubiKey errors
## 3.2
+ * First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
* Material design
- * Integration of QR Scanner (New permissions required)
+ * Integration of QR Code Scanning (New permissions required)
* Improved key creation wizard
* Fix missing contacts after sync
* Requires Android 4
@@ -13,6 +33,7 @@
* Fix: Some valid keys were shown revoked or expired
* Don't accept signatures by expired or revoked subkeys
* Keybase.io support in advanced view
+ * Method to update all keys at once
## 3.1.2
@@ -35,7 +56,7 @@
* Redesigned decrypt screen
* New icon usage and colors
* Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * Experimental YubiKey support: Subkey IDs are now checked correctly
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Full support for Yubikey signature generation and decryption!
* Propose installable compatible apps in apps list
* New design for decryption screens
* Many fixes for key import, also fixes stripped keys
@@ -55,12 +75,13 @@
* Fixing user id revocation certificates
* New cloud search (searches over traditional keyservers and keybase.io)
* Support for stripping keys inside OpenKeychain
+ * Experimental YubiKey support: Support for signature generation and decryption
## 2.9.2
* Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ * Experimental YubiKey support: Decryption now working via API
## 2.9.1
@@ -69,7 +90,7 @@
* Fix key flags handling (now supporting Mailvelope 0.7 keys)
* Improved passphrase handling
* Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
* Fix usage of stripped keys
* SHA256 as default for compatibility
* Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Fixing crashes introduced in v2.8
* Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
+ * Experimental YubiKey support: Only signing with imported keys
## 2.8
@@ -206,7 +227,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
* App2sd
* More choices for passphrase cache: 1, 2, 4, 8, hours
* Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
- * Bugfixes
+ تعمیر باگ‌ها
* Optimizations
@@ -224,7 +245,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
* Stream encryption/decryption (gallery, etc.)
* New options (language, force v3 signatures)
* Interface changes
- * Bugfixes
+ تعمیر باگ‌ها
## 1.0.5
@@ -266,4 +287,4 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
* Slovenian translation
* New database, much faster, less memory usage
* Defined Intents and content provider for other apps
- * Bugfixes \ No newline at end of file
+ تعمیر باگ‌ها \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-fa/help_start.md b/OpenKeychain/src/main/res/raw-fa/help_start.md
new file mode 100644
index 000000000..5bf44a862
--- /dev/null
+++ b/OpenKeychain/src/main/res/raw-fa/help_start.md
@@ -0,0 +1,16 @@
+[//]: # (تذکر: هر جمله در همان خط!)
+
+## چگونه OpenKeychain را در برنامهٔ K-9 Mail فعال کنم؟
+برای استفاده از OpenKeychain در برنامهٔ K-9 Mail، مراحل زیر را دنبال کنید:
+ 1. برنامهٔ K-9 Mail را باز کنید و روی حسابی که می‌خواهید در آن از OpenKeychain استفاده کنید، تَپ کرده و نگه دارید.
+ 2. "تنظیمات حساب" را انتخاب کنید، به پایین صفحه بروید و "رمزنگاری" را انتخاب کنید.
+ 3. روی "فراهم‌کنندهٔ OpenPGP" تپ کرده و OpenKeychain را از لیست انتخاب کنید.
+
+## من یک باگ در OpenKeychain پیدا کردم!
+لطفاً باگ را از این طریق [دنبال‌کنندهٔ مسائلِ OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues) گزارش کنید.
+
+## مشارکت
+اگر می‌خواهید با نوشتن کد به ما در توسعهٔ OpenKeychain کمک کنید، [راهنمایی ما را در Github دنبال کنید](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
+
+## ترجمه‌ها
+کمک کنید تا OpenKeychain را ترجمه کنیم! هر کسی می‌تواند از طریق [OpenKeychain بر روی Transifex](https://www.transifex.com/projects/p/open-keychain/) مشارکت داشته باشد. \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-fi/help_about.md b/OpenKeychain/src/main/res/raw-fi/help_about.md
index 9d635d9df..06bed0021 100644
--- a/OpenKeychain/src/main/res/raw-fi/help_about.md
+++ b/OpenKeychain/src/main/res/raw-fi/help_about.md
@@ -6,41 +6,67 @@
License: GPLv3+
-## Developers
+[//]: # (NOTE: Alphabetic ordering)
+
+## Main Developers
* Dominik Schürmann (Maintainer)
- * Art O Cathain
+ * Vincent Breitmoser
+
+## Top Contributors
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG developer)
+ * Tim Bray
+
+## Occasional Contributors
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//]: # (NOTE: Alphabetic ordering)
## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
* [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-fi/help_changelog.md b/OpenKeychain/src/main/res/raw-fi/help_changelog.md
index ab7fa8773..11d7975b1 100644
--- a/OpenKeychain/src/main/res/raw-fi/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-fi/help_changelog.md
@@ -1,9 +1,29 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+## 3.4
+
+ * Anonymous key download over Tor
+ * Proxy support
+ * Better YubiKey error handling
+
+## 3.3
+
+ * New decryption screen
+ * Decryption of multiple files at once
+ * Better handling of YubiKey errors
+
## 3.2
+ * First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
* Material design
- * Integration of QR Scanner (New permissions required)
+ * Integration of QR Code Scanning (New permissions required)
* Improved key creation wizard
* Fix missing contacts after sync
* Requires Android 4
@@ -13,6 +33,7 @@
* Fix: Some valid keys were shown revoked or expired
* Don't accept signatures by expired or revoked subkeys
* Keybase.io support in advanced view
+ * Method to update all keys at once
## 3.1.2
@@ -35,7 +56,7 @@
* Redesigned decrypt screen
* New icon usage and colors
* Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * Experimental YubiKey support: Subkey IDs are now checked correctly
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Full support for Yubikey signature generation and decryption!
* Propose installable compatible apps in apps list
* New design for decryption screens
* Many fixes for key import, also fixes stripped keys
@@ -55,12 +75,13 @@
* Fixing user id revocation certificates
* New cloud search (searches over traditional keyservers and keybase.io)
* Support for stripping keys inside OpenKeychain
+ * Experimental YubiKey support: Support for signature generation and decryption
## 2.9.2
* Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ * Experimental YubiKey support: Decryption now working via API
## 2.9.1
@@ -69,7 +90,7 @@
* Fix key flags handling (now supporting Mailvelope 0.7 keys)
* Improved passphrase handling
* Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
* Fix usage of stripped keys
* SHA256 as default for compatibility
* Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Fixing crashes introduced in v2.8
* Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
+ * Experimental YubiKey support: Only signing with imported keys
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-fr/help_about.md b/OpenKeychain/src/main/res/raw-fr/help_about.md
index edf3dd78d..5994ae4df 100644
--- a/OpenKeychain/src/main/res/raw-fr/help_about.md
+++ b/OpenKeychain/src/main/res/raw-fr/help_about.md
@@ -6,41 +6,67 @@
Licence : GPLv3+
-## Développeurs
+[//] : # (NOTE : tri alphabétique)
+
+## Développeurs principaux
* Dominik Schürmann (mainteneur)
- * Art O Cathain
+ * Vincent Breitmoser
+
+## Meilleurs contributeurs
+ * Adithya Abraham Philip
* Ash Hughes
+ * « mar-v-in »
+ * 'Thialfihar' (développeur APG)
+ * Tim Bray
+
+## Contributeurs occasionnels
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar « kalkin » Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * « mar-v-in »
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* « Senecaso »
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//] : # (NOTE : tri alphabétique)
## Bibliothèques
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (Licence MIT X11)
- * [Bibliothèque d'échange SafeSlinger](https://github.com/SafeSlingerProject/exchange-android) (Licence MIT)
* [Bibliothèques de soutien Android](http://developer.android.com/tools/support-library/index.html) (Licence Apache v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Licence Apache v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Licence Apache v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Licence Apache v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Licence Apache v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Licence Apache v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Licence Apache v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Licence Apache v2)
- * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Licence Apache v2)
- * [ZXing](https://github.com/zxing/zxing) (Licence Apache v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Licence Apache v2)
+ * [OkHttp](http://square.github.io/okhttp/) (licence Apache v2)
* [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Conception matérielle)</a> (Licence Apache v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Licence Apache v2)
+ * [Bibliothèque d'échange SafeSlinger](https://github.com/SafeSlingerProject/exchange-android) (Licence MIT)
* [Snackbar](https://github.com/nispok/snackbar) (Licence MIT)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Licence Apache v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Licence Apache v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Licence Apache v2) \ No newline at end of file
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (Licence MIT X11)
+ * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Licence Apache v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Licence Apache v2)
+ * [ZXing](https://github.com/zxing/zxing) (Licence Apache v2)
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Licence Apache v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-fr/help_certification.md b/OpenKeychain/src/main/res/raw-fr/help_certification.md
index 643756bcf..87473a2e7 100644
--- a/OpenKeychain/src/main/res/raw-fr/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-fr/help_certification.md
@@ -2,13 +2,13 @@
## Confirmation de clef
Sans confirmation, vous ne pouvez pas être certain que la clef appartient à une personne déterminée.
-La façon la plus simple de confirmer une clef est en balayant le code QR ou en l'échangeant par NFC.
+La façon la plus simple de confirmer une clef est en lisant le code QR ou en l'échangeant par NFC.
Pour confirmer des clefs entre plus de deux personnes, nous suggérons d'utiliser la méthode d'échange de clef proposée pour vos clefs.
## État de la clef
<img src="status_signature_verified_cutout_24dp"/>
-Confirmée : vous avez déjà confirmé cette clef, p. ex. en balayant le code QR.
+Confirmée : vous avez déjà confirmé cette clef, p. ex. en lisant le code QR.
<img src="status_signature_unverified_cutout_24dp"/>
Non confirmée : cette clef n'a pas encore été confirmée. Vous ne pouvez pas être certain que la clef appartient à une personne déterminée.
<img src="status_signature_expired_cutout_24dp"/>
diff --git a/OpenKeychain/src/main/res/raw-fr/help_changelog.md b/OpenKeychain/src/main/res/raw-fr/help_changelog.md
index b221eda6d..44e949c77 100644
--- a/OpenKeychain/src/main/res/raw-fr/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-fr/help_changelog.md
@@ -1,9 +1,29 @@
[//] : # (NOTE : veuillez mettre chaque phrase sur sa propre ligne. Transifex met chaque ligne dans son propre champ de traduction !)
+## 3.5
+
+ * révocation de la clef lors de la suppression de la clef
+ * Vérifications améliorées à la recherche d'une cryptographie non fiable
+ * Correctif : ne pas fermer OpenKeychain après une réussite de l'assistant de première utilisation
+ * API : version 8
+
+## 3.4
+
+ * Téléchargement anonyme de clefs avec Tor
+ * Prise en charge des serveurs mandataires
+ * Meilleur gestion des erreurs de la ClefYubi
+
+## 3.3
+
+ * Nouvel écran de déchiffrement
+ * Déchiffrement simultané de plusieurs fichiers
+ * Meilleure gestion des erreurs ClefYubi
+
## 3.2
+ * Première version avec prise en charge complète de la ClefYubi, proposée dans l'interface utilisateur : modifier les clefs, relier la clef Yubi au clefs...
* Conception matérielle
- * Intégration du lecteur QR (nouvelles permissions exigées)
+ * Intégration de la lecture de code QR (nouvelles permissions exigées)
* Amélioration de l'assistant de création de clef
* Correctif - Contacts manquants après la synchro
* Android 4 exigé
@@ -13,6 +33,7 @@
* Correctif - Certaines clefs valides apparaissaient comme révoquées ou expirées
* Ne pas accepter de signatures par des sous-clefs expirées ou révoquées
* Prise en charge de keybase.io dans la vue avancée
+ * Méthode pour mettre toutes les clefs à jour en même temps
## 3.1.2
@@ -35,7 +56,7 @@
* Écran de déchiffrement redessiné
* Nouveaux agencement et couleurs d'icônes
* Importation des clefs secrètes corrigée de Symantec Encryption Desktop
- * Les ID de sous-clefs des Yubikeys sont maintenant vérifiés correctement
+ * Prise en charge expérimentale de la ClefYubi : les ID de sous-clefs sont maintenant vérifiés correctement
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Prise en charge complète de la génération de signature par Yubikey et de leur déchiffrement !
* Des applis compatibles installables sont proposées dans la liste des applis
* Nouvelle conception pour les écrans de déchiffrement
* Nombreux correctifs d'importation des clefs, corrigent aussi les clefs dépouillées
@@ -55,12 +75,13 @@
* Corrigé - Certificats de révocation des ID utilisateurs
* Nouvelle recherche nuagique (dans les serveurs traditionnels et dans keybase.io)
* Prise en charge du dépouillement des clefs dans OpenKeychain
+ * Prise en charge expérimentale de la ClefYubi : prise en charge de la génération de signature et le déchiffrement
## 2.9.2
* Correctif - Clefs brisées dans 2.9.1
- * Le déchiffrement des Yukukeys par l'API fonctionne maintenant
+ * Prise en charge expérimentale de la ClefYubi : le déchiffrement fonctionne maintenant avec l'API
## 2.9.1
@@ -69,7 +90,7 @@
* Correctif - Gestion des drapeaux de clefs (prend maintenant en charge les clefs Mailvelope 0.7)
* Gestion des phrases de passe améliorée
* Partage de clefs par SafeSlinger
- * Yubikey : préférence pour permette d'autre NIP, seule la signature par l'API OpenPGP fonctionne présentement, mais pas à l'intérieur d'OpenKeychain
+ * Prise en charge expérimentale de la ClefYubi : préférence pour permettre d'autres NIP, seule la signature par l'API OpenPGP fonctionne actuellement, mais pas dans OpenKeychain
* Correctif - Utilisation de clefs dépouillées
* SHA256 par défaut pour la compatibilité
* L'API des intentions a changé, voir https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Correction des plantages présents dans v2.8
* Prise en charge expérimentale CCE
- * Prise en charge expérimentale de Yubikey (signature seulement avec les clefs importées)
+ * Prise en charge expérimentale de la ClefYubi : signature seulement avec les clefs importées
## 2.8
@@ -180,7 +201,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
## 2.1.1
- * Mise à jour de l'API, préparation à l'intégration à K-9 Mail
+ * Mise à jour de l'API, préparation à l'intégration à Courriel K-9 Mail
## 2.1
@@ -251,7 +272,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
* Listes de clefs filtrables
* Présélection plus intelligente des clefs de chiffrement
* Nouvelle gestion des intentions pour VIEW et SEND, permet le chiffrement/déchiffrement des fichiers du gestionnaires de fichiers
- * Correctifs et fonctions additionnelles (présélection des clefs) pour K-9-Mail, nouvelle version bêta proposée
+ * Correctifs et fonctions additionnelles (présélection des clefs) pour Courriel K-9-Mail, nouvelle version bêta proposée
## 1.0.1
@@ -261,7 +282,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
## 1.0.0
- * Intégration à K-9 Mail, APG prenant en charge la version bêta de K-9 Mail
+ * Intégration à K-9 Mail, APG prenant en charge la version bêta de Courriel K-9 Mail
* Prise en charge de plus de gestionnaires de fichiers (incluant ASTRO)
* Traduction slovène
* Nouvelle base de données, bien plus rapide, utilisation de la mémoire moindre
diff --git a/OpenKeychain/src/main/res/raw-fr/help_start.md b/OpenKeychain/src/main/res/raw-fr/help_start.md
index 7ac45cc88..9e712c220 100644
--- a/OpenKeychain/src/main/res/raw-fr/help_start.md
+++ b/OpenKeychain/src/main/res/raw-fr/help_start.md
@@ -1,8 +1,8 @@
[//] : # (NOTE : veuillez mettre chaque phrase dans sa propre ligne. Transifex met chaque ligne dans son propre champ de traduction !)
-## Comment puis-je activer OpenKeychain dans K-9 Mail ?
-Pour utiliser OpenKeychain avec K-9 Mail, vous devez suivre ces étapes :
- 1. Ouvrez K-9 Mail et appuyez longuement sur le compte avec lequel vous voulez utiliser OpenKeychain.
+## Comment puis-je activer OpenKeychain dans Courriel K-9 Mail ?
+Pour utiliser OpenKeychain avec Courriel K-9 Mail, vous devez suivre ces étapes :
+ 1. Ouvrez Courriel K-9 Mail et appuyez longuement sur le compte avec lequel vous voulez utiliser OpenKeychain.
2. Sélectionnez « Paramètres du compte », faite défiler vers le bas et cliquez sur « Cryptographie ».
3. Cliquez sur « Fournisseur OpenPGP » et sélectionnez OpenKeychain dans la liste.
diff --git a/OpenKeychain/src/main/res/raw-pt/help_about.md b/OpenKeychain/src/main/res/raw-hu/help_about.md
index 9d635d9df..06bed0021 100644
--- a/OpenKeychain/src/main/res/raw-pt/help_about.md
+++ b/OpenKeychain/src/main/res/raw-hu/help_about.md
@@ -6,41 +6,67 @@
License: GPLv3+
-## Developers
+[//]: # (NOTE: Alphabetic ordering)
+
+## Main Developers
* Dominik Schürmann (Maintainer)
- * Art O Cathain
+ * Vincent Breitmoser
+
+## Top Contributors
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG developer)
+ * Tim Bray
+
+## Occasional Contributors
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//]: # (NOTE: Alphabetic ordering)
## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
* [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-bg/help_certification.md b/OpenKeychain/src/main/res/raw-hu/help_certification.md
index 3518adf73..3518adf73 100644
--- a/OpenKeychain/src/main/res/raw-bg/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-hu/help_certification.md
diff --git a/OpenKeychain/src/main/res/raw-is/help_changelog.md b/OpenKeychain/src/main/res/raw-hu/help_changelog.md
index ab7fa8773..11d7975b1 100644
--- a/OpenKeychain/src/main/res/raw-is/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-hu/help_changelog.md
@@ -1,9 +1,29 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+## 3.4
+
+ * Anonymous key download over Tor
+ * Proxy support
+ * Better YubiKey error handling
+
+## 3.3
+
+ * New decryption screen
+ * Decryption of multiple files at once
+ * Better handling of YubiKey errors
+
## 3.2
+ * First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
* Material design
- * Integration of QR Scanner (New permissions required)
+ * Integration of QR Code Scanning (New permissions required)
* Improved key creation wizard
* Fix missing contacts after sync
* Requires Android 4
@@ -13,6 +33,7 @@
* Fix: Some valid keys were shown revoked or expired
* Don't accept signatures by expired or revoked subkeys
* Keybase.io support in advanced view
+ * Method to update all keys at once
## 3.1.2
@@ -35,7 +56,7 @@
* Redesigned decrypt screen
* New icon usage and colors
* Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * Experimental YubiKey support: Subkey IDs are now checked correctly
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Full support for Yubikey signature generation and decryption!
* Propose installable compatible apps in apps list
* New design for decryption screens
* Many fixes for key import, also fixes stripped keys
@@ -55,12 +75,13 @@
* Fixing user id revocation certificates
* New cloud search (searches over traditional keyservers and keybase.io)
* Support for stripping keys inside OpenKeychain
+ * Experimental YubiKey support: Support for signature generation and decryption
## 2.9.2
* Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ * Experimental YubiKey support: Decryption now working via API
## 2.9.1
@@ -69,7 +90,7 @@
* Fix key flags handling (now supporting Mailvelope 0.7 keys)
* Improved passphrase handling
* Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
* Fix usage of stripped keys
* SHA256 as default for compatibility
* Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Fixing crashes introduced in v2.8
* Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
+ * Experimental YubiKey support: Only signing with imported keys
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-bg/help_start.md b/OpenKeychain/src/main/res/raw-hu/help_start.md
index 4cc331942..4cc331942 100644
--- a/OpenKeychain/src/main/res/raw-bg/help_start.md
+++ b/OpenKeychain/src/main/res/raw-hu/help_start.md
diff --git a/OpenKeychain/src/main/res/raw-is/help_about.md b/OpenKeychain/src/main/res/raw-is/help_about.md
deleted file mode 100644
index 9d635d9df..000000000
--- a/OpenKeychain/src/main/res/raw-is/help_about.md
+++ /dev/null
@@ -1,46 +0,0 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-
-[http://www.openkeychain.org](http://www.openkeychain.org)
-
-[OpenKeychain](http://www.openkeychain.org) is an OpenPGP implementation for Android.
-
-License: GPLv3+
-
-## Developers
- * Dominik Schürmann (Maintainer)
- * Art O Cathain
- * Ash Hughes
- * Brian C. Barnes
- * Bahtiar 'kalkin' Gadimov
- * Daniel Albert
- * Daniel Hammann
- * Daniel Haß
- * Greg Witczak
- * 'mar-v-in'
- * Markus Doits
- * Miroojin Bakshi
- * Nikhil Peter Raj
- * Paul Sarbinowski
- * 'Senecaso'
- * Signe Rüsch
- * Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
-
-## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
- * [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
- * [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
- * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
- * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
- * [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-is/help_start.md b/OpenKeychain/src/main/res/raw-is/help_start.md
deleted file mode 100644
index 4cc331942..000000000
--- a/OpenKeychain/src/main/res/raw-is/help_start.md
+++ /dev/null
@@ -1,16 +0,0 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-
-## How do I activate OpenKeychain in K-9 Mail?
-To use OpenKeychain with K-9 Mail, you want to follow these steps:
- 1. Open K-9 Mail and long-tap on the account you want to use OpenKeychain with.
- 2. Select "Account settings", scroll to the very bottom and click "Cryptography".
- 3. Click on "OpenPGP Provider" and select OpenKeychain from the list.
-
-## I found a bug in OpenKeychain!
-Please report the bug using the [issue tracker of OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
-
-## Contribute
-If you want to help us developing OpenKeychain by contributing code [follow our small guide on Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
-
-## Translations
-Help translating OpenKeychain! Everybody can participate at [OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-it/help_about.md b/OpenKeychain/src/main/res/raw-it/help_about.md
index 9d635d9df..dbf7b57a7 100644
--- a/OpenKeychain/src/main/res/raw-it/help_about.md
+++ b/OpenKeychain/src/main/res/raw-it/help_about.md
@@ -1,46 +1,72 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTA: Si prega di mettere ogni frase in una propria linea, Transifex mette ogni riga nel proprio campo di traduzione!)
[http://www.openkeychain.org](http://www.openkeychain.org)
-[OpenKeychain](http://www.openkeychain.org) is an OpenPGP implementation for Android.
+[OpenKeychain](http://www.openkeychain.org) e un impelementazione OpenPGP per Android.
-License: GPLv3+
+Licenza: GPLv3+
-## Developers
- * Dominik Schürmann (Maintainer)
- * Art O Cathain
+[//]: # (NOTE: Alphabetic ordering)
+
+## Sviluppatori principali
+ * Dominik Schürmann (Manutentore)
+ * Vincent Breitmoser
+
+## Top Contributors
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (Sviluppatore APG)
+ * Tim Bray
+
+## Occasional Contributors
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
-## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+[//]: # (NOTE: Alphabetic ordering)
+
+## Librerie
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Design materiale) (Apache License v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
* [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-it/help_certification.md b/OpenKeychain/src/main/res/raw-it/help_certification.md
index 3518adf73..8b4d58955 100644
--- a/OpenKeychain/src/main/res/raw-it/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-it/help_certification.md
@@ -1,28 +1,28 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTA: Si prega di mettere ogni frase in una propria linea, Transifex mette ogni riga nel proprio campo di traduzione!)
-## Key Confirmation
-Without confirmation, you cannot be sure if a key really corresponds to a specific person.
-The simplest way to confirm a key is by scanning the QR Code or exchanging it via NFC.
-To confirm keys between more than two persons, we suggest to use the key exchange method available for your keys.
+## Conferma chiave
+Senza conferma, non puoi essere sicuro la chiave veramente corrisponde a una persona specifica.
+Il modo più semplice per confermare una chiave è la scansione del codice QR o scambiarla via NFC.
+Per confermare le chiavi tra più di due persone, si consiglia di utilizzare il metodo di scambio di chiavi a disposizione per le vostre chiavi.
-## Key Status
+## Stato chiave
<img src="status_signature_verified_cutout_24dp"/>
-Confirmed: You have already confirmed this key, e.g., by scanning the QR Code.
+Confermato: Hai già confermato questa chiave, ad esempio, attraverso la scansione del codice QR.
<img src="status_signature_unverified_cutout_24dp"/>
-Unconfirmed: This key has not been confirmed yet. You cannot be sure if the key really corresponds to a specific person.
+Non confermato: Questa chiave non è ancora stata confermata. Non si può essere sicuri se la chiave corrisponde davvero a una persona specifica.
<img src="status_signature_expired_cutout_24dp"/>
-Expired: This key is no longer valid. Only the owner can extend its validity.
+Scaduta: Questa chiave non è più valida. Solo il proprietario può estendere la sua validità.
<img src="status_signature_revoked_cutout_24dp"/>
-Revoked: This key is no longer valid. It has been revoked by its owner.
+Revoca: Questa chiave non è più valida. È stata revocata dal suo proprietario.
-## Advanced Information
-A "key confirmation" in OpenKeychain is implemented by creating a certification according to the OpenPGP standard.
-This certification is a ["generic certification (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) described in the standard by:
-"The issuer of this certification does not make any particular assertion as to how well the certifier has checked that the owner of the key is in fact the person described by the User ID."
+## Informazioni avanzate
+Una "conferma chiave" in OpenKeychain è attuato mediante la creazione di una certificazione secondo lo standard OpenPGP.
+Questa certificazione è un ["generic certification (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) indicata nella norma per:
+"L'emittente di tale certificazione non va alcun particolare asserzione da quanto accurato il certificatore ha verificato che il proprietario della chiave è infatti la persona descritta dal ID utente."
-Traditionally, certifications (also with higher certification levels, such as "positive certifications" (0x13)) are organized in OpenPGP's Web of Trust.
-Our model of key confirmation is a much simpler concept to avoid common usability problems related to this Web of Trust.
-We assume that keys are verified only to a certain degree that is still usable enough to be executed "on the go".
-We also do not implement (potentially transitive) trust signatures or an ownertrust database like in GnuPG.
-Furthermore, keys which contain at least one user ID certified by a trusted key will be marked as "confirmed" in the key listings. \ No newline at end of file
+Tradizionalmente, certificazioni (anche con livelli di certificazione più elevati, come "certificazioni positivi" (0x13)) sono organizzate su OpenPGP nel web di confidenza ("Web of trust").
+Il nostro modello di conferma chiave è un concetto molto più semplice per evitare problemi di usabilità comuni relativi a questo Web of Trust.
+Assumiamo che le chiavi vengono verificati solo fino ad un certo grado che è ancora abbastanza utilizzabile da eseguire "in movimento".
+Inoltre, non implementiamo (potenzialmente transitive) firme fiduciarie o un database ownertrust come in GnuPG.
+Inoltre, chiavi che contengono almeno un ID utente certificata da una chiave di fiducia saranno contrassegnati come "confermato" negli elenchi principali. \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-it/help_changelog.md b/OpenKeychain/src/main/res/raw-it/help_changelog.md
index ab7fa8773..e5ebfc8c1 100644
--- a/OpenKeychain/src/main/res/raw-it/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-it/help_changelog.md
@@ -1,9 +1,29 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTA: Si prega di mettere ogni frase in una propria linea, Transifex mette ogni riga nel proprio campo di traduzione!)
+
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+## 3.4
+
+ * Anonymous key download over Tor
+ * Proxy support
+ * Better YubiKey error handling
+
+## 3.3
+
+ * New decryption screen
+ * Decryption of multiple files at once
+ * Better handling of YubiKey errors
## 3.2
+ * First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
* Material design
- * Integration of QR Scanner (New permissions required)
+ * Integration of QR Code Scanning (New permissions required)
* Improved key creation wizard
* Fix missing contacts after sync
* Requires Android 4
@@ -13,6 +33,7 @@
* Fix: Some valid keys were shown revoked or expired
* Don't accept signatures by expired or revoked subkeys
* Keybase.io support in advanced view
+ * Method to update all keys at once
## 3.1.2
@@ -35,7 +56,7 @@
* Redesigned decrypt screen
* New icon usage and colors
* Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * Experimental YubiKey support: Subkey IDs are now checked correctly
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Full support for Yubikey signature generation and decryption!
* Propose installable compatible apps in apps list
* New design for decryption screens
* Many fixes for key import, also fixes stripped keys
@@ -55,12 +75,13 @@
* Fixing user id revocation certificates
* New cloud search (searches over traditional keyservers and keybase.io)
* Support for stripping keys inside OpenKeychain
+ * Experimental YubiKey support: Support for signature generation and decryption
## 2.9.2
* Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ * Experimental YubiKey support: Decryption now working via API
## 2.9.1
@@ -69,7 +90,7 @@
* Fix key flags handling (now supporting Mailvelope 0.7 keys)
* Improved passphrase handling
* Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
* Fix usage of stripped keys
* SHA256 as default for compatibility
* Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Fixing crashes introduced in v2.8
* Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
+ * Experimental YubiKey support: Only signing with imported keys
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-it/help_start.md b/OpenKeychain/src/main/res/raw-it/help_start.md
index 4cc331942..bac4542ab 100644
--- a/OpenKeychain/src/main/res/raw-it/help_start.md
+++ b/OpenKeychain/src/main/res/raw-it/help_start.md
@@ -1,16 +1,16 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTA: Si prega di mettere ogni frase in una propria linea, Transifex mette ogni riga nel proprio campo di traduzione!)
-## How do I activate OpenKeychain in K-9 Mail?
-To use OpenKeychain with K-9 Mail, you want to follow these steps:
- 1. Open K-9 Mail and long-tap on the account you want to use OpenKeychain with.
- 2. Select "Account settings", scroll to the very bottom and click "Cryptography".
- 3. Click on "OpenPGP Provider" and select OpenKeychain from the list.
+## Come attivare OpenKeychain in K-9 Mail?
+Per usare OpenKeychain con K-9 Mail, vuoi segire questi passi:
+ 1. Avia K-9 Mail e premi lungo sul account che vuoi usare con OpenKeychain.
+ 2. Seleziona "Impostazioni account", scorri verso il basso e clicca "Crittografia".
+ 3. Clicca su "OpenPGP Provider" e seleziona OpenKeychain dalla lista.
-## I found a bug in OpenKeychain!
-Please report the bug using the [issue tracker of OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
+## Ho trovato un bug in OpenKeychain!
+Si prega di segnalare dei bug utilizzando [issue tracker di OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
-## Contribute
-If you want to help us developing OpenKeychain by contributing code [follow our small guide on Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
+## Contribuire
+Se vuoi aiutarci a sviluppare OpenKeychain contribuendo codice [segui la nostra piccola guida su Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
-## Translations
-Help translating OpenKeychain! Everybody can participate at [OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
+## Traduzioni
+Aiuta a tradurre OpenKeychain! Tutti possono partecipare su [OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-ja/help_about.md b/OpenKeychain/src/main/res/raw-ja/help_about.md
index f19a5f877..17e3e17e3 100644
--- a/OpenKeychain/src/main/res/raw-ja/help_about.md
+++ b/OpenKeychain/src/main/res/raw-ja/help_about.md
@@ -6,41 +6,67 @@
ライセンス: GPLv3以降
-## Developers## 開発者
+[//]: # (NOTE: Alphabetic ordering)
+
+## メイン開発者
* Dominik Schürmann (メンテナ)
- * Art O Cathain
+ * Vincent Breitmoser
+
+## 最優秀貢献者たち
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG 開発者)
+ * Tim Bray
+
+## 時折貢献してくれる方たち
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//]: # (NOTE: Alphabetic ordering)
## ライブラリ
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
- * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
- * [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
* [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
* [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
+ * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [ZXing](https://github.com/zxing/zxing) (Apache License v2)
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-ja/help_certification.md b/OpenKeychain/src/main/res/raw-ja/help_certification.md
index 8b4364c09..8de3079cd 100644
--- a/OpenKeychain/src/main/res/raw-ja/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-ja/help_certification.md
@@ -19,10 +19,10 @@
## 詳細情報
OpenKeychainでの"鍵の検証"はOpenPGP標準に準拠した証明を生成する実装がなされています。
この証明は ["汎用証明 (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) として標準に以下として記述されています:
-"The issuer of this certification does not make any particular assertion as to how well the certifier has checked that the owner of the key is in fact the person described by the User ID."
+"この証明書の発行者は、認証者は、鍵の所有者が実際にユーザIDによって記述の人であることを確認したこの証明書のように、任意の特定の主張を行いません。"
歴史的に、証明(またより高いレベルの証明、"肯定的な証明" (0x13)) は OpenPGPによるWeb of Trustとして組織化されます。
われわれの鍵の証明モデルはとてもシンプルなコンセプトによって関連する一般的なユーザビリティの問題を回避する概念です。
-We assume that keys are verified only to a certain degree that is still usable enough to be executed "on the go".
-We also do not implement (potentially transitive) trust signatures or an ownertrust database like in GnuPG.
-Furthermore, keys which contain at least one user ID certified by a trusted key will be marked as "confirmed" in the key listings. \ No newline at end of file
+私たちは、鍵が唯一の "外出先で"まだ実行されるのに十分な使用可能なある程度検証されることを想定しています。
+しかし私達は(潜在的推移則での)信頼署名やGnuPG式の所有信頼データベースの実装はしません。
+さらに、信頼できるキーで証明された少なくとも1つのユーザIDが含まれるキーは、キーのリストに「確認」としてマークされます。 \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-ja/help_changelog.md b/OpenKeychain/src/main/res/raw-ja/help_changelog.md
index 181544767..148a33206 100644
--- a/OpenKeychain/src/main/res/raw-ja/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-ja/help_changelog.md
@@ -1,9 +1,29 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+## 3.5
+
+ * 鍵削除時の鍵の破棄
+ * セキュアではない暗号についてのチェックの改善
+ * 修正: OpenKeychainの初回ウィザード成功後クローズされない問題
+ * API: Version 8
+
+## 3.4
+
+ * Tor経由での匿名鍵ダウンロード
+ * プロキシのサポート
+ * よりよりYubiKeyのエラーの扱い
+
+## 3.3
+
+ * 新しい復号画面
+ * 一度に複数のファイルの復号
+ * YubiKeyのエラーについてよりよい扱い
+
## 3.2
+ * 完全なYubiKeyサポートがユーザーインタフェースから利用できる最初のバージョン: 鍵の編集、YubiKeyと鍵の紐付け、...
* マテリアルデザイン
- * QRスキャナの統合 (新しいパーミッションを必要とします)
+ * QRコードのスキャンの統合 (新しいパーミッションを必要とします)
* 鍵生成ウィザードの改善
* 同期後に連絡先を見失う問題の修正
* Android 4を必要とします
@@ -13,6 +33,7 @@
* 修正: いくつかの正しい鍵が破棄もしくは期限切れとして表示される
* 副鍵が期限切れもしくは破棄されている場合に署名を受け入れない
* 拡張ビューでのKeybase.ioのサポート
+ * すべての鍵を一度にアップデートするメソッド
## 3.1.2
@@ -35,7 +56,7 @@
* 復号化画面の再デザイン
* 新しいアイコン利用とカラー
* Symantec Encryption Desktopから秘密鍵をインポート時の問題修正
- * Yubikeyでの副鍵IDを正くチェックするようになりました
+ * 実験的なYubiKeyサポート: Yubikeyでの副鍵IDを正くチェックするようになりました
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Yubikeyでの署名生成と復号化のフルサポート
* インストールできるAPIの互換性のあるアプリをアプリ内リストで提示します
* 復号化画面を新しいデザインに
* 鍵のインポートで沢山の修正、また鍵のストリップでも修正
@@ -55,12 +75,13 @@
* ユーザーID破棄証明の修正
* 新しいクラウド検索 (古典的な keyserverと keybase.io から検索します)
* OpenKeychain内で鍵をストリップするのをサポートしました
+ * 実験的なYubiKeyサポート: Yubikeyでの署名生成と復号化のフルサポート
## 2.9.2
* 2.9.1での鍵破壊問題修正
- * API経由でYubikeyの復号処理が動くようになった
+ * 実験的なYubiKeyサポート: API経由でYubikeyの復号処理が動くようになった
## 2.9.1
@@ -69,7 +90,7 @@
* 鍵のフラグ管理を修正 (現在Mailvelope 0.7 鍵をサポート)
* パスフレーズの取り回しを改善
* SafeSlingerでの鍵の共有
- * Yubikey: 設定で他のPINを受け付け、現在OpenPGP API経由での署名しか動きません、OpenKeychainの内部ではないため
+ * 実験的なYubiKeyサポート: 設定で他のPINを受け付け、現在OpenPGP API経由での署名しか動きません、OpenKeychainの内部ではないため
* ストリップした鍵の利用法を修正
* 互換性のためデフォルトをSHA256に
* インテント API を変更しました、以下参照 https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* v2.8 から発生したクラッシュ問題をFix
* 実験的にECCをサポート
- * 実験的にYubikeyをサポート(インポート済みの鍵での署名のみ)
+ * 実験的なYubiKeyサポート: インポート済みの鍵での署名のみ
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-nl/help_about.md b/OpenKeychain/src/main/res/raw-nl/help_about.md
index 9d3693d33..5c0bc3327 100644
--- a/OpenKeychain/src/main/res/raw-nl/help_about.md
+++ b/OpenKeychain/src/main/res/raw-nl/help_about.md
@@ -6,41 +6,67 @@
Licentie: GPLv3+
-## Ontwikkelaars
+[//]: # (NOTE: Alphabetic ordering)
+
+## Hoofdontwikkelaars
* Dominik Schürmann (beheerder)
- * Art O Cathain
+ * Vincent Breitmoser
+
+## Topmedewerkers
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (ontwikkelaar van APG)
+ * Tim Bray
+
+## Occasionele medewerkers
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//]: # (NOTE: Alphabetic ordering)
## Bibliotheken
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 licentie)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT licentie)
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache licentie v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache licentie v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache licentie v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache licentie v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache licentie v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache licentie v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache licentie v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache licentie v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache licentie v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache licentie v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT licentie)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT licentie)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 licentie)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache licentie v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache licentie v2)
* [ZXing](https://github.com/zxing/zxing) (Apache licentie v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache licentie v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache licentie v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache licentie v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT licentie)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache licentie v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache licentie v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache licentie v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache licentie v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-nl/help_changelog.md b/OpenKeychain/src/main/res/raw-nl/help_changelog.md
index 28c008583..d378550c4 100644
--- a/OpenKeychain/src/main/res/raw-nl/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-nl/help_changelog.md
@@ -1,9 +1,29 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+## 3.5
+
+ * Sleutel intrekken bij verwijderen
+ * Verbeterde controles voor onveilige cryptografie
+ * Opgelost: Sluit OpenKeychain niet na eerste gebruik
+ * API: versie 8
+
+## 3.4
+
+ * Anoniem sleutels downloaden via Tor
+ * Proxy-ondersteuning
+ * Beter verwerken van YubiKey-fouten
+
+## 3.3
+
+ * Nieuw ontsleutelingsscherm
+ * Ontsleuteling van meerdere bestanden tegelijk
+ * Beter verwerken van YubiKey-fouten
+
## 3.2
+ * Eerste versie waarin YubiKey volledig wordt ondersteund vanuit de gebruikersinterface: sleutels aanmaken, YubiKey binden aan sleutels, ...
* Material design
- * Integratie van QR-scanner (nieuwe machtigingen vereist)
+ * Integratie van QR-code scannen (nieuwe permissies vereist)
* Sleutelaanmaakwizard verbeterd
* Probleem met ontbrekende contacten na synchronisatie opgelost
* Vereist Android 4
@@ -13,6 +33,7 @@
* Oplossing voor probleem waarbij sommige geldige sleutels weergegeven werden als ingetrokken of verlopen
* Aanvaard geen ondertekeningen door verlopen of ingetrokken subsleutels
* Ondersteuning voor Keybase.io in geavanceerde modus
+ * Methode om alle sleutels tegelijk bij te werken
## 3.1.2
@@ -35,7 +56,7 @@
* Nieuw design voor ontcijferingsscherm
* Nieuw icoon en kleuren
* Oplossing voor importeren van geheime sleutels van Symantec Encryption Desktop
- * Subsleutel-ID's op Yubikeys worden nu currect gecontroleerd
+ * Experimentele ondersteuning voor YubiKey: subsleutel-ID's worden nu correct gecontroleerd
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Volledige ondersteuning voor Yubikey ondertekeningsgeneratie en ontcijfering!
* Stel installeerbare compatibele apps voor in apps-lijst
* Nieuw design voor ontcijferingsschermen
* Veel oplossingen voor sleutelimporteren, lost ook gestripte sleutels op
@@ -55,12 +75,13 @@
* Oplossing voor gebruikers-ID-intrekkingscertificaten
* Nieuwe cloud search (zoekt op traditionele sleutelservers en keybase.io)
* Ondersteuning voor strippen van sleutels in OpenKeychain
+ * Experimentele ondersteuning voor YubiKey: ondersteuning voor aanmaken en ontsleutelen van ondertekeningen
## 2.9.2
* Oplossing voor gebroken sleutels in 2.9.1
- * Yubikey-ontsleuteling werkt nu via API
+ * Experimentele ondersteuning voor YubiKey: ontsleuteling werkt nu via API
## 2.9.1
@@ -69,7 +90,7 @@
* Oplossing voor sleutelvlaggen (ondersteunt nu Mailvelope 0.7 sleutels)
* Verbeterde behandeling van wachtwoorden
* Sleutels delen via SafeSlinger
- * Yubikey: optie om andere PINs toe te staan, momenteel werkt enkel ondertekenen via de OpenPGP API, niet in OpenKeychain zelf
+ * Experimentele ondersteuning voor YubiKey: instelling om andere PIN's toe te laten, momenteel werkt ondertekenen enkel via de OpenPGP API, niet binnen OpenKeychain
* Oplossing voor gestripte sleutels
* SHA256 als standaard voor compatibiliteit
* Intent API is veranderd, zie https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Oplossing voor crashes geïntroduceerd in v2.8
* Experimentele ondersteuning voor ECC
- * Experimentele ondersteuning voor Yubikey (alleen ondertekenen met geïmporteerde sleutels)
+ * Experimentele ondersteuning voor YubiKey: enkel ondertekenen met geïmporteerde sleutels
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-pl/help_about.md b/OpenKeychain/src/main/res/raw-pl/help_about.md
index 9d635d9df..7262dfefd 100644
--- a/OpenKeychain/src/main/res/raw-pl/help_about.md
+++ b/OpenKeychain/src/main/res/raw-pl/help_about.md
@@ -2,45 +2,71 @@
[http://www.openkeychain.org](http://www.openkeychain.org)
-[OpenKeychain](http://www.openkeychain.org) is an OpenPGP implementation for Android.
+[OpenKeychain](http://www.openkeychain.org) jest implementacją OpenPGP dla Androida.
-License: GPLv3+
+Licencja: GPLv3+
-## Developers
- * Dominik Schürmann (Maintainer)
- * Art O Cathain
+[//]: # (NOTE: Alphabetic ordering)
+
+## Główni deweloperzy
+ * Dominik Schürmann (Opiekun projektu)
+ * Vincent Breitmoser
+
+## Top Contributors
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG developer)
+ * Tim Bray
+
+## Occasional Contributors
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
-## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+[//]: # (NOTE: Alphabetic ordering)
+
+## Biblioteki
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
* [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-pl/help_certification.md b/OpenKeychain/src/main/res/raw-pl/help_certification.md
index 3518adf73..8031f7a87 100644
--- a/OpenKeychain/src/main/res/raw-pl/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-pl/help_certification.md
@@ -1,8 +1,8 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-## Key Confirmation
-Without confirmation, you cannot be sure if a key really corresponds to a specific person.
-The simplest way to confirm a key is by scanning the QR Code or exchanging it via NFC.
+## Potwiedzenie klucza
+Bez potwierdzenia nie masz pewności czy klucz odpowiada danej osobie.
+Najłatwiejszą drogą potwierdzenia klucza jest zeskanowanie kodu QR lub wysłanie go przez NFC
To confirm keys between more than two persons, we suggest to use the key exchange method available for your keys.
## Key Status
diff --git a/OpenKeychain/src/main/res/raw-pl/help_changelog.md b/OpenKeychain/src/main/res/raw-pl/help_changelog.md
index ab7fa8773..11d7975b1 100644
--- a/OpenKeychain/src/main/res/raw-pl/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-pl/help_changelog.md
@@ -1,9 +1,29 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+## 3.4
+
+ * Anonymous key download over Tor
+ * Proxy support
+ * Better YubiKey error handling
+
+## 3.3
+
+ * New decryption screen
+ * Decryption of multiple files at once
+ * Better handling of YubiKey errors
+
## 3.2
+ * First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
* Material design
- * Integration of QR Scanner (New permissions required)
+ * Integration of QR Code Scanning (New permissions required)
* Improved key creation wizard
* Fix missing contacts after sync
* Requires Android 4
@@ -13,6 +33,7 @@
* Fix: Some valid keys were shown revoked or expired
* Don't accept signatures by expired or revoked subkeys
* Keybase.io support in advanced view
+ * Method to update all keys at once
## 3.1.2
@@ -35,7 +56,7 @@
* Redesigned decrypt screen
* New icon usage and colors
* Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * Experimental YubiKey support: Subkey IDs are now checked correctly
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Full support for Yubikey signature generation and decryption!
* Propose installable compatible apps in apps list
* New design for decryption screens
* Many fixes for key import, also fixes stripped keys
@@ -55,12 +75,13 @@
* Fixing user id revocation certificates
* New cloud search (searches over traditional keyservers and keybase.io)
* Support for stripping keys inside OpenKeychain
+ * Experimental YubiKey support: Support for signature generation and decryption
## 2.9.2
* Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ * Experimental YubiKey support: Decryption now working via API
## 2.9.1
@@ -69,7 +90,7 @@
* Fix key flags handling (now supporting Mailvelope 0.7 keys)
* Improved passphrase handling
* Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
* Fix usage of stripped keys
* SHA256 as default for compatibility
* Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Fixing crashes introduced in v2.8
* Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
+ * Experimental YubiKey support: Only signing with imported keys
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-pt/help_certification.md b/OpenKeychain/src/main/res/raw-pt/help_certification.md
deleted file mode 100644
index 3518adf73..000000000
--- a/OpenKeychain/src/main/res/raw-pt/help_certification.md
+++ /dev/null
@@ -1,28 +0,0 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-
-## Key Confirmation
-Without confirmation, you cannot be sure if a key really corresponds to a specific person.
-The simplest way to confirm a key is by scanning the QR Code or exchanging it via NFC.
-To confirm keys between more than two persons, we suggest to use the key exchange method available for your keys.
-
-## Key Status
-
-<img src="status_signature_verified_cutout_24dp"/>
-Confirmed: You have already confirmed this key, e.g., by scanning the QR Code.
-<img src="status_signature_unverified_cutout_24dp"/>
-Unconfirmed: This key has not been confirmed yet. You cannot be sure if the key really corresponds to a specific person.
-<img src="status_signature_expired_cutout_24dp"/>
-Expired: This key is no longer valid. Only the owner can extend its validity.
-<img src="status_signature_revoked_cutout_24dp"/>
-Revoked: This key is no longer valid. It has been revoked by its owner.
-
-## Advanced Information
-A "key confirmation" in OpenKeychain is implemented by creating a certification according to the OpenPGP standard.
-This certification is a ["generic certification (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) described in the standard by:
-"The issuer of this certification does not make any particular assertion as to how well the certifier has checked that the owner of the key is in fact the person described by the User ID."
-
-Traditionally, certifications (also with higher certification levels, such as "positive certifications" (0x13)) are organized in OpenPGP's Web of Trust.
-Our model of key confirmation is a much simpler concept to avoid common usability problems related to this Web of Trust.
-We assume that keys are verified only to a certain degree that is still usable enough to be executed "on the go".
-We also do not implement (potentially transitive) trust signatures or an ownertrust database like in GnuPG.
-Furthermore, keys which contain at least one user ID certified by a trusted key will be marked as "confirmed" in the key listings. \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-pt/help_changelog.md b/OpenKeychain/src/main/res/raw-pt/help_changelog.md
deleted file mode 100644
index ab7fa8773..000000000
--- a/OpenKeychain/src/main/res/raw-pt/help_changelog.md
+++ /dev/null
@@ -1,269 +0,0 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-
-## 3.2
-
- * Material design
- * Integration of QR Scanner (New permissions required)
- * Improved key creation wizard
- * Fix missing contacts after sync
- * Requires Android 4
- * Redesigned key screen
- * Simplify crypto preferences, better selection of secure ciphers
- * API: Detached signatures, free selection of signing key,...
- * Fix: Some valid keys were shown revoked or expired
- * Don't accept signatures by expired or revoked subkeys
- * Keybase.io support in advanced view
-
-
-## 3.1.2
-
- * Fix key export to files (now for real)
-
-
-## 3.1.1
-
- * Fix key export to files (they were written partially)
- * Fix crash on Android 2.3
-
-
-## 3.1
-
- * Fix crash on Android 5
- * New certify screen
- * Secure Exchange directly from key list (SafeSlinger library)
- * New QR Code program flow
- * Redesigned decrypt screen
- * New icon usage and colors
- * Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
-
-
-## 3.0.1
-
- * Better handling of large key imports
- * Improved subkey selection
-
-
-## 3.0
-
- * Full support for Yubikey signature generation and decryption!
- * Propose installable compatible apps in apps list
- * New design for decryption screens
- * Many fixes for key import, also fixes stripped keys
- * Honor and display key authenticate flags
- * User interface to generate custom keys
- * Fixing user id revocation certificates
- * New cloud search (searches over traditional keyservers and keybase.io)
- * Support for stripping keys inside OpenKeychain
-
-
-## 2.9.2
-
- * Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
-
-
-## 2.9.1
-
- * Split encrypt screen into two
- * Fix key flags handling (now supporting Mailvelope 0.7 keys)
- * Improved passphrase handling
- * Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
- * Fix usage of stripped keys
- * SHA256 as default for compatibility
- * Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
- * OpenPGP API now handles revoked/expired keys and returns all user ids
-
-
-## 2.9
-
- * Fixing crashes introduced in v2.8
- * Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
-
-
-## 2.8
-
- * So many bugs have been fixed in this release that we focus on the main new features
- * Key edit: awesome new design, key revocation
- * Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
- * New first time screen
- * New key creation screen: autocompletion of name and email based on your personal Android accounts
- * File encryption: awesome new design, support for encrypting multiple files
- * New icons to show status of key (by Brennan Novak)
- * Important bug fix: Importing of large key collections from a file is now possible
- * Notification showing cached passphrases
- * Keys are connected to Android's contacts
-
-This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar
-
-## 2.7
-
- * Purple! (Dominik, Vincent)
- * New key view design (Dominik, Vincent)
- * New flat Android buttons (Dominik, Vincent)
- * API fixes (Dominik)
- * Keybase.io import (Tim Bray)
-
-
-## 2.6.1
-
- * Some fixes for regression bugs
-
-
-## 2.6
-
- * Key certifications (thanks to Vincent Breitmoser)
- * Support for GnuPG partial secret keys (thanks to Vincent Breitmoser)
- * New design for signature verification
- * Custom key length (thanks to Greg Witczak)
- * Fix share-functionality from other apps
-
-
-## 2.5
-
- * Fix decryption of symmetric OpenPGP messages/files
- * Refactored key edit screen (thanks to Ash Hughes)
- * New modern design for encrypt/decrypt screens
- * OpenPGP API version 3 (multiple api accounts, internal fixes, key lookup)
-
-
-## 2.4
-Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
-Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
-Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.
-
- * New unified key list
- * Colorized key fingerprint
- * Support for keyserver ports
- * Deactivate possibility to generate weak keys
- * Much more internal work on the API
- * Certify user ids
- * Keyserver query based on machine-readable output
- * Lock navigation drawer on tablets
- * Suggestions for emails on creation of keys
- * Search in public key lists
- * And much more improvements and fixes…
-
-
-## 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
- * New public key view
- * Bug fixes for importing of keys
- * Key cross-certification (thanks to Ash Hughes)
- * Handle UTF-8 passwords properly (thanks to Ash Hughes)
- * First version with new languages (thanks to the contributors on Transifex)
- * Sharing of keys via QR Codes fixed and improved
- * Package signature verification for API
-
-
-## 2.1.1
-
- * API Updates, preparation for K-9 Mail integration
-
-
-## 2.1
-
- * Lots of bug fixes
- * New API for developers
- * PRNG bug fix by Google
-
-
-## 2.0
-
- * Complete redesign
- * Share public keys via QR codes, NFC beam
- * Sign keys
- * Upload keys to server
- * Fixes import issues
- * New AIDL API
-
-
-## 1.0.8
-
- * Basic keyserver support
- * App2sd
- * More choices for passphrase cache: 1, 2, 4, 8, hours
- * Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
- * Bugfixes
- * Optimizations
-
-
-## 1.0.7
-
- * Fixed problem with signature verification of texts with trailing newline
- * More options for passphrase cache time to live (20, 40, 60 mins)
-
-
-## 1.0.6
-
- * Account adding crash on Froyo fixed
- * Secure file deletion
- * Option to delete key file after import
- * Stream encryption/decryption (gallery, etc.)
- * New options (language, force v3 signatures)
- * Interface changes
- * Bugfixes
-
-
-## 1.0.5
-
- * German and Italian translation
- * Much smaller package, due to reduced BC sources
- * New preferences GUI
- * Layout adjustment for localization
- * Signature bugfix
-
-
-## 1.0.4
-
- * Fixed another crash caused by some SDK bug with query builder
-
-
-## 1.0.3
-
- * Fixed crashes during encryption/signing and possibly key export
-
-
-## 1.0.2
-
- * Filterable key lists
- * Smarter pre-selection of encryption keys
- * New Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
- * Fixes and additional features (key preselection) for K-9 Mail, new beta build available
-
-
-## 1.0.1
-
- * GMail account listing was broken in 1.0.0, fixed again
-
-
-## 1.0.0
-
- * K-9 Mail integration, APG supporting beta build of K-9 Mail
- * Support of more file managers (including ASTRO)
- * Slovenian translation
- * New database, much faster, less memory usage
- * Defined Intents and content provider for other apps
- * Bugfixes \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-pt/help_start.md b/OpenKeychain/src/main/res/raw-pt/help_start.md
deleted file mode 100644
index 4cc331942..000000000
--- a/OpenKeychain/src/main/res/raw-pt/help_start.md
+++ /dev/null
@@ -1,16 +0,0 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-
-## How do I activate OpenKeychain in K-9 Mail?
-To use OpenKeychain with K-9 Mail, you want to follow these steps:
- 1. Open K-9 Mail and long-tap on the account you want to use OpenKeychain with.
- 2. Select "Account settings", scroll to the very bottom and click "Cryptography".
- 3. Click on "OpenPGP Provider" and select OpenKeychain from the list.
-
-## I found a bug in OpenKeychain!
-Please report the bug using the [issue tracker of OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
-
-## Contribute
-If you want to help us developing OpenKeychain by contributing code [follow our small guide on Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
-
-## Translations
-Help translating OpenKeychain! Everybody can participate at [OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-ro/help_about.md b/OpenKeychain/src/main/res/raw-ro/help_about.md
deleted file mode 100644
index 9d635d9df..000000000
--- a/OpenKeychain/src/main/res/raw-ro/help_about.md
+++ /dev/null
@@ -1,46 +0,0 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-
-[http://www.openkeychain.org](http://www.openkeychain.org)
-
-[OpenKeychain](http://www.openkeychain.org) is an OpenPGP implementation for Android.
-
-License: GPLv3+
-
-## Developers
- * Dominik Schürmann (Maintainer)
- * Art O Cathain
- * Ash Hughes
- * Brian C. Barnes
- * Bahtiar 'kalkin' Gadimov
- * Daniel Albert
- * Daniel Hammann
- * Daniel Haß
- * Greg Witczak
- * 'mar-v-in'
- * Markus Doits
- * Miroojin Bakshi
- * Nikhil Peter Raj
- * Paul Sarbinowski
- * 'Senecaso'
- * Signe Rüsch
- * Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
-
-## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
- * [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
- * [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
- * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
- * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
- * [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-ro/help_certification.md b/OpenKeychain/src/main/res/raw-ro/help_certification.md
deleted file mode 100644
index 3518adf73..000000000
--- a/OpenKeychain/src/main/res/raw-ro/help_certification.md
+++ /dev/null
@@ -1,28 +0,0 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-
-## Key Confirmation
-Without confirmation, you cannot be sure if a key really corresponds to a specific person.
-The simplest way to confirm a key is by scanning the QR Code or exchanging it via NFC.
-To confirm keys between more than two persons, we suggest to use the key exchange method available for your keys.
-
-## Key Status
-
-<img src="status_signature_verified_cutout_24dp"/>
-Confirmed: You have already confirmed this key, e.g., by scanning the QR Code.
-<img src="status_signature_unverified_cutout_24dp"/>
-Unconfirmed: This key has not been confirmed yet. You cannot be sure if the key really corresponds to a specific person.
-<img src="status_signature_expired_cutout_24dp"/>
-Expired: This key is no longer valid. Only the owner can extend its validity.
-<img src="status_signature_revoked_cutout_24dp"/>
-Revoked: This key is no longer valid. It has been revoked by its owner.
-
-## Advanced Information
-A "key confirmation" in OpenKeychain is implemented by creating a certification according to the OpenPGP standard.
-This certification is a ["generic certification (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) described in the standard by:
-"The issuer of this certification does not make any particular assertion as to how well the certifier has checked that the owner of the key is in fact the person described by the User ID."
-
-Traditionally, certifications (also with higher certification levels, such as "positive certifications" (0x13)) are organized in OpenPGP's Web of Trust.
-Our model of key confirmation is a much simpler concept to avoid common usability problems related to this Web of Trust.
-We assume that keys are verified only to a certain degree that is still usable enough to be executed "on the go".
-We also do not implement (potentially transitive) trust signatures or an ownertrust database like in GnuPG.
-Furthermore, keys which contain at least one user ID certified by a trusted key will be marked as "confirmed" in the key listings. \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-ro/help_start.md b/OpenKeychain/src/main/res/raw-ro/help_start.md
deleted file mode 100644
index 4cc331942..000000000
--- a/OpenKeychain/src/main/res/raw-ro/help_start.md
+++ /dev/null
@@ -1,16 +0,0 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-
-## How do I activate OpenKeychain in K-9 Mail?
-To use OpenKeychain with K-9 Mail, you want to follow these steps:
- 1. Open K-9 Mail and long-tap on the account you want to use OpenKeychain with.
- 2. Select "Account settings", scroll to the very bottom and click "Cryptography".
- 3. Click on "OpenPGP Provider" and select OpenKeychain from the list.
-
-## I found a bug in OpenKeychain!
-Please report the bug using the [issue tracker of OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
-
-## Contribute
-If you want to help us developing OpenKeychain by contributing code [follow our small guide on Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
-
-## Translations
-Help translating OpenKeychain! Everybody can participate at [OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-ru/help_about.md b/OpenKeychain/src/main/res/raw-ru/help_about.md
index 9b2e6e78a..1243a5d14 100644
--- a/OpenKeychain/src/main/res/raw-ru/help_about.md
+++ b/OpenKeychain/src/main/res/raw-ru/help_about.md
@@ -6,41 +6,67 @@
Лицензия: GPLv3+
-## Разработчики
+[//]: # (NOTE: Alphabetic ordering)
+
+## Ведущие разработчики
* Dominik Schürmann (Ведущий разработчик)
- * Art O Cathain
+ * Vincent Breitmoser
+
+## Top Contributors
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (разработчик APG)
+ * Tim Bray
+
+## Occasional Contributors
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//]: # (NOTE: Alphabetic ordering)
## Используемые библиотеки
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
* [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-ru/help_certification.md b/OpenKeychain/src/main/res/raw-ru/help_certification.md
index 61dfa67a5..787588b05 100644
--- a/OpenKeychain/src/main/res/raw-ru/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-ru/help_certification.md
@@ -2,8 +2,8 @@
## Подтверждение ключей
Без подтверждения Вы не можете быть уверены, что ключ принадлежит определенному человеку.
-Простейший способ подтвердить - отсканировать QR код или получить ключ через NFC.
-To confirm keys between more than two persons, we suggest to use the key exchange method available for your keys.
+Простейший способ подтвердить ключ — отсканировать QR-код или получить его через NFC.
+Для подтверждения ключей более чем двух человек, мы рекомендуем использовать один из доступных методов обмена ключами.
## Статус ключей
@@ -17,9 +17,9 @@ To confirm keys between more than two persons, we suggest to use the key exchang
Отозван: Этот ключ больше не действителен. Владелец ключа отозвал его.
## Подробная информация
-A "key confirmation" in OpenKeychain is implemented by creating a certification according to the OpenPGP standard.
-This certification is a ["generic certification (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) described in the standard by:
-"The issuer of this certification does not make any particular assertion as to how well the certifier has checked that the owner of the key is in fact the person described by the User ID."
+"Подтверждение ключей" в OpenKeychain реализовано методом сертификации, согласно стандарту OpenPGP.
+Эта сертификация представляет собой ["generic certification (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) , описанной в стандарте:
+"Издатель такой подписи (поручитель) никак не оговаривает, что провёл какую-то проверку ключа и его связь с лицом, чьё имя указано в сертификате."
Traditionally, certifications (also with higher certification levels, such as "positive certifications" (0x13)) are organized in OpenPGP's Web of Trust.
Our model of key confirmation is a much simpler concept to avoid common usability problems related to this Web of Trust.
diff --git a/OpenKeychain/src/main/res/raw-ru/help_changelog.md b/OpenKeychain/src/main/res/raw-ru/help_changelog.md
index ab7fa8773..11d7975b1 100644
--- a/OpenKeychain/src/main/res/raw-ru/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-ru/help_changelog.md
@@ -1,9 +1,29 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+## 3.4
+
+ * Anonymous key download over Tor
+ * Proxy support
+ * Better YubiKey error handling
+
+## 3.3
+
+ * New decryption screen
+ * Decryption of multiple files at once
+ * Better handling of YubiKey errors
+
## 3.2
+ * First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
* Material design
- * Integration of QR Scanner (New permissions required)
+ * Integration of QR Code Scanning (New permissions required)
* Improved key creation wizard
* Fix missing contacts after sync
* Requires Android 4
@@ -13,6 +33,7 @@
* Fix: Some valid keys were shown revoked or expired
* Don't accept signatures by expired or revoked subkeys
* Keybase.io support in advanced view
+ * Method to update all keys at once
## 3.1.2
@@ -35,7 +56,7 @@
* Redesigned decrypt screen
* New icon usage and colors
* Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * Experimental YubiKey support: Subkey IDs are now checked correctly
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Full support for Yubikey signature generation and decryption!
* Propose installable compatible apps in apps list
* New design for decryption screens
* Many fixes for key import, also fixes stripped keys
@@ -55,12 +75,13 @@
* Fixing user id revocation certificates
* New cloud search (searches over traditional keyservers and keybase.io)
* Support for stripping keys inside OpenKeychain
+ * Experimental YubiKey support: Support for signature generation and decryption
## 2.9.2
* Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ * Experimental YubiKey support: Decryption now working via API
## 2.9.1
@@ -69,7 +90,7 @@
* Fix key flags handling (now supporting Mailvelope 0.7 keys)
* Improved passphrase handling
* Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
* Fix usage of stripped keys
* SHA256 as default for compatibility
* Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Fixing crashes introduced in v2.8
* Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
+ * Experimental YubiKey support: Only signing with imported keys
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-ru/help_start.md b/OpenKeychain/src/main/res/raw-ru/help_start.md
index 4cc331942..d883f21ec 100644
--- a/OpenKeychain/src/main/res/raw-ru/help_start.md
+++ b/OpenKeychain/src/main/res/raw-ru/help_start.md
@@ -1,16 +1,16 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-## How do I activate OpenKeychain in K-9 Mail?
-To use OpenKeychain with K-9 Mail, you want to follow these steps:
- 1. Open K-9 Mail and long-tap on the account you want to use OpenKeychain with.
- 2. Select "Account settings", scroll to the very bottom and click "Cryptography".
- 3. Click on "OpenPGP Provider" and select OpenKeychain from the list.
+## Как мне активировать OpenKeychain в K-9 Mail?
+Для использования OpenKeychain с K-9 Mail, необходимо выполнить следующие шаги:
+ 1. Открыть K-9 Mail и долгим нажатием выбрать учетную запись с которой будет использоваться OpenKeychain.
+ 2. Выбрать "Настройки учетной записи" и опуститься в самый конец меню до пункта "Шифрование".
+ 3. Нажать на "OpenPGP Provider" и выбрать OpenKeychain из списка.
-## I found a bug in OpenKeychain!
-Please report the bug using the [issue tracker of OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
+## Я нашел ошибку в OpenKeychain!
+Пожалуйста, сообщайте обо всех проблемах и ошибках в разделе [Решение проблем OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
-## Contribute
-If you want to help us developing OpenKeychain by contributing code [follow our small guide on Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
+## Вклад в развитие
+Если Вы хотите помочь в разработке OpenKeychain, обратитесь к [инструкции на Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
-## Translations
-Help translating OpenKeychain! Everybody can participate at [OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
+## Перевод
+Помогите переводить OpenKeychain! Каждый может принять участие в переводе [OpenKeychain на Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-sl/help_about.md b/OpenKeychain/src/main/res/raw-sl/help_about.md
index dc333ca2d..3fe72beef 100644
--- a/OpenKeychain/src/main/res/raw-sl/help_about.md
+++ b/OpenKeychain/src/main/res/raw-sl/help_about.md
@@ -6,41 +6,67 @@
Licenca: GPLv3+
-## Razvijalci
+[//]: # (NOTE: Alphabetic ordering)
+
+## Main Developers
* Dominik Schürmann (Skrbnik)
- * Art O Cathain
+ * Vincent Breitmoser
+
+## Top Contributors
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG developer)
+ * Tim Bray
+
+## Occasional Contributors
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//]: # (NOTE: Alphabetic ordering)
## Knjižnice
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (Licenca MIT X11)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (Licenca MIT)
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Licenca Apache v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Licenca Apache v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Licenca Apache v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Licenca Apache v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Licenca Apache v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Licenca Apache v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (Licenca MIT)
+ * [Snackbar](https://github.com/nispok/snackbar) (Licenca MIT)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (Licenca MIT X11)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Licenca Apache v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Licenca Apache v2)
* [ZXing](https://github.com/zxing/zxing) (Licenca Apache v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Licenca Apache v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Licenca Apache v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Licenca Apache v2)
- * [Snackbar](https://github.com/nispok/snackbar) (Licenca MIT)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Licenca Apache v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Licenca Apache v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Licenca Apache v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Licenca Apache v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-sl/help_changelog.md b/OpenKeychain/src/main/res/raw-sl/help_changelog.md
index ab7fa8773..11d7975b1 100644
--- a/OpenKeychain/src/main/res/raw-sl/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-sl/help_changelog.md
@@ -1,9 +1,29 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+## 3.4
+
+ * Anonymous key download over Tor
+ * Proxy support
+ * Better YubiKey error handling
+
+## 3.3
+
+ * New decryption screen
+ * Decryption of multiple files at once
+ * Better handling of YubiKey errors
+
## 3.2
+ * First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
* Material design
- * Integration of QR Scanner (New permissions required)
+ * Integration of QR Code Scanning (New permissions required)
* Improved key creation wizard
* Fix missing contacts after sync
* Requires Android 4
@@ -13,6 +33,7 @@
* Fix: Some valid keys were shown revoked or expired
* Don't accept signatures by expired or revoked subkeys
* Keybase.io support in advanced view
+ * Method to update all keys at once
## 3.1.2
@@ -35,7 +56,7 @@
* Redesigned decrypt screen
* New icon usage and colors
* Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * Experimental YubiKey support: Subkey IDs are now checked correctly
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Full support for Yubikey signature generation and decryption!
* Propose installable compatible apps in apps list
* New design for decryption screens
* Many fixes for key import, also fixes stripped keys
@@ -55,12 +75,13 @@
* Fixing user id revocation certificates
* New cloud search (searches over traditional keyservers and keybase.io)
* Support for stripping keys inside OpenKeychain
+ * Experimental YubiKey support: Support for signature generation and decryption
## 2.9.2
* Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ * Experimental YubiKey support: Decryption now working via API
## 2.9.1
@@ -69,7 +90,7 @@
* Fix key flags handling (now supporting Mailvelope 0.7 keys)
* Improved passphrase handling
* Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
* Fix usage of stripped keys
* SHA256 as default for compatibility
* Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Fixing crashes introduced in v2.8
* Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
+ * Experimental YubiKey support: Only signing with imported keys
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-sr/help_about.md b/OpenKeychain/src/main/res/raw-sr/help_about.md
index 77a4904e5..5bfbbc6ff 100644
--- a/OpenKeychain/src/main/res/raw-sr/help_about.md
+++ b/OpenKeychain/src/main/res/raw-sr/help_about.md
@@ -6,41 +6,67 @@
Лиценца: ГПЛв3+
-## Програмери
- * Dominik Schürmann (главни програмер)
- * Art O Cathain
- * Ash Hughes
- * Brian C. Barnes
- * Bahtiar „kalkin“ Gadimov
- * Daniel Albert
- * Daniel Hammann
- * Daniel Haß
- * Greg Witczak
+[//]:
+
+## Главни програмери
+ * Доминик Ширман (Dominik Schürmann, одржавалац)
+ * Винсент Брајтмозер (Vincent Breitmoser)
+
+## Главни доприносиоци
+ * Adithya Abraham Philip
+ * Еш Хјуџис (Ash Hughes)
* „mar-v-in“
- * Markus Doits
- * Miroojin Bakshi
- * Nikhil Peter Raj
- * Paul Sarbinowski
+ * „Thialfihar“ (АПГ програмер)
+ * Tim Bray
+
+## Повремени доприносиоци
+ * Арт Окатаин (Art O Cathain)
+ * Брајан Ц. Барнс (Brian C. Barnes)
+ * Бахтјар „kalkin“ Гадимов (Bahtiar Gadimov)
+ * Данијел Алберт (Daniel Albert)
+ * Данијел Хаман (Daniel Hammann)
+ * Данијел Хас (Daniel Haß)
+ * Данијел Нелц (Daniel Nelz)
+ * Данијел Рамос (Daniel Ramos)
+ * Грег Вичак (Greg Witczak)
+ * „iseki“
+ * Ишан Кана (Ishan Khanna)
+ * „jellysheep“
+ * „Jesperbk“
+ * „jkolo“
+ * Џои Кастиљо (Joey Castillo)
+ * Каи Ђианг (Kai Jiang)
+ * Картик Арора (Kartik Arora)
+ * „Kent“
+ * „ligi“
+ * Лукас Зорић (Lukas Zorich)
+ * Маноџ Кана (Manoj Khanna)
+ * Маркус Доитс (Markus Doits)
+ * Мироџин Бакши (Miroojin Bakshi)
+ * Морган Гангвер (Morgan Gangwere)
+ * Никил Питер Раџ (Nikhil Peter Raj)
+ * Паул Сарбиновски (Paul Sarbinowski)
* „Senecaso“
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (АПГ 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * „steelman“
+
+[//]:
## Библиотеке
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (МИТ Икс11 лиценца)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (МИТ лиценца)
* [Андроидове библиотеке подршке](http://developer.android.com/tools/support-library/index.html) (Апачи лиценца в2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Апачи лиценца в2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Апачи лиценца в2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Апачи лиценца в2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Апачи лиценца в2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Апачи лиценца в2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Апачи лиценца в2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Апачи лиценца в2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Материјал дизајн) (Апачи лиценца в2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (МИТ лиценца)
+ * [Snackbar](https://github.com/nispok/snackbar) (МИТ лиценца)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (МИТ Икс11 лиценца)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Апачи лиценца в2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Апачи лиценца в2)
* [ZXing](https://github.com/zxing/zxing) (Апачи лиценца в2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Апачи лиценца в2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Материјал дизајн)</a> (Апачи лиценца в2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Апачи лиценца в2)
- * [Snackbar](https://github.com/nispok/snackbar) (МИТ лиценца)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Апачи лиценца в2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Апачи лиценца в2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Апачи лиценца в2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Апачи лиценца в2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-sr/help_certification.md b/OpenKeychain/src/main/res/raw-sr/help_certification.md
index 20b68cf9b..2456ee374 100644
--- a/OpenKeychain/src/main/res/raw-sr/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-sr/help_certification.md
@@ -18,11 +18,11 @@
## Напредни подаци
„Потврда кључа“ у Отвореном кључарнику се реализује прављењем сертификације по ОпенПГП стандарду.
-Ова сертификација је [„општа сертификација (0x10)“](http://tools.ietf.org/html/rfc4880#section-5.2.1) описана стандардом у:
-"The issuer of this certification does not make any particular assertion as to how well the certifier has checked that the owner of the key is in fact the person described by the User ID."
+Ова сертификација је [„општа сертификација (0x10)“](http://tools.ietf.org/html/rfc4880#section-5.2.1) описана у стандарду:
+„Издавач ове сертификације не наводи посебно са којом сигурношћу је сертификатор утврдио да је власник кључа у ствари особа описана корисничким ИД-ом.“
-Traditionally, certifications (also with higher certification levels, such as "positive certifications" (0x13)) are organized in OpenPGP's Web of Trust.
-Our model of key confirmation is a much simpler concept to avoid common usability problems related to this Web of Trust.
-We assume that keys are verified only to a certain degree that is still usable enough to be executed "on the go".
-We also do not implement (potentially transitive) trust signatures or an ownertrust database like in GnuPG.
-Furthermore, keys which contain at least one user ID certified by a trusted key will be marked as "confirmed" in the key listings. \ No newline at end of file
+Традиционално, сертификације (такође оне са вишим нивоима овере, као нпр. „позитивне сертификације“ (0x13)) су организоване у ОпенПГП-овом Вебу поверења.
+Наш модел потврде кључа је много једноставнији концепт да би се избегли уобичајени проблеми са коришћењем везаним за овај Веб поверења.
+Ми претпостављамо да су кључеви потврђени само до одређеног степена, довољно употребљивог за коришћење „у покрету“.
+Такође не имплементирамо (потенцијално прелазне) потписе поверења или базу података поузданих власника као у ГнуПГ-у.
+Штавише, кључеви који садрже бар један кориснички ИД потврђен поузданим кључем биће означен као „потврђен“ на списку кључева. \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-sr/help_changelog.md b/OpenKeychain/src/main/res/raw-sr/help_changelog.md
index e33c6b4e4..680664a6d 100644
--- a/OpenKeychain/src/main/res/raw-sr/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-sr/help_changelog.md
@@ -1,7 +1,27 @@
[//]: #
-## 3.2beta2
+## 3.5
+ * Опозив кључа при брисању кључа
+ * Побољшане провере за небезбедну криптографију
+ * Поправка: не затварај Отворени кључарник након успеха чаробњака за прво покретање
+ * АПИ: издање 8
+
+## 3.4
+
+ * Анонимна преузимања кључева преко Тора
+ * Подршка за прокси
+ * Боље руковање грешкама Јубикључа
+
+## 3.3
+
+ * Нови екран дешифровања
+ * Дешифровање више фајлова одједном
+ * Боље руковање грешкама Јубикључа
+
+## 3.2
+
+ * Прво издање са пуном подршком за Јубикључ преко корисничког сучеља: уређивање кључева, повезивање Јубикључева на кључеве,...
* Материјал дизајн
* Интеграција читача бар-кôда (захтева нове дозволе)
* Побољшан чаробњак прављења кључа
@@ -11,8 +31,9 @@
* Поједностављење поставки криптографије, бољи избор безбедних шифрара
* АПИ: одвојени потписи, слободан избор кључа за потпис,...
* Поправка: неки од важећих кључева су били приказивани као опозвани или истекли
- * Не прихаћај потписе од истеклих или опозваних кључева
+ * Не прихатај потписе од истеклих или опозваних кључева
* Keybase.io подршка у напредном приказу
+ * Метода ажурирања свих кључева одједном
## 3.1.2
@@ -35,7 +56,7 @@
* Редизајн екрана дешифровања
* Нова употреба икона и боја
* Поправка увоза тајних кључева са „Symantec Encryption Desktop“
- * ИД-ови поткључева на Јубикључу сада се исправно проверавају
+ * Експериментална подршка за Јубикључ: ИД-ови поткључа сада се исправно проверавају
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Пуна подршка за прављење и дешифровање Јубикључ потписа!
* Предлози за инсталабилне компатибилне апликације у списку апликација
* Нови дизајн за екране дешифровања
* Много поправки за увоз кључа, такође поправљени огољени кључеви
@@ -55,12 +75,13 @@
* Поправка корисничког ид-а сертификата опозива
* Нова клауд претрага (тражи преко традиционални сервера кључева и keybase.io)
* Подршка за огољивање кључева унутар Отвореног кључарника
+ * Експериментална подршка за Јубикључ: подршка за генерисање потписа и дешифровање
## 2.9.2
* Поправка кључева покварених у 2.9.1
- * Јубикључ дешифровање сада ради преко АПИ-ја
+ * Експериментална подршка за Јубикључ: дешифровање сада ради преко АПИ-ја
## 2.9.1
@@ -69,7 +90,7 @@
* Поправка руковања заставицама кључа (подршка за Mailvelope 0.7 кључеве)
* Побољшано руковање лозинкама
* Дељење кључа преко Сејфслингера (SafeSlinger)
- * Јубикључ: опција за дозволу осталих ПИН-ова, тренутно ради само потписивање преко ОпенПГП АПИ-ја, не унутар Отвореног кључарника
+ * Експериментална подршка за Јубикључ: поставка за дозволу осталих ПИНова, за сада само потписивање преко ОпенПГП АПИ-ја ради, не унутар Отвореног кључарника
* Поправка употребе огољених кључева
* СХА256 подразумевано због компатибилности
* Интент АПИ је измењен, погледајте https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Поправка рушења која су се појавила у в2.8
* Експериментална подршка за ЕЦЦ
- * Експериментална подршка за Јубикључ (Yubikey) (само пријава са увезеним кључевима)
+ * Експериментална подршка за Јубикључ: потписивање само увезеним кључевима
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-sv/help_about.md b/OpenKeychain/src/main/res/raw-sv/help_about.md
index 9d635d9df..0fd4713bb 100644
--- a/OpenKeychain/src/main/res/raw-sv/help_about.md
+++ b/OpenKeychain/src/main/res/raw-sv/help_about.md
@@ -1,46 +1,72 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTERING: Var vänlig och sätt varje mening på sin egen rad, Transifex sätter varje rad i sitt eget fält för översättningar!)
[http://www.openkeychain.org](http://www.openkeychain.org)
-[OpenKeychain](http://www.openkeychain.org) is an OpenPGP implementation for Android.
+[OpenKeychain](http://www.openkeychain.org) är en OpenPGP-implementation till Android.
-License: GPLv3+
+Licens: GPLv3+
-## Developers
- * Dominik Schürmann (Maintainer)
- * Art O Cathain
+[//]: # (NOTE: Alphabetic ordering)
+
+## Huvudsakliga utvecklare
+ * Dominik Schürmann (Projektledare)
+ * Vincent Breitmoser
+
+## Top Contributors
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG-utvecklare)
+ * Tim Bray
+
+## Occasional Contributors
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//]: # (NOTE: Alphabetic ordering)
-## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+## Biblioteken
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
+ * [SafeSlinger's bibliotek för utbyte](https://github.com/SafeSlingerProject/exchange-android) (MIT-licens)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11-licens)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
* [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-sv/help_certification.md b/OpenKeychain/src/main/res/raw-sv/help_certification.md
index 3518adf73..babb90ab0 100644
--- a/OpenKeychain/src/main/res/raw-sv/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-sv/help_certification.md
@@ -1,22 +1,22 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTERING: Var vänlig och sätt varje mening på sin egen rad, Transifex sätter varje rad i sitt eget fält för översättningar!)
-## Key Confirmation
-Without confirmation, you cannot be sure if a key really corresponds to a specific person.
-The simplest way to confirm a key is by scanning the QR Code or exchanging it via NFC.
-To confirm keys between more than two persons, we suggest to use the key exchange method available for your keys.
+## Nyckelbekräftelse
+Utan bekräftelse kan du inte vara säker på om en nyckel verkligen motsvarar en viss person.
+Det enklaste sättet att bekräfta en nyckel är genom att skanna QR-koden eller att byta ut den via NFC.
+För att bekräfta nycklar mellan fler än två personer så föreslår vi att du använder utbytesmetoden som är passande för dina nycklar.
-## Key Status
+## Status för nyckel
<img src="status_signature_verified_cutout_24dp"/>
-Confirmed: You have already confirmed this key, e.g., by scanning the QR Code.
+Bekräftat: Du har redan bekräftat den här nyckeln, t.ex genom att skanna QR-koden.
<img src="status_signature_unverified_cutout_24dp"/>
Unconfirmed: This key has not been confirmed yet. You cannot be sure if the key really corresponds to a specific person.
<img src="status_signature_expired_cutout_24dp"/>
-Expired: This key is no longer valid. Only the owner can extend its validity.
+Utgången: Den här nyckeln är inte längre giltig. Endast ägaren kan förlänga dess giltighet.
<img src="status_signature_revoked_cutout_24dp"/>
Revoked: This key is no longer valid. It has been revoked by its owner.
-## Advanced Information
+## Avancerad information
A "key confirmation" in OpenKeychain is implemented by creating a certification according to the OpenPGP standard.
This certification is a ["generic certification (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) described in the standard by:
"The issuer of this certification does not make any particular assertion as to how well the certifier has checked that the owner of the key is in fact the person described by the User ID."
diff --git a/OpenKeychain/src/main/res/raw-sv/help_changelog.md b/OpenKeychain/src/main/res/raw-sv/help_changelog.md
index ab7fa8773..2bec6c216 100644
--- a/OpenKeychain/src/main/res/raw-sv/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-sv/help_changelog.md
@@ -1,18 +1,39 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTERING: Var vänlig och sätt varje mening på sin egen rad, Transifex sätter varje rad i sitt eget fält för översättningar!)
+
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+## 3.4
+
+ * Anonymous key download over Tor
+ * Proxy support
+ * Better YubiKey error handling
+
+## 3.3
+
+ * New decryption screen
+ * Decryption of multiple files at once
+ * Better handling of YubiKey errors
## 3.2
- * Material design
- * Integration of QR Scanner (New permissions required)
- * Improved key creation wizard
- * Fix missing contacts after sync
- * Requires Android 4
+ * Första versionen med fullt YubiKey-stöd tillgängligt från användargränssnittet: Redigera nycklar. binda YubiKey till nycklar,...
+ * Materialkonstruktion
+ * Integrering av skanning av QR-kod (Nya behörigheter krävs)
+ * Förbättrade guiden för skapande av nyckel
+ * Fixa kontakter som saknas efter synkronisering
+ * Kräver Android 4
* Redesigned key screen
* Simplify crypto preferences, better selection of secure ciphers
* API: Detached signatures, free selection of signing key,...
* Fix: Some valid keys were shown revoked or expired
* Don't accept signatures by expired or revoked subkeys
- * Keybase.io support in advanced view
+ * Keybase.io-stöd i avancerad vy
+ * Metod för att uppdatera alla nycklar på en gång
## 3.1.2
@@ -23,19 +44,19 @@
## 3.1.1
* Fix key export to files (they were written partially)
- * Fix crash on Android 2.3
+ * Fixa krasch i Android 2.3
## 3.1
- * Fix crash on Android 5
- * New certify screen
+ * Fixa krasch i Android 5
+ Ny certifieringsskärm
* Secure Exchange directly from key list (SafeSlinger library)
* New QR Code program flow
* Redesigned decrypt screen
* New icon usage and colors
* Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * Experimental YubiKey support: Subkey IDs are now checked correctly
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Full support for Yubikey signature generation and decryption!
* Propose installable compatible apps in apps list
* New design for decryption screens
* Many fixes for key import, also fixes stripped keys
@@ -55,12 +75,13 @@
* Fixing user id revocation certificates
* New cloud search (searches over traditional keyservers and keybase.io)
* Support for stripping keys inside OpenKeychain
+ * Experimental YubiKey support: Support for signature generation and decryption
## 2.9.2
- * Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ Fixa trasiga nycklar i 2.9.1
+ * Experimental YubiKey support: Decryption now working via API
## 2.9.1
@@ -69,7 +90,7 @@
* Fix key flags handling (now supporting Mailvelope 0.7 keys)
* Improved passphrase handling
* Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
* Fix usage of stripped keys
* SHA256 as default for compatibility
* Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Fixing crashes introduced in v2.8
* Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
+ * Experimental YubiKey support: Only signing with imported keys
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-sv/help_start.md b/OpenKeychain/src/main/res/raw-sv/help_start.md
index 4cc331942..be8cc64b5 100644
--- a/OpenKeychain/src/main/res/raw-sv/help_start.md
+++ b/OpenKeychain/src/main/res/raw-sv/help_start.md
@@ -1,16 +1,16 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (NOTERING: Var vänlig och sätt varje mening på sin egen rad, Transifex sätter varje rad i sitt eget fält för översättningar!)
-## How do I activate OpenKeychain in K-9 Mail?
-To use OpenKeychain with K-9 Mail, you want to follow these steps:
+## Hur aktiverar jag OpenKeychain i K-9 Mail?
+För att använda OpenKeychain med K-9 Mail så ska du följa dessa steg:
1. Open K-9 Mail and long-tap on the account you want to use OpenKeychain with.
- 2. Select "Account settings", scroll to the very bottom and click "Cryptography".
- 3. Click on "OpenPGP Provider" and select OpenKeychain from the list.
+ 2. Välj "Konto-inställningar", skrolla längst ner och klicka på "Kryptering".
+ 3. Klicka på "OpenPGP-leverantör" och välj OpenKeyChain från listan.
-## I found a bug in OpenKeychain!
-Please report the bug using the [issue tracker of OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
+## Jag hittade en bugg i OpenKeychain!
+Vänligen rapportera buggen genom att använda [problemspåraren för OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
-## Contribute
-If you want to help us developing OpenKeychain by contributing code [follow our small guide on Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
+## Bidra
+Om du vill hjälpa oss att utveckla OpenKeychain genom att bidra med kod [följ vår lilla guide på Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
-## Translations
+## Översättningar
Help translating OpenKeychain! Everybody can participate at [OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-tr/help_about.md b/OpenKeychain/src/main/res/raw-tr/help_about.md
index 9d635d9df..06bed0021 100644
--- a/OpenKeychain/src/main/res/raw-tr/help_about.md
+++ b/OpenKeychain/src/main/res/raw-tr/help_about.md
@@ -6,41 +6,67 @@
License: GPLv3+
-## Developers
+[//]: # (NOTE: Alphabetic ordering)
+
+## Main Developers
* Dominik Schürmann (Maintainer)
- * Art O Cathain
+ * Vincent Breitmoser
+
+## Top Contributors
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG developer)
+ * Tim Bray
+
+## Occasional Contributors
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//]: # (NOTE: Alphabetic ordering)
## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
* [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-tr/help_changelog.md b/OpenKeychain/src/main/res/raw-tr/help_changelog.md
index ab7fa8773..11d7975b1 100644
--- a/OpenKeychain/src/main/res/raw-tr/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-tr/help_changelog.md
@@ -1,9 +1,29 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+## 3.4
+
+ * Anonymous key download over Tor
+ * Proxy support
+ * Better YubiKey error handling
+
+## 3.3
+
+ * New decryption screen
+ * Decryption of multiple files at once
+ * Better handling of YubiKey errors
+
## 3.2
+ * First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
* Material design
- * Integration of QR Scanner (New permissions required)
+ * Integration of QR Code Scanning (New permissions required)
* Improved key creation wizard
* Fix missing contacts after sync
* Requires Android 4
@@ -13,6 +33,7 @@
* Fix: Some valid keys were shown revoked or expired
* Don't accept signatures by expired or revoked subkeys
* Keybase.io support in advanced view
+ * Method to update all keys at once
## 3.1.2
@@ -35,7 +56,7 @@
* Redesigned decrypt screen
* New icon usage and colors
* Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * Experimental YubiKey support: Subkey IDs are now checked correctly
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Full support for Yubikey signature generation and decryption!
* Propose installable compatible apps in apps list
* New design for decryption screens
* Many fixes for key import, also fixes stripped keys
@@ -55,12 +75,13 @@
* Fixing user id revocation certificates
* New cloud search (searches over traditional keyservers and keybase.io)
* Support for stripping keys inside OpenKeychain
+ * Experimental YubiKey support: Support for signature generation and decryption
## 2.9.2
* Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ * Experimental YubiKey support: Decryption now working via API
## 2.9.1
@@ -69,7 +90,7 @@
* Fix key flags handling (now supporting Mailvelope 0.7 keys)
* Improved passphrase handling
* Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
* Fix usage of stripped keys
* SHA256 as default for compatibility
* Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Fixing crashes introduced in v2.8
* Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
+ * Experimental YubiKey support: Only signing with imported keys
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-uk/help_about.md b/OpenKeychain/src/main/res/raw-uk/help_about.md
index 9d635d9df..06bed0021 100644
--- a/OpenKeychain/src/main/res/raw-uk/help_about.md
+++ b/OpenKeychain/src/main/res/raw-uk/help_about.md
@@ -6,41 +6,67 @@
License: GPLv3+
-## Developers
+[//]: # (NOTE: Alphabetic ordering)
+
+## Main Developers
* Dominik Schürmann (Maintainer)
- * Art O Cathain
+ * Vincent Breitmoser
+
+## Top Contributors
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG developer)
+ * Tim Bray
+
+## Occasional Contributors
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//]: # (NOTE: Alphabetic ordering)
## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
* [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-uk/help_changelog.md b/OpenKeychain/src/main/res/raw-uk/help_changelog.md
index ab7fa8773..11d7975b1 100644
--- a/OpenKeychain/src/main/res/raw-uk/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-uk/help_changelog.md
@@ -1,9 +1,29 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+## 3.4
+
+ * Anonymous key download over Tor
+ * Proxy support
+ * Better YubiKey error handling
+
+## 3.3
+
+ * New decryption screen
+ * Decryption of multiple files at once
+ * Better handling of YubiKey errors
+
## 3.2
+ * First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
* Material design
- * Integration of QR Scanner (New permissions required)
+ * Integration of QR Code Scanning (New permissions required)
* Improved key creation wizard
* Fix missing contacts after sync
* Requires Android 4
@@ -13,6 +33,7 @@
* Fix: Some valid keys were shown revoked or expired
* Don't accept signatures by expired or revoked subkeys
* Keybase.io support in advanced view
+ * Method to update all keys at once
## 3.1.2
@@ -35,7 +56,7 @@
* Redesigned decrypt screen
* New icon usage and colors
* Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * Experimental YubiKey support: Subkey IDs are now checked correctly
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Full support for Yubikey signature generation and decryption!
* Propose installable compatible apps in apps list
* New design for decryption screens
* Many fixes for key import, also fixes stripped keys
@@ -55,12 +75,13 @@
* Fixing user id revocation certificates
* New cloud search (searches over traditional keyservers and keybase.io)
* Support for stripping keys inside OpenKeychain
+ * Experimental YubiKey support: Support for signature generation and decryption
## 2.9.2
* Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ * Experimental YubiKey support: Decryption now working via API
## 2.9.1
@@ -69,7 +90,7 @@
* Fix key flags handling (now supporting Mailvelope 0.7 keys)
* Improved passphrase handling
* Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
* Fix usage of stripped keys
* SHA256 as default for compatibility
* Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Fixing crashes introduced in v2.8
* Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
+ * Experimental YubiKey support: Only signing with imported keys
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-zh-rTW/help_about.md b/OpenKeychain/src/main/res/raw-zh-rTW/help_about.md
index 95fca0cec..ed32e0d5d 100644
--- a/OpenKeychain/src/main/res/raw-zh-rTW/help_about.md
+++ b/OpenKeychain/src/main/res/raw-zh-rTW/help_about.md
@@ -2,45 +2,71 @@
[http://www.openkeychain.org](http://www.openkeychain.org)
-<a href="http://www.openkeychain.org">OpenKeychain</a>是一個Android的OpenPGP應用。
+[OpenKeychain](http://www.openkeychain.org)是一個Android的OpenPGP應用。
-授權: GPLv3+
+授權:GPLv3+
-## Developers
- * Dominik Schürmann (Maintainer)
- * Art O Cathain
+[//]: # (備註: 字母順序排序)
+
+##主要開發者
+ * Dominik Schürmann (維護人)
+ * Vincent Breitmoser
+
+主要開發者
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG開發者)
+ * Tim Bray
+
+##不固定的貢獻者
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
-## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+[//]: # (備註: 字母順序排序)
+
+##函式庫
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
* [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-zh-rTW/help_certification.md b/OpenKeychain/src/main/res/raw-zh-rTW/help_certification.md
index a69349fa2..2055f2e36 100644
--- a/OpenKeychain/src/main/res/raw-zh-rTW/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-zh-rTW/help_certification.md
@@ -1,28 +1,28 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-金鑰認證
-這個身分識別尚未經過認證,你不能確認這個身分識別是否屬於真的某個人。
-最簡單確認金鑰的方式就是透過掃描QR code或是經由NFC交換。
-To confirm keys between more than two persons, we suggest to use the key exchange method available for your keys.
+##金鑰確認
+在經過確認之前,你無法確定這個金鑰是否真實屬於某個人。
+確認金鑰的最簡單方式就是透過掃描QR code或是經由NFC交換。
+要在多於兩個人之間確認彼此的金鑰,我們建議使用金鑰交換方式。
## 金鑰狀態
<img src="status_signature_verified_cutout_24dp"/>
-已認證: 你已經認證了這個金鑰,例如透過掃描QR Code。
+已確認:你已經確認了這個金鑰,例如透過掃描QR Code。
<img src="status_signature_unverified_cutout_24dp"/>
-未確認:這個身分識別尚未經過認證,你不能確認這個身分識別是否屬於真的某個人。
+未確認:這個金鑰尚未經過確認,你無法確定這個金鑰是否真實屬於某個人。
<img src="status_signature_expired_cutout_24dp"/>
-已過期:這個金鑰因超過有效期限而失效。只有金鑰擁有者可以改變有效期限。
+已過期:這個金鑰不再有效。只有金鑰擁有者可以展延有效期限。
<img src="status_signature_revoked_cutout_24dp"/>
已撤銷:這個金鑰已經被擁有者撤銷而失效。
-## Advanced Information
-在OpenKeychain中,透過根據標準OpenPGP所建立的證書可以簡單的認證一個金鑰。
-This certification is a ["generic certification (0x10)"](http://tools.ietf.org/html/rfc4880#section-5.2.1) described in the standard by:
-"The issuer of this certification does not make any particular assertion as to how well the certifier has checked that the owner of the key is in fact the person described by the User ID."
+##進階資訊
+在OpenKeychain中,確認金鑰是透過根據OpenPGP標準簽發認證來達成。
+這是一個“[一般認證 (0x10)](http://tools.ietf.org/html/rfc4880#section-5.2.1)”,規範描述如下:
+“認證的發行者不就妥善檢查金鑰持有人與金鑰所示身份相符與否一事做出任何允諾。”
-Traditionally, certifications (also with higher certification levels, such as "positive certifications" (0x13)) are organized in OpenPGP's Web of Trust.
-Our model of key confirmation is a much simpler concept to avoid common usability problems related to this Web of Trust.
-We assume that keys are verified only to a certain degree that is still usable enough to be executed "on the go".
-We also do not implement (potentially transitive) trust signatures or an ownertrust database like in GnuPG.
-Furthermore, keys which contain at least one user ID certified by a trusted key will be marked as "confirmed" in the key listings. \ No newline at end of file
+傳統上,包含更高等級的(像是主動型認證(0x13))認證在內,認證是有組織的存在於OpenPGP信任網當中。
+我們的金鑰確認模型採用一套相對簡單的概念,以避開普遍存在的信任網相關可用性問題。
+我們假設金鑰只驗證到足以隨時隨地使用的程度。
+同時我們暫時也不打算導入像是GnuPG的信任簽章或主觀信任資料庫。
+此外,如果某金鑰含有至少一個被信任金鑰所認證的身分時,這把金鑰將被標記為已確認。 \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-zh-rTW/help_changelog.md b/OpenKeychain/src/main/res/raw-zh-rTW/help_changelog.md
index ab7fa8773..20f2ef6c5 100644
--- a/OpenKeychain/src/main/res/raw-zh-rTW/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-zh-rTW/help_changelog.md
@@ -1,75 +1,96 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+## 3.5
+
+ *可以在刪除金鑰時撤銷金鑰
+ *改進對加密安全性進行檢查
+ *修正:在OpenKeychain第一次執行精靈成功後不自動關閉程式
+ * API: Version 8
+
+## 3.4
+
+ * Anonymous key download over Tor
+ *支援代理伺服器
+ * Better YubiKey error handling
+
+## 3.3
+
+ *新的解密畫面
+ *可同時解密多個檔案
+ * Better handling of YubiKey errors
+
## 3.2
- * Material design
- * Integration of QR Scanner (New permissions required)
- * Improved key creation wizard
- * Fix missing contacts after sync
- * Requires Android 4
- * Redesigned key screen
- * Simplify crypto preferences, better selection of secure ciphers
- * API: Detached signatures, free selection of signing key,...
- * Fix: Some valid keys were shown revoked or expired
- * Don't accept signatures by expired or revoked subkeys
- * Keybase.io support in advanced view
+ * 首次完整支援Yubikey:修改金鑰、連結Yubikey與金鑰、...
+ * 素材設計
+ * 整合QR條碼掃描 (需要新權限)
+ * 改進的金鑰建立精靈
+ * 修正同步後遺失聯絡人
+ * 最低要求 Android 4
+ * 重新設計的金鑰畫面
+ * 簡化的加密參數,更佳的安全加密算法選項
+ * API:分離的簽名、自由選擇用來簽名的金鑰、...
+ * 修正:部分有效的金鑰被顯示為已撤銷或過期
+ * 不接受來自已撤銷或過期子金鑰的簽名
+ * 進階檢視中的Keybase.io支援
+ * 可以一次更新所有的金鑰了
## 3.1.2
- * Fix key export to files (now for real)
+ * 修正匯出金鑰到檔案的功能(這次是真的了)
## 3.1.1
- * Fix key export to files (they were written partially)
- * Fix crash on Android 2.3
+ * 修正匯出金鑰到檔案功能(一部分)
+ * 修正在Android 2.3上的崩潰
## 3.1
- * Fix crash on Android 5
- * New certify screen
- * Secure Exchange directly from key list (SafeSlinger library)
- * New QR Code program flow
- * Redesigned decrypt screen
- * New icon usage and colors
- * Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * 修正在Android 5上的崩潰
+ * 新的認證畫面
+ * 直接在金鑰清單進行安全金鑰交換(使用SafeSlinger)
+ * 新的QR條碼作業流程
+ * 重新設計的解密畫面
+ * 新的圖示和配色
+ * 修正從Symantec Encryption Desktop匯入密鑰的問題
+ * 實驗性的YubiKey支援:子金鑰ID現在可以正確的檢查了
## 3.0.1
- * Better handling of large key imports
- * Improved subkey selection
+ * 更好的大型金鑰匯入處理
+ * 改良的子金鑰選取
## 3.0
- * Full support for Yubikey signature generation and decryption!
- * Propose installable compatible apps in apps list
- * New design for decryption screens
- * Many fixes for key import, also fixes stripped keys
+ * 在應用列表中建議相容的應用
+ * 新設計的解密畫面
+ * 許多匯入金鑰的修正,並修正剝離的金鑰
* Honor and display key authenticate flags
- * User interface to generate custom keys
- * Fixing user id revocation certificates
- * New cloud search (searches over traditional keyservers and keybase.io)
- * Support for stripping keys inside OpenKeychain
+ * 產生自訂金鑰的介面
+ * 修復使用者身分撤銷
+ * 新的雲端檢索功能(同時搜尋傳統的金鑰伺服器以及keybase.io資料庫)
+ * 現在OpenKeychain可以支援剝離的金鑰了
+ * 實驗性的YubiKey支援:支援簽名和解密
## 2.9.2
- * Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ * 修正2.9.1導致金鑰破損的問題
+ * 實驗性的YubiKey支援:現在可以透過API進行解密
## 2.9.1
- * Split encrypt screen into two
+ * 加密畫面現在一分為二
* Fix key flags handling (now supporting Mailvelope 0.7 keys)
* Improved passphrase handling
* Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
* Fix usage of stripped keys
* SHA256 as default for compatibility
* Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Fixing crashes introduced in v2.8
* Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
+ * Experimental YubiKey support: Only signing with imported keys
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-zh-rTW/help_start.md b/OpenKeychain/src/main/res/raw-zh-rTW/help_start.md
index 4cc331942..a27d029ad 100644
--- a/OpenKeychain/src/main/res/raw-zh-rTW/help_start.md
+++ b/OpenKeychain/src/main/res/raw-zh-rTW/help_start.md
@@ -1,16 +1,16 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
-## How do I activate OpenKeychain in K-9 Mail?
-To use OpenKeychain with K-9 Mail, you want to follow these steps:
- 1. Open K-9 Mail and long-tap on the account you want to use OpenKeychain with.
- 2. Select "Account settings", scroll to the very bottom and click "Cryptography".
- 3. Click on "OpenPGP Provider" and select OpenKeychain from the list.
+##我要如何在K-9 Mail啟用OpenKeychain?
+要在K-9 Mail使用OpenKeychain,請依下列指示操作:
+ 1. 打開K-9 Mail,長按要設置OpenKeychain的帳號。
+ 2. 選擇“帳戶設定”,捲動至最下方並選擇“加密”。
+ 3. 選擇“OpenPGP提供者”,並從清單中選取OpenKeychain。
-## I found a bug in OpenKeychain!
-Please report the bug using the [issue tracker of OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
+##我在OpenKeychain發現問題!
+請通過[OpenKeychain問題追蹤](https://github.com/openpgp-keychain/openpgp-keychain/issues)回報問題。
-## Contribute
-If you want to help us developing OpenKeychain by contributing code [follow our small guide on Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
+##發佈
+如果你願意發佈原始碼協助我們開發,請參考我們Github上的[發佈指南](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code)。
-## Translations
-Help translating OpenKeychain! Everybody can participate at [OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
+##翻譯
+請協助翻譯OpenKeychain!每個人都可以在[OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/)貢獻一己之力。 \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-zh/help_about.md b/OpenKeychain/src/main/res/raw-zh/help_about.md
index 9d635d9df..d8bc20016 100644
--- a/OpenKeychain/src/main/res/raw-zh/help_about.md
+++ b/OpenKeychain/src/main/res/raw-zh/help_about.md
@@ -1,46 +1,72 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (注意: 请把每个句子放在其本行中, Transifex把每一行放在它自己的位置!)
[http://www.openkeychain.org](http://www.openkeychain.org)
-[OpenKeychain](http://www.openkeychain.org) is an OpenPGP implementation for Android.
+[OpenKeychain](http://www.openkeychain.org) 是安卓上的一个 OpenPGP 协议实现。
-License: GPLv3+
+许可协议:GPLv3+
-## Developers
- * Dominik Schürmann (Maintainer)
- * Art O Cathain
+[//]: # (NOTE: Alphabetic ordering)
+
+## 主要开发者
+ * Dominik Schürmann (维护者)
+ * Vincent Breitmoser
+
+## Top Contributors
+ * Adithya Abraham Philip
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG 开发者)
+ * Tim Bray
+
+## Occasional Contributors
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
* Daniel Hammann
* Daniel Haß
+ * Daniel Nelz
+ * Daniel Ramos
* Greg Witczak
- * 'mar-v-in'
+ * 'iseki'
+ * Ishan Khanna
+ * 'jellysheep'
+ * 'Jesperbk'
+ * 'jkolo'
+ * Joey Castillo
+ * Kai Jiang
+ * Kartik Arora
+ * 'Kent'
+ * 'ligi'
+ * Lukas Zorich
+ * Manoj Khanna
* Markus Doits
* Miroojin Bakshi
+ * Morgan Gangwere
* Nikhil Peter Raj
* Paul Sarbinowski
* 'Senecaso'
* Signe Rüsch
* Sreeram Boyapati
- * Thialfihar (APG 1.x)
- * Tim Bray
- * Vincent Breitmoser
+ * 'steelman'
+
+[//]: # (NOTE: Alphabetic ordering)
-## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
- * [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
- * [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
- * [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
- * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
- * [ZXing](https://github.com/zxing/zxing) (Apache License v2)
- * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialNavigationDrawer](https://github.com/neokree/MaterialNavigationDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/dschuermann/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2) \ No newline at end of file
+## 库
+ [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache 许可证 v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache 许可证 v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache 许可证 v2)
+ * [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache 许可证 v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache 许可证 v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache 许可证 v2)
+ * [MiniDNS](https://github.com/rtreffer/minidns) (Apache 许可证 v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache 许可证 v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT 许可证)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT 许可证)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 许可证)
+ * [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache 许可证 v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache 许可证 v2)
+ * [ZXing](https://github.com/zxing/zxing) (Apache 许可证 v2)
+ * [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache 许可证 v2) \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw-zh/help_certification.md b/OpenKeychain/src/main/res/raw-zh/help_certification.md
index 3518adf73..7e66b1dd7 100644
--- a/OpenKeychain/src/main/res/raw-zh/help_certification.md
+++ b/OpenKeychain/src/main/res/raw-zh/help_certification.md
@@ -1,18 +1,18 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (注意: 请把每个句子放在其本行中, Transifex把每一行放在它自己的位置!)
-## Key Confirmation
-Without confirmation, you cannot be sure if a key really corresponds to a specific person.
-The simplest way to confirm a key is by scanning the QR Code or exchanging it via NFC.
-To confirm keys between more than two persons, we suggest to use the key exchange method available for your keys.
+## 密钥确认
+在没有进行确认之前,你无法确保一个密钥与特定的人的密钥是相符的
+确认密钥相符的最简单方式是扫描二维码或者通过NFC交换
+为了确认多于2个人的密钥是相符的, 我们建议使用密钥交换的方法进行确认.
-## Key Status
+## 密钥状态
<img src="status_signature_verified_cutout_24dp"/>
-Confirmed: You have already confirmed this key, e.g., by scanning the QR Code.
+已确认: 你已经通过例如二维码扫描这种方式确认了这个密钥
<img src="status_signature_unverified_cutout_24dp"/>
-Unconfirmed: This key has not been confirmed yet. You cannot be sure if the key really corresponds to a specific person.
+未确认: 这个密钥尚未被确认. 你无法确保这个密钥与指定的人的密钥是相同的.
<img src="status_signature_expired_cutout_24dp"/>
-Expired: This key is no longer valid. Only the owner can extend its validity.
+已过期: 这个密钥不再有效. 只有它的拥有者能扩展它的正确性.
<img src="status_signature_revoked_cutout_24dp"/>
Revoked: This key is no longer valid. It has been revoked by its owner.
diff --git a/OpenKeychain/src/main/res/raw-zh/help_changelog.md b/OpenKeychain/src/main/res/raw-zh/help_changelog.md
index ab7fa8773..11d7975b1 100644
--- a/OpenKeychain/src/main/res/raw-zh/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw-zh/help_changelog.md
@@ -1,9 +1,29 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+## 3.4
+
+ * Anonymous key download over Tor
+ * Proxy support
+ * Better YubiKey error handling
+
+## 3.3
+
+ * New decryption screen
+ * Decryption of multiple files at once
+ * Better handling of YubiKey errors
+
## 3.2
+ * First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
* Material design
- * Integration of QR Scanner (New permissions required)
+ * Integration of QR Code Scanning (New permissions required)
* Improved key creation wizard
* Fix missing contacts after sync
* Requires Android 4
@@ -13,6 +33,7 @@
* Fix: Some valid keys were shown revoked or expired
* Don't accept signatures by expired or revoked subkeys
* Keybase.io support in advanced view
+ * Method to update all keys at once
## 3.1.2
@@ -35,7 +56,7 @@
* Redesigned decrypt screen
* New icon usage and colors
* Fix import of secret keys from Symantec Encryption Desktop
- * Subkey IDs on Yubikeys are now checked correctly
+ * Experimental YubiKey support: Subkey IDs are now checked correctly
## 3.0.1
@@ -46,7 +67,6 @@
## 3.0
- * Full support for Yubikey signature generation and decryption!
* Propose installable compatible apps in apps list
* New design for decryption screens
* Many fixes for key import, also fixes stripped keys
@@ -55,12 +75,13 @@
* Fixing user id revocation certificates
* New cloud search (searches over traditional keyservers and keybase.io)
* Support for stripping keys inside OpenKeychain
+ * Experimental YubiKey support: Support for signature generation and decryption
## 2.9.2
* Fix keys broken in 2.9.1
- * Yubikey decryption now working via API
+ * Experimental YubiKey support: Decryption now working via API
## 2.9.1
@@ -69,7 +90,7 @@
* Fix key flags handling (now supporting Mailvelope 0.7 keys)
* Improved passphrase handling
* Key sharing via SafeSlinger
- * Yubikey: preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
+ * Experimental YubiKey support: Preference to allow other PINs, currently only signing via the OpenPGP API works, not inside of OpenKeychain
* Fix usage of stripped keys
* SHA256 as default for compatibility
* Intent API has changed, see https://github.com/open-keychain/open-keychain/wiki/Intent-API
@@ -80,7 +101,7 @@
* Fixing crashes introduced in v2.8
* Experimental ECC support
- * Experimental Yubikey support (signing-only with imported keys)
+ * Experimental YubiKey support: Only signing with imported keys
## 2.8
diff --git a/OpenKeychain/src/main/res/raw-zh/help_start.md b/OpenKeychain/src/main/res/raw-zh/help_start.md
index 4cc331942..183c63150 100644
--- a/OpenKeychain/src/main/res/raw-zh/help_start.md
+++ b/OpenKeychain/src/main/res/raw-zh/help_start.md
@@ -1,16 +1,16 @@
-[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+[//]: # (注意: 请把每个句子放在其本行中, Transifex把每一行放在它自己的位置!)
-## How do I activate OpenKeychain in K-9 Mail?
-To use OpenKeychain with K-9 Mail, you want to follow these steps:
- 1. Open K-9 Mail and long-tap on the account you want to use OpenKeychain with.
- 2. Select "Account settings", scroll to the very bottom and click "Cryptography".
- 3. Click on "OpenPGP Provider" and select OpenKeychain from the list.
+## 我怎样才能在 K-9 Mail 中激活 OpenKeychain?
+在K-9 Mail中使用OpenKeychain,请遵循以下步骤:
+ 1.打开K-9 Mail并长按你想要使用OpenKeychain的账户。
+ 2.选择“账户设置”,滑到最下方,点击“加密”。
+ 3.点击“OpenPGP提供者”并在列表中选择OpenKeychain。
-## I found a bug in OpenKeychain!
-Please report the bug using the [issue tracker of OpenKeychain](https://github.com/openpgp-keychain/openpgp-keychain/issues).
+## 我发现了OpenKeychain的问题!
+请使用 [OpenKeychain问题跟踪系统](https://github.com/openpgp-keychain/openpgp-keychain/issues)提交问题。
-## Contribute
-If you want to help us developing OpenKeychain by contributing code [follow our small guide on Github](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
+## 贡献
+如果你想要通过贡献代码来帮助开发OpenKeychain [请按照我们在Github上的一个小指引](https://github.com/openpgp-keychain/openpgp-keychain#contribute-code).
-## Translations
-Help translating OpenKeychain! Everybody can participate at [OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
+## 翻译
+帮助翻译OpenKeychain!每个人都可以参与。[OpenKeychain on Transifex](https://www.transifex.com/projects/p/open-keychain/). \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/raw/help_about.md b/OpenKeychain/src/main/res/raw/help_about.md
index 65a04e56e..45d326daa 100644
--- a/OpenKeychain/src/main/res/raw/help_about.md
+++ b/OpenKeychain/src/main/res/raw/help_about.md
@@ -6,14 +6,21 @@
License: GPLv3+
+[//]: # (NOTE: Alphabetic ordering)
+
## Main Developers
* Dominik Schürmann (Maintainer)
* Vincent Breitmoser
-## Contributors
+## Top Contributors
* Adithya Abraham Philip
- * Art O Cathain
* Ash Hughes
+ * 'mar-v-in'
+ * 'Thialfihar' (APG developer)
+ * Tim Bray
+
+## Occasional Contributors
+ * Art O Cathain
* Brian C. Barnes
* Bahtiar 'kalkin' Gadimov
* Daniel Albert
@@ -34,7 +41,6 @@ License: GPLv3+
* 'ligi'
* Lukas Zorich
* Manoj Khanna
- * 'mar-v-in'
* Markus Doits
* Miroojin Bakshi
* Morgan Gangwere
@@ -44,22 +50,23 @@ License: GPLv3+
* Signe Rüsch
* Sreeram Boyapati
* 'steelman'
- * 'Thialfihar' (APG developer)
- * Tim Bray
+
+[//]: # (NOTE: Alphabetic ordering)
## Libraries
- * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
- * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
* [Android Support Libraries](http://developer.android.com/tools/support-library/index.html) (Apache License v2)
+ * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
+ * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
* [KeybaseLib](https://github.com/timbray/KeybaseLib) (Apache License v2)
- * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
+ * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
+ * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
* [MiniDNS](https://github.com/rtreffer/minidns) (Apache License v2)
+ * [OkHttp](http://square.github.io/okhttp/) (Apache License v2)
+ * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design) (Apache License v2)
+ * [SafeSlinger Exchange library](https://github.com/SafeSlingerProject/exchange-android) (MIT License)
+ * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
+ * [SpongyCastle](http://rtyley.github.com/spongycastle/) (MIT X11 License)
* [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) (Apache License v2)
+ * [TokenAutoComplete](https://github.com/splitwise/TokenAutoComplete) (Apache License v2)
* [ZXing](https://github.com/zxing/zxing) (Apache License v2)
* [ZXing Android Minimal](https://github.com/journeyapps/zxing-android-embedded) (Apache License v2)
- * [PagerSlidingTabStrip](https://github.com/jpardogo/PagerSlidingTabStrip) (Material Design)</a> (Apache License v2)
- * [MaterialDrawer](https://github.com/mikepenz/MaterialDrawer) (Apache License v2)
- * [Snackbar](https://github.com/nispok/snackbar) (MIT License)
- * [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) (Apache License v2)
- * [HtmlTextView](https://github.com/sufficientlysecure/html-textview) (Apache License v2)
- * [Markdown4J](https://github.com/jdcasey/markdown4j) (Apache License v2)
diff --git a/OpenKeychain/src/main/res/raw/help_changelog.md b/OpenKeychain/src/main/res/raw/help_changelog.md
index 6f499d531..d35d52500 100644
--- a/OpenKeychain/src/main/res/raw/help_changelog.md
+++ b/OpenKeychain/src/main/res/raw/help_changelog.md
@@ -1,5 +1,24 @@
[//]: # (NOTE: Please put every sentence in its own line, Transifex puts every line in its own translation field!)
+## 3.5
+
+ * Key revocation on key deletion
+ * Improved checks for insecure cryptography
+ * Fix: Don't close OpenKeychain after first time wizard succeeds
+ * API: Version 8
+
+## 3.4
+
+ * Anonymous key download over Tor
+ * Proxy support
+ * Better YubiKey error handling
+
+## 3.3
+
+ * New decryption screen
+ * Decryption of multiple files at once
+ * Better handling of YubiKey errors
+
## 3.2
* First version with full YubiKey support available from the user interface: Edit keys, bind YubiKey to keys,...
diff --git a/OpenKeychain/src/main/res/raw/help_faq.md b/OpenKeychain/src/main/res/raw/help_faq.md
index 049d040e1..da02a08f1 100644
--- a/OpenKeychain/src/main/res/raw/help_faq.md
+++ b/OpenKeychain/src/main/res/raw/help_faq.md
@@ -52,7 +52,10 @@ However, scanning a QR code, receiving a key via NFC, or exchanging keys via Saf
## Can I mark public keys as trusted without certifying them with my own key?
No. You can, however, simply create a new key just for certification, which will essentially be the same thing.
-
+
+## I see no suitable option in the app selection menu when trying to open a local file, what's wrong?
+
+You probably don't have any stand-alone file managers installed, like [OI File Manager](https://f-droid.org/repository/browse/?fdid=org.openintents.filemanager) or [Amaze](https://f-droid.org/repository/browse/?fdid=com.amaze.filemanager). OpenKeychain needs one in order to select files from local storage or SD card, such as for importing keys or encrypting/decrypting files.
# Avanced Questions
diff --git a/OpenKeychain/src/main/res/values-cs/strings.xml b/OpenKeychain/src/main/res/values-cs/strings.xml
index f99b32a2d..6b6bc956b 100644
--- a/OpenKeychain/src/main/res/values-cs/strings.xml
+++ b/OpenKeychain/src/main/res/values-cs/strings.xml
@@ -7,11 +7,11 @@
<string name="title_encrypt_text">Zašifrovat</string>
<string name="title_encrypt_files">Zašifrovat</string>
<string name="title_decrypt">Rozšifrovat</string>
- <string name="title_unlock">Odemknout klíč</string>
<string name="title_add_subkey">Přidat podklíč</string>
<string name="title_edit_key">Editovat klíč</string>
<string name="title_preferences">Nastavení</string>
<string name="title_api_registered_apps">Aplikace</string>
+ <string name="title_key_server_preference">OpenPGP keyservery</string>
<string name="title_change_passphrase">Změnit heslo</string>
<string name="title_share_fingerprint_with">Sdílet otisk s...</string>
<string name="title_share_key">Sdílet klíč s...</string>
@@ -20,8 +20,6 @@
<string name="title_encrypt_to_file">Zašifrovat do souboru</string>
<string name="title_decrypt_to_file">Rozšifrovat do souboru</string>
<string name="title_import_keys">Importovat klíče</string>
- <string name="title_export_key">Exportovat klíče</string>
- <string name="title_export_keys">Exportovat klíče</string>
<string name="title_key_not_found">Klíč nebyl nalezen</string>
<string name="title_send_key">Nahrát na keyserver</string>
<string name="title_certify_key">Potvrdit klíč</string>
@@ -29,33 +27,31 @@
<string name="title_help">Nápověda</string>
<string name="title_log_display">Log</string>
<string name="title_exchange_keys">Vyměnit klíče</string>
- <string name="title_advanced_key_info">Další informace ke klíči</string>
+ <string name="title_advanced_key_info">Pokročilé informace</string>
<string name="title_delete_secret_key">Smazat Váš klíč \'%s\'?</string>
<string name="title_export_log">Exportovat log</string>
<string name="title_manage_my_keys">Spravovat klíče</string>
<!--section-->
<string name="section_user_ids">Identity</string>
+ <string name="section_yubikey">YubiKey</string>
<string name="section_linked_system_contact">Propojený kontakt v systému</string>
<string name="section_should_you_trust">Důvěřujete tomuto klíči?</string>
<string name="section_proof_details">Doložit ověření</string>
<string name="section_cloud_evidence">Doložení z cloudu</string>
<string name="section_keys">Podklíče</string>
<string name="section_cloud_search">Vyhledávání v Cloud službě</string>
- <string name="section_passphrase_cache">Cache hesel</string>
<string name="section_certify">Potvrdit</string>
<string name="section_actions">Akce</string>
<string name="section_share_key">Klíč</string>
<string name="section_key_server">Keyserver</string>
<string name="section_fingerprint">Otisk</string>
<string name="section_encrypt">Zašifrovat</string>
- <string name="section_decrypt">Rozšifrovat</string>
<string name="section_current_expiry">Současná expirace</string>
<string name="section_new_expiry">Nová expirace</string>
<!--button-->
<string name="btn_decrypt_verify_file">Dešifrovat, ověřit a uložit soubor</string>
<string name="btn_encrypt_share_file">Zašifrovat a sdílet soubor</string>
<string name="btn_encrypt_save_file">Zašifrovat a uložit soubor</string>
- <string name="btn_save">Uložit</string>
<string name="btn_do_not_save">Zrušit</string>
<string name="btn_delete">Smazat</string>
<string name="btn_no_date">Žádná expirace</string>
@@ -70,17 +66,18 @@
<string name="btn_view_cert_key">Zobrazit klíč certifikátu</string>
<string name="btn_create_key">Vytvořit klíč</string>
<string name="btn_add_files">Přidat soubor(y)</string>
- <string name="btn_add_share_decrypted_text">Sdílet dešifrovaný text</string>
- <string name="btn_decrypt_clipboard">Dešifrovat text ze schránky</string>
- <string name="btn_decrypt_and_verify">a ověřit podpisy</string>
- <string name="btn_decrypt_files">Dešifrovat soubory</string>
+ <string name="btn_share_decrypted_text">Sdílet dešifrovaný text</string>
+ <string name="btn_copy_decrypted_text">Kopírovat dešifrovaný text</string>
<string name="btn_encrypt_files">Zašifrovat soubory</string>
<string name="btn_encrypt_text">Zašifrovat text</string>
<string name="btn_add_email">Přidat další emailové adresy</string>
+ <string name="btn_unlock">Odemknout</string>
+ <string name="btn_add_keyserver">Přidat</string>
+ <string name="btn_save_default">Uložit jako výchozí</string>
+ <string name="btn_saved">Uloženo!</string>
<!--menu-->
<string name="menu_preferences">Nastavení</string>
<string name="menu_help">Nápověda</string>
- <string name="menu_export_key">Exportovat do souboru</string>
<string name="menu_delete_key">Smazat klíč</string>
<string name="menu_manage_keys">Spravovat klíče</string>
<string name="menu_search">Hledat</string>
@@ -89,15 +86,20 @@
<string name="menu_encrypt_to">Zašifrovat do...</string>
<string name="menu_select_all">Vybrat vše</string>
<string name="menu_export_all_keys">Exportovat všechny klíče</string>
- <string name="menu_advanced">Zobrazit pokročilé info</string>
+ <string name="menu_update_all_keys">Aktualizovat všechny klíče</string>
+ <string name="menu_advanced">Pokročilá informace</string>
+ <string name="menu_certify_fingerprint">Potvrdit porovnáním otisků</string>
+ <string name="menu_export_log">Exportovat log</string>
<!--label-->
+ <string name="label_message">Text</string>
<string name="label_file">Soubor</string>
<string name="label_files">Soubor(y)</string>
<string name="label_file_colon">Soubor:</string>
- <string name="label_no_passphrase">Bez hesla</string>
+ <string name="label_no_passphrase">Žádné heslo</string>
<string name="label_passphrase">Heslo</string>
<string name="label_unlock">Odemykám...</string>
- <string name="label_passphrase_again">Opakovat heslo</string>
+ <string name="label_passphrase_again">Zopakovat heslo</string>
+ <string name="label_show_passphrase">Zobrazit heslo</string>
<string name="label_algorithm">Algoritmus</string>
<string name="label_ascii_armor">Soubor ASCII armor</string>
<string name="label_file_ascii_armor">Povolit ASCII armor</string>
@@ -106,17 +108,17 @@
<string name="label_use_default_yubikey_pin">Použít výchozí YubiKey PIN</string>
<string name="label_use_num_keypad_for_yubikey_pin">Použít numerickou klávesnici pro YubiKey PIN</string>
<string name="label_label_use_default_yubikey_pin_summary">Používá základní PIN (123456) pro přístup k YubiKey přes NFC</string>
- <string name="label_asymmetric_from">Podepsáno:</string>
<string name="label_to">Zašifrovat pro:</string>
+ <string name="label_delete_after_encryption">Smazat soubory po zašifrování</string>
<string name="label_delete_after_decryption">Smazat po rozšifrování</string>
<string name="label_encryption_algorithm">Šifrovací algoritmus</string>
<string name="label_hash_algorithm">Hashovací algoritmus</string>
<string name="label_symmetric">Zašifrovat heslem</string>
- <string name="label_passphrase_cache_ttl">Čas vypršení pro cache</string>
- <string name="label_passphrase_cache_subs">Pamatovat si heslo podle podklíče</string>
+ <string name="label_message_compression">Komprimovat text</string>
<string name="label_file_compression">Komprimovat soubor</string>
- <string name="label_keyservers">Keyservery</string>
+ <string name="label_keyservers">Vyberte OpenPGP keyservery</string>
<string name="label_key_id">ID klíče</string>
+ <string name="label_key_created">Klíč vytvořen %s</string>
<string name="label_creation">Vytvořeno</string>
<string name="label_expiry">Expirace</string>
<string name="label_usage">Použití</string>
@@ -129,8 +131,22 @@
<string name="label_send_key">Synchronizovat s cloudem</string>
<string name="label_fingerprint">Otisk</string>
<string name="expiry_date_dialog_title">Nastavit datum expirace</string>
- <string name="label_first_keyserver_is_used">(Výchozí je první keyserver)</string>
<string name="label_preferred">upřednostněno</string>
+ <string name="label_enable_compression">Zapnout kompresi</string>
+ <string name="label_encrypt_filenames">Zašifrovat jména souborů</string>
+ <string name="label_hidden_recipients">Skrýt příjemce</string>
+ <string name="label_verify_keyserver">Ověřit keyserver</string>
+ <string name="label_enter_keyserver_url">Zadejte URL keyserveru</string>
+ <string name="pref_keyserver">OpenPGP keyserver</string>
+ <string name="pref_keyserver_summary">Vyhledat klíče na vybraném OpenPGP keyserveru (protokol HKP)</string>
+ <string name="pref_keybase">keybase.io</string>
+ <string name="pref_keybase_summary">Hledat klíče na keybase.io</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <!--proxy type choices and values-->
+ <!--OrbotHelper strings-->
+ <!--InstallDialogFragment strings-->
+ <!--StartOrbotDialogFragment strings-->
<string name="user_id_no_name">&lt;beze jména&gt;</string>
<string name="none">&lt;žádný&gt;</string>
<plurals name="n_keys">
@@ -172,20 +188,25 @@
<string name="flag_encrypt">Zašifrovat</string>
<string name="flag_authenticate">Authentikovat</string>
<!--sentences-->
- <string name="wrong_passphrase">Špatné heslo.</string>
+ <string name="wrong_passphrase">Chybné heslo.</string>
<string name="no_filemanager_installed">Není nainstalován žádný compatibilní správce souborů.</string>
- <string name="passphrases_do_not_match">Hesla se neshodují.</string>
- <string name="passphrase_must_not_be_empty">Prosím zadejte heslo.</string>
+ <string name="passphrases_do_not_match">Hesla se neschodují.</string>
+ <string name="passphrase_must_not_be_empty">Zadejte heslo.</string>
<string name="passphrase_for_symmetric_encryption">Symetrická šifra.</string>
<string name="passphrase_for">Zadejte heslo pro \'%s\'</string>
- <string name="pin_for">Zadat PIN pro \'%s\'</string>
+ <string name="pin_for">Zadejte PIN pro \'%s\'</string>
<string name="yubikey_pin_for">Zadejte PIN pro přístup k YubiKey pro \'%s\'</string>
- <string name="nfc_text">Přidržte YubiKey u zadní strany vašeho přístroje.</string>
- <string name="no_file_selected">Nejprve vyberte soubor.</string>
+ <string name="file_delete_confirmation_title">Smazat původní soubory?</string>
+ <string name="file_delete_confirmation">Následjící soubory budou vymazány:%s</string>
+ <string name="file_delete_successful">%1$d z %2$d souborů bylo vymazáno.%3$s</string>
<string name="encrypt_sign_successful">Úspěšně podepsáno a/nebo zašifrováno.</string>
<string name="encrypt_sign_clipboard_successful">Úspěšně podepsání a/nebo zašifrováno do schránky.</string>
<string name="select_encryption_key">Vyberte alespoň jeden šifrovací klíč.</string>
- <string name="select_encryption_or_signature_key">Vyberte alespoň jeden šifrovací nebo podpisový klíč.</string>
+ <string name="specify_file_to_encrypt_to">Prosím specifikujte do kterého souboru zašifrovat.\nVAROVÁNÍ: Pokud soubor již existuje, bude přepsán.</string>
+ <string name="specify_file_to_decrypt_to">Prosím specifikujte do kterého souboru rozšifrovat.\nVAROVÁNÍ: Pokud soubor již existuje, bude přepsán.</string>
+ <string name="key_deletion_confirmation_multi">Opravdu chcete smazat všechny vybrané soubory?</string>
+ <string name="secret_key_deletion_confirmation">Po smazání již nebudete schopni přečíst zprávy zašifrované tímto klíčem a stratíte všechny potvrzení udělané tímto klíčem!</string>
+ <string name="public_key_deletetion_confirmation">Smazat klíč \'%s\'?</string>
<string name="also_export_secret_keys">Zárověň exportovat tajný klíč</string>
<string name="reinstall_openkeychain">Narazili jste na známou chybu v Androidu. Přeinstalujte prosím OpenKeychain pokud chcete své propojit kontakty s klíči.</string>
<string name="key_exported">Úspěšně exportován 1 klíč.</string>
@@ -193,45 +214,52 @@
<string name="no_keys_exported">Žádný kláč pro export.</string>
<string name="key_creation_el_gamal_info">Žádný: pouze podklíče podporují ElGamal.</string>
<string name="key_not_found">Nemohu najít klíč %08X.</string>
+ <string name="specify_file_to_export_log_to">Prosím specifikujte do kterého souboru exportovat.\nVAROVÁNÍ: Pokud soubor již existuje, bude přepsán.</string>
<string name="list_empty">Seznam je prázný!</string>
<string name="nfc_successful">Úspěšně odeslaný klíč pomocí NFC Beam!</string>
<string name="key_copied_to_clipboard">Klíč byl zkopírován do schránky!</string>
<string name="fingerprint_copied_to_clipboard">Fingerprint byl zkopírován do schránky!</string>
+ <string name="select_key_to_certify">Prosím vyberte klíč, který bude použit k potvrzení!</string>
<string name="key_too_big_for_sharing">Klíč je příliš velký aby byl sdílen pomocí této metody!</string>
<string name="text_copied_to_clipboard">Text byl zkopírován do schránky!</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">nebyly smazány. Smažte je ručně!</string>
+ <string name="error_file_added_already">%s byl již přidán.</string>
<string name="error_file_not_found">soubor nenalezen</string>
<string name="error_no_secret_key_found">žádný vhodný tajný klíč nenalezen</string>
<string name="error_external_storage_not_ready">externí úložiště není připraveno</string>
<string name="error_key_size_minimum512bit">délka klíče musí být alespoň 512 bitů</string>
<string name="error_unknown_algorithm_choice">neznámý typ algoritmu</string>
- <string name="error_user_id_no_email">žádný email nenalezen</string>
<string name="error_key_needs_a_user_id">potřebuji alespoň jednu identitu</string>
- <string name="error_no_signature_passphrase">nebylo poskytnuto heslo</string>
<string name="error_no_signature_key">nebyl poskytnut podpisový klíč</string>
+ <string name="error_invalid_data">Žádný validní zašifrovaný nebo podepsaný OpenPGP obsah!</string>
<string name="error_integrity_check_failed">selhala kontrola integrity! Data byla pozměněna!</string>
- <string name="error_wrong_passphrase">špatné heslo</string>
+ <string name="error_wrong_passphrase">chybné heslo.</string>
<string name="error_could_not_extract_private_key">nebylo možné extrahovat privátní klíč</string>
<!--errors without preceeding Error:-->
<string name="error_jelly_bean_needed">Musíte mít Android 4.1 abyste mohli používat Androidí NFC Beam technologii!</string>
+ <string name="error_nfc_needed">NFC musí být zapnuté!</string>
+ <string name="error_beam_needed">Beam musí být zapnutý!</string>
<string name="error_nothing_import">Žádný klíč nenalezen!</string>
+ <string name="error_nothing_import_selected">Žádný klíč nebyl vybrán pro import!</string>
<string name="error_contacts_key_id_missing">Příjem ID klíče od kontatktu selhal!</string>
<string name="error_generic_report_bug">Nastala obecná chyba, prosím vytvořte nový bug report pro OpenKeychain.</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">Nepodepsáno</string>
<string name="decrypt_result_invalid_signature">Špatný podpis!</string>
- <string name="decrypt_result_signature_uncertified">Kým podepsáno (neověřen!)</string>
- <string name="decrypt_result_signature_certified">Podepsáno</string>
- <string name="decrypt_result_signature_expired_key">Klíč neplatný!</string>
- <string name="decrypt_result_signature_revoked_key">Klíč byl revokován!</string>
- <string name="decrypt_result_signature_missing_key">Neznámý veřejný klíč</string>
+ <string name="decrypt_result_signature_uncertified">Podepsáno <b>nepotvrzeným</b> klíčem</string>
+ <string name="decrypt_result_signature_secret">Podepsáno vaším klíčem</string>
+ <string name="decrypt_result_signature_certified">Podepsáno ověřeným klíčem</string>
+ <string name="decrypt_result_signature_expired_key">Podepsáno <b>vyexpirovaným</b> klíčem!</string>
+ <string name="decrypt_result_signature_revoked_key">Podepsáno <b>zneplatněným</b> klíčem!</string>
+ <string name="decrypt_result_signature_missing_key">Podepsáno <b>neznámým veřejným klíčem</b></string>
<string name="decrypt_result_encrypted">Zašifrováno</string>
<string name="decrypt_result_not_encrypted">Nezašifrováno</string>
<string name="decrypt_result_action_show">Zobrazit</string>
<string name="decrypt_result_action_Lookup">Vyhledat</string>
- <string name="decrypt_invalid_text">Podpis neplatný, platnost klíče vypršela nebo byl zrušen. Nemůžete si být jistí tím, kdo text napsal. Jste si jistí, že chcete i přesto zprávu zobrazit?</string>
+ <string name="decrypt_invalid_text">Buď je podpis neplatný nebo byl klíč zneplatněn. Není jisté, kdo napsal tento text. Chcete jej i presto zobrazit?</string>
<string name="decrypt_invalid_button">Beru na vědomí, zobraz to!</string>
<!--Add keys-->
<string name="add_keys_my_key">Můj klíč:</string>
@@ -241,6 +269,7 @@
<string name="progress_cancelling">ukončuji...</string>
<string name="progress_saving">ukládám...</string>
<string name="progress_importing">importuji...</string>
+ <string name="progress_updating">Aktualizuji klíče...</string>
<string name="progress_exporting">exportuji...</string>
<string name="progress_uploading">uloaduji...</string>
<string name="progress_building_key">vytvářím klíč...</string>
@@ -259,12 +288,13 @@
<string name="progress_modify_subkeychange">modifikuji podklíče...</string>
<string name="progress_modify_subkeyrevoke">revokuji podklíče...</string>
<string name="progress_modify_subkeyadd">přidávám podklíče...</string>
- <string name="progress_modify_passphrase">mněním heslo...</string>
+ <string name="progress_modify_passphrase">měním heslo...</string>
<plurals name="progress_exporting_key">
<item quantity="one">exportuji klíč...</item>
<item quantity="few">exportuji klíče...</item>
<item quantity="other">exportuji klíče...</item>
</plurals>
+ <string name="progress_start">připravuji operaci...</string>
<string name="progress_extracting_signature_key">extrahuji podpisový klíč...</string>
<string name="progress_extracting_key">extrahuji klíč...</string>
<string name="progress_preparing_streams">připravuji streamy...</string>
@@ -321,8 +351,8 @@
<string name="import_tab_qr_code">QR kód/NFC</string>
<string name="import_import">Importovat vybrané klíče</string>
<string name="import_qr_code_wrong">QR kód nerozpoznán! Zkuste to znovu prosím!</string>
- <string name="import_qr_code_too_short_fingerprint">Fingerprint je příliš krátký (&lt; 16 znaků)</string>
<string name="import_qr_code_button">Naskenovat QR kód</string>
+ <!--Import from URL-->
<!--Generic result toast-->
<!--Import result toast-->
<plurals name="import_keys_added_and_updated_1">
@@ -340,6 +370,7 @@
<!--Delete result toast-->
<string name="delete_nothing">Nic ke smazání.</string>
<string name="delete_cancelled">Operace smazání zrušena.</string>
+ <!--Revoke result toast (snackbar)-->
<!--Certify result toast-->
<!--Intent labels-->
<string name="intent_decrypt_file">Dešifrovat soubor pomocí OpenKeychain</string>
@@ -347,10 +378,6 @@
<string name="intent_send_encrypt">Zašifrovat pomocí OpenKeychain</string>
<string name="intent_send_decrypt">Dešifrovat pomocí OpenKeychain</string>
<!--Remote API-->
- <string name="api_settings_show_info">Zobrazit pokročilé informace</string>
- <string name="api_settings_hide_info">Schovat pokročilé informace</string>
- <string name="api_settings_show_advanced">Zobrazit pokročilá nastavení</string>
- <string name="api_settings_hide_advanced">Schovat pokročilá nastavení</string>
<string name="api_settings_no_key">Žádný klíč nebyl vybrán</string>
<string name="api_settings_select_key">Vybrat klíč</string>
<string name="api_settings_save">Uložit</string>
@@ -360,21 +387,23 @@
<string name="api_settings_start">Spustit aplikaci</string>
<string name="api_settings_delete_account">Smazat účet</string>
<string name="api_settings_package_name">Jméno balíčku</string>
- <string name="api_settings_package_signature">SHA-256 z podpisu balíčku</string>
<string name="api_settings_settings">Nastavení</string>
<string name="api_settings_key">Klíč účtu:</string>
<string name="api_settings_accounts_empty">Žádné účty nejsou specifikovány pro tuto appku.</string>
<string name="api_register_allow">Povolit přístup</string>
<string name="api_register_disallow">Zamítnout přístup</string>
<string name="api_register_error_select_key">Prosím vyberte klíč!</string>
- <string name="api_select_pub_keys_missing_text">Nebyly nalezeny žádné klíče pro tyto identity:</string>
- <string name="api_select_pub_keys_dublicates_text">Existuje více jak jeden klíč pro tyto identity:</string>
<string name="api_select_pub_keys_text">Zkontrolujte prosím seznam příjemců!</string>
<string name="api_select_pub_keys_text_no_user_ids">Vyberte prosím příjemce!</string>
<string name="api_error_wrong_signature">Selhala kontrola podpisu! Instalovali jste tuto aplikaci z jiného zdroje? Pokud jste si jistí, že toto není útok, zneplatněte registraci této aplikace v OpenKeychain a poté ji znovu zaregistrujte.</string>
+ <string name="api_select_sign_key_text">Prosím vyberte jeden z existujících klíčů nebo vytvořte novy.</string>
+ <string name="api_select_keys_text">Žádným z povolených klíčů se nedaří obsah rozšifrovat. Prosím vyberte povolené klíče.</string>
<!--Share-->
<string name="share_qr_code_dialog_title">Sdílet pomocí QR kódu</string>
<string name="share_nfc_dialog">Sdílet pomocí NFC</string>
+ <!--retry upload dialog-->
+ <!--Delete or revoke private key dialog-->
+ <!--Delete Or Revoke Dialog spinner-->
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">1 kíč vybrán</item>
@@ -388,6 +417,7 @@
<string name="key_view_action_edit">Editovat klíč</string>
<string name="key_view_action_encrypt">Zašifrovat text</string>
<string name="key_view_action_encrypt_files">soubory</string>
+ <string name="key_view_action_certify">Potvrdit klíč</string>
<string name="key_view_action_update">Aktualizuji z keyserveru</string>
<string name="key_view_action_share_with">Sdílet pomocí...</string>
<string name="key_view_action_share_nfc">Sdílet přes NFC</string>
@@ -396,6 +426,7 @@
<string name="key_view_tab_share">Sdílet</string>
<string name="key_view_tab_keys">Podklíče</string>
<string name="key_view_tab_certs">Certifikáty</string>
+ <string name="key_view_tab_keybase">Keybase.io</string>
<string name="user_id_info_revoked_title">Zneplatněno</string>
<string name="user_id_info_revoked_text">Tato identity byla zneplatněna vlastníkem klíče. Klíč již není platný.</string>
<string name="user_id_info_certified_title">Ověřeno</string>
@@ -405,7 +436,22 @@
<string name="user_id_info_invalid_title">Neplatná</string>
<string name="user_id_info_invalid_text">S touto identitou je něco v nepořádku!</string>
<!--Key trust-->
+ <string name="key_trust_already_verified">Již jste tento klíč potvrdili!</string>
+ <string name="key_trust_it_is_yours">Toto je jeden z vašich klíčů!</string>
+ <string name="key_trust_maybe">Tento klíč je buďto zneplatněný nebo vypršel\nNepotvrdili jste ho, ale můžete mu začít důvěřovat.</string>
+ <string name="key_trust_revoked">Klíč byl zneplatněn vlastníkem. Neměli byste mu důvěřovat.</string>
+ <string name="key_trust_expired">Tento klíč vypršel. Neměli byste mu důvěřovat.</string>
+ <string name="key_trust_old_keys">Rozšifrovat staré zprávy pocházející z doby kdy klíč byl ještě platný, může být v pořádku.</string>
+ <string name="key_trust_no_cloud_evidence">O důvěryhodnosti tohoto klíče není možné získat důkaz z cloudu.</string>
+ <string name="key_trust_start_cloud_search">Vyhledat</string>
+ <string name="key_trust_results_prefix">Keybase.io nabízí “důkazy” které tvrdí, že vlastníkem tohoto klíče je: </string>
+ <string name="key_trust_header_text">Poznámka: Keybase.io důkazy jsou experimentální fíčura OpenKeychainu. Doporučujeme vám navíc potvrdit je pomocí naskenování QR kódu nebo si vyměnit klíče pomocí NFC.</string>
<!--keybase proof stuff-->
+ <string name="keybase_a_post">Příspěvek</string>
+ <string name="keybase_twitter_proof">tweet</string>
+ <string name="keybase_web_site_proof">textový soubor</string>
+ <string name="keybase_reddit_proof">soubor JSON</string>
+ <string name="keybase_verify">Ověřit</string>
<!--Edit key-->
<string name="edit_key_action_change_passphrase">Změnit heslo</string>
<string name="edit_key_action_add_identity">Přidat identitu</string>
@@ -420,26 +466,43 @@
</string-array>
<string name="edit_key_edit_user_id_revoked">Tato identity byla zneplatněna. Toto není možné vzít zpět.</string>
<string name="edit_key_edit_subkey_title">Vyberte akci!</string>
- <string-array name="edit_key_edit_subkey">
- <item>Změnit expiraci</item>
- <item>Zneplatnit podklíč</item>
- <item>Strip podklíč</item>
- </string-array>
<string name="edit_key_new_subkey">nový podklíč</string>
<string name="edit_key_select_flag">Prosím vyberte alespoň jeden příznak!</string>
<string name="edit_key_error_add_identity">Přidejte alespoň jednu identitu!</string>
<string name="edit_key_error_add_subkey">Přidejte alespoň jeden podklíč!</string>
<!--Create key-->
+ <string name="create_key_upload">Synchronizovat s cloudem</string>
<string name="create_key_empty">Toto pole je vyžadováno</string>
- <string name="create_key_passphrases_not_equal">Heslo nesouhlasí</string>
+ <string name="create_key_passphrases_not_equal">Hesla se neschodují.</string>
<string name="create_key_final_text">Zadali jste následující identitu:</string>
<string name="create_key_final_robot_text">Vytvoření klíče může chvíli trvat, mezitím si dejte třeba šálek dobré kávy...</string>
<string name="create_key_rsa">(3 podklíče, RSA, 4096 bit)</string>
<string name="create_key_custom">(uživatelská konfigurace klíče)</string>
+ <string name="create_key_email_text">Zadejte hlavní emailovou adresu používanou pro zabezpečenou komunikaci.</string>
+ <string name="create_key_passphrase_text">Zvolte silné heslo. Chrání váš klíč pokud je zařízeno zcizeno.</string>
+ <string name="create_key_hint_full_name">Plné jméno nebo přezdívka</string>
<string name="create_key_edit">Změnit konfiguraci klíče</string>
+ <string name="create_key_add_email">Přidat emailovou adresu</string>
+ <string name="create_key_add_email_text">Další emailové adresy jsou přiřazeny k tomuto klíči a mohou být požity k zabezpečené komunikaci.</string>
+ <string name="create_key_email_already_exists_text">Emailová adresa již byla přidána.</string>
+ <string name="create_key_email_invalid_email">Formát emailové adresy není platný.</string>
<!--View key-->
+ <string name="view_key_revoked">Zneplatněn: Klíč nesmí být dále použit!</string>
+ <string name="view_key_expired">Vypršel: Kontakt musí rozšířit platnost klíče!</string>
+ <string name="view_key_expired_secret">Vypršel: Můžete rozšířit platnost klíče tím, že ho upravíte!</string>
+ <string name="view_key_my_key">Můj klíč</string>
+ <string name="view_key_verified">Potvrzený klíč</string>
+ <string name="view_key_unverified">Nepotvrzen: Naskenujte QR kód abyste ho potvrdili!</string>
+ <string name="view_key_fragment_no_system_contact">&lt;žádný&gt;</string>
+ <!--Add/Edit keyserver-->
+ <string name="add_keyserver_dialog_title">Přidat keyserver</string>
+ <string name="add_keyserver_verified">Keyserver ověřen!</string>
+ <string name="add_keyserver_without_verification">Keyserver přidán bez verifikace.</string>
+ <string name="add_keyserver_invalid_url">Neplatná URL!</string>
+ <string name="add_keyserver_connection_failed">Nepodařilo se připojit ke key severu. Prosím ověřte URL a vaše připojení k internetu.</string>
<!--Navigation Drawer-->
<string name="nav_keys">Klíče</string>
+ <string name="nav_encrypt_decrypt">Zašifrovat/Dešifrovat</string>
<string name="nav_apps">Aplikace</string>
<string name="drawer_open">Otevřít navigační panel</string>
<string name="drawer_close">Zavří navigační panel</string>
@@ -460,11 +523,113 @@
<string name="msg_internal_error">Vnitřní chyba!</string>
<string name="msg_cancelled">Operace zrušena.</string>
<!--Import Public log entries-->
+ <string name="msg_ip_apply_batch">Aplikuji vložení dávkovou operace.</string>
+ <string name="msg_ip_bad_type_secret">Pokusil jsem se importovat bezpečnou klíčenku jako veřejmý. Toto je bug, prosím vyplňte hlášení!</string>
+ <string name="msg_ip_delete_old_fail">Nebyl nalezen žádný starý klíč (vytvořit nový?)</string>
+ <string name="msg_ip_delete_old_ok">Z databáze byl smazán starý klíč</string>
<string name="msg_ip_encode_fail">Operace selhala kvůli chybě kódování</string>
+ <string name="msg_ip_error_io_exc">Operace selhala kůli i/o chybě</string>
+ <string name="msg_ip_error_op_exc">Operace selhala kůli chybě databáze</string>
+ <string name="msg_ip_error_remote_ex">Operace selhala kůli interní chybě</string>
<string name="msg_ip">Importuje se veřejná klíčenka %s</string>
+ <string name="msg_ip_insert_keyring">Kóduji data klíčenky</string>
+ <string name="msg_ip_insert_keys">Parsuji klíče</string>
+ <string name="msg_ip_prepare">Připravuji operaci databáze</string>
+ <string name="msg_ip_master">Zpracovávám hlavní klíč %s</string>
+ <string name="msg_ip_master_expired">Klíčenka vypršela %s</string>
+ <string name="msg_ip_master_expires">Klíčenka vyprší %s</string>
+ <string name="msg_ip_master_flags_unspecified">Hlavní příznaky: nespecifikované (předpokládat všechny)</string>
+ <string name="msg_ip_master_flags_cesa">Hlavní příznaky: ověřiení, zašifrování, podepsání, přihlášení</string>
+ <string name="msg_ip_master_flags_cesx">Hlavní příznaky: ověření, zašifrování, podepsání</string>
+ <string name="msg_ip_master_flags_cexa">Hlavní příznaky: ověření, zašifrování, přihlášení</string>
+ <string name="msg_ip_master_flags_cexx">Hlavní příznaky: ověření, zašifrování</string>
+ <string name="msg_ip_master_flags_cxsa">Hlavní přiznaky: ověření, podepsání, přihlášení</string>
+ <string name="msg_ip_master_flags_cxsx">Hlavní příznaky: ověření, podepsání</string>
+ <string name="msg_ip_master_flags_cxxa">Hlavní příznaky: ověření, přihlášení</string>
+ <string name="msg_ip_master_flags_cxxx">Hlavní příznaky: ověření</string>
+ <string name="msg_ip_master_flags_xesa">Hlavní příznaky: zašifrování, podepsání, přihlášení</string>
+ <string name="msg_ip_master_flags_xesx">Hlavní příznaky: zašifrování, podepsání</string>
+ <string name="msg_ip_master_flags_xexa">Hlavní příznaky: zašifrování, přihlášení</string>
+ <string name="msg_ip_master_flags_xexx">Hlavní příznaky: zašifrování</string>
+ <string name="msg_ip_master_flags_xxsa">Hlavní příznaky: podepsání, přihlášení</string>
+ <string name="msg_ip_master_flags_xxsx">Hlavní příznaky: podepsání</string>
+ <string name="msg_ip_master_flags_xxxa">Hlavní příznaky: přihlášení</string>
+ <string name="msg_ip_master_flags_xxxx">Hlavní příznaky: žádné</string>
+ <string name="msg_ip_merge_public">Slučuji importovaná data do existující veřejné klíčenky</string>
+ <string name="msg_ip_merge_secret">Slučuji importovaná data do existující veřejné klíčenky</string>
+ <string name="msg_ip_subkey">Zpracovávám podklíč %s</string>
+ <string name="msg_ip_subkey_expired">Podklíč vypršel %s</string>
+ <string name="msg_ip_subkey_expires">Podklíč vypršel %s</string>
+ <string name="msg_ip_subkey_flags_unspecified">Příznaky podklíče: nespecifikované (předpokládat všechny)</string>
+ <string name="msg_ip_subkey_flags_cesa">Příznaky podklíče: ověřiení, zašifrování, podepsání, přihlášení</string>
+ <string name="msg_ip_subkey_flags_cesx">Příznaky podklíče: ověření, zašifrování, podepsání</string>
+ <string name="msg_ip_subkey_flags_cexa">Příznaky podklíče: ověřiení, zašifrování, přihlášení</string>
+ <string name="msg_ip_subkey_flags_cexx">Příznaky podklíče: ověření, zašifrování</string>
+ <string name="msg_ip_subkey_flags_cxsa">Příznaky podklíče: ověření, podepsání, přihlášení</string>
+ <string name="msg_ip_subkey_flags_cxsx">Příznaky podklíče: ověření, podepsání</string>
+ <string name="msg_ip_subkey_flags_cxxa">Příznaky podklíče: ověření, přihlášení</string>
+ <string name="msg_ip_subkey_flags_cxxx">Příznaky podklíče: ověření</string>
+ <string name="msg_ip_subkey_flags_xesa">Příznaky podklíče: zašifrování, podepsání, přihlášení</string>
+ <string name="msg_ip_subkey_flags_xesx">Příznaky podklíče: zašifrování, podepsání</string>
+ <string name="msg_ip_subkey_flags_xexa">Příznaky podklíčey: zašifrování, přihlášení</string>
+ <string name="msg_ip_subkey_flags_xexx">Příznaky podklíče: zašifrování</string>
+ <string name="msg_ip_subkey_flags_xxsa">Příznaky podklíče: podepsání, přihlášení</string>
+ <string name="msg_ip_subkey_flags_xxsx">Příznaky podklíče: podepsání</string>
+ <string name="msg_ip_subkey_flags_xxxa">Příznaky podklíče: přihlášení</string>
+ <string name="msg_ip_subkey_flags_xxxx">Příznaky podklíče: žádné</string>
+ <string name="msg_ip_success">Veřejná klíčenka úspěšně importována</string>
+ <string name="msg_ip_success_identical">Klíčenka neobsahuje žádná data, není co provést</string>
+ <string name="msg_ip_reinsert_secret">Znovu vkládám tajný klíč</string>
+ <string name="msg_ip_uid_cert_bad">Špatný certifikát!</string>
+ <string name="msg_ip_uid_cert_error">Chyba zpracování certifikátu!</string>
+ <string name="msg_ip_uid_cert_nonrevoke">Již se jedná o nezneplatnitelný certifikát, přeskakuji.</string>
+ <string name="msg_ip_uid_cert_old">Certifikát je starší než předešlý, přeskakuji.</string>
+ <string name="msg_ip_uid_cert_new">Certifikát je novější, nahrazuji předchozí.</string>
+ <string name="msg_ip_uid_cert_good">Nalezen platný certifikát od %1$s</string>
+ <string name="msg_ip_uid_cert_good_revoke">Nalezena platné zneplatnění certifikátu od %1$s</string>
+ <string name="msg_ip_uid_reorder">Přeskládávám uživatelské jména</string>
+ <string name="msg_ip_uid_processing">Zpracovávám uživatelské jméno %s</string>
+ <string name="msg_ip_uid_revoked">Uživatelské jméno zneplatněno</string>
+ <string name="msg_ip_uat_processing_image">Zpracovávám uživatelský atribut typu obrázek</string>
+ <string name="msg_ip_uat_processing_unknown">Zpracovávám uživatelský atribut neznámého typu</string>
+ <string name="msg_ip_uat_cert_bad">Špatný certifikát!</string>
+ <string name="msg_ip_uat_cert_error">Chyba zpracování certifikátu!</string>
+ <string name="msg_ip_uat_cert_nonrevoke">Již se jedná o nezneplatnitelný certifikát, přeskakuji.</string>
+ <string name="msg_ip_uat_cert_old">Certifikát je starší než předešlý, přeskakuji.</string>
+ <string name="msg_ip_uat_cert_new">Certifikát je novější, nahrazuji předchozí.</string>
+ <string name="msg_ip_uat_cert_good">Nalezen platný certifikát od %1$s</string>
+ <string name="msg_ip_uat_cert_good_revoke">Nalezena platné zneplatnění certifikátu od %1$s</string>
+ <string name="msg_ip_uat_revoked">Atribut uživatele je zneplatněn</string>
+ <string name="msg_is_bad_type_public">Pokus o import veřejné klíčenky jako tajné. Toto je bug, prosím nahlašte ho!</string>
+ <string name="msg_is_bad_type_uncanon">Pokus o importování klíčenky bez kanonizace. Toto je bug, prosím vyplňte hlášení!</string>
<!--Import Secret log entries-->
+ <string name="msg_is">Importuji tajný klíč %s</string>
<string name="msg_is_db_exception">Chyba databáze!</string>
+ <string name="msg_is_importing_subkeys">Zpracovávám tajné podklíče</string>
+ <string name="msg_is_error_io_exc">Chyba kódování klíčenky</string>
+ <string name="msg_is_merge_public">Slučuji importovaná data do existující veřejné klíčenky</string>
+ <string name="msg_is_merge_secret">Slučuji importovaná data do existující veřejné klíčenky</string>
+ <string name="msg_is_pubring_generate">Vytvářím veřejnou klíčenku z tajné</string>
+ <string name="msg_is_subkey_nonexistent">Podklíč %s není k dispozici v tajném klíči</string>
+ <string name="msg_is_subkey_ok">Označen tajný podklíč %s jako že je k dispozici</string>
+ <string name="msg_is_subkey_empty">Označen tajný podklíč %s jako k dispozici s prázným heslem</string>
+ <string name="msg_is_subkey_pin">Označen tajný podklíč %s jako že je k dispozici s PINem</string>
+ <string name="msg_is_success_identical">Klíčenka neobsahuje žádná nová data, není co provést</string>
+ <string name="msg_is_success">Úspěšně importován tajný klíč</string>
<!--Keyring Canonicalization log entries-->
+ <string name="msg_kc_error_v3">Toto je klíč OpenPGP verze 3, který již naní podporován!</string>
+ <string name="msg_kc_error_no_uid">Klíčenka neobsahuje žádná platná uživatelská jména!</string>
+ <string name="msg_kc_error_master_algo">Hlavní klíč používá neznámý (%s) algoritmus!</string>
+ <string name="msg_kc_error_dup_key">Podklíč %s je obsažen v klíčence dvakrát. Klíčenka je poškozená, nelze importovat!</string>
+ <string name="msg_kc_master">Zpracovávám hlavní klíč</string>
+ <string name="msg_kc_master_bad_type">Odebírám hlavní klíč certifikátu neznámého typu (%s)</string>
+ <string name="msg_kc_master_bad_local">Odebírám hlavní klíč certifikátu s příznakem \'lokální\'</string>
+ <string name="msg_kc_master_bad_err">Odebírám vadný hlavní klíč certifikátu</string>
+ <string name="msg_kc_master_bad_time">Odebírám zneplatňovací certifikát klíčenky s časovou značkou v budoucnu</string>
+ <string name="msg_kc_master_bad_type_uid">Odebírám uživatelské jméno certifikátu se špatnou pozicí</string>
+ <string name="msg_kc_master_bad">Odebírám špatný hlavní klíč certifikátu</string>
+ <string name="msg_kc_master_local">Odebírám hlavní klíč certifikátu s příznakem \'lokální\'</string>
+ <string name="msg_kc_revoke_dup">Odebírám redundantní zneplatňovací certifikát klíčenky</string>
<!--Keyring merging log entries-->
<!--createSecretKeyRing-->
<string name="msg_cr">Generuji nový hlavní klíč</string>
@@ -483,11 +648,10 @@
<!--Messages for SignEncrypt operation-->
<!--Messages for PgpSignEncrypt operation-->
<string name="msg_acc_saved">Účet uložen</string>
+ <!--Messages for Keybase Verification operation-->
<!--Messages for Export Log operation-->
<!--PassphraseCache-->
- <string name="passp_cache_notif_click_to_clear">Kliknout pro vymazání načtených hesel</string>
- <string name="passp_cache_notif_clear">Smazat cache</string>
- <string name="passp_cache_notif_pwd">Heslo</string>
+ <!--Keyserver sync-->
<!--First Time-->
<string name="first_time_text1">Převezměte opět kontrolu nad svým soukromím s OpenKeychain!</string>
<string name="first_time_skip">Přeskočit nastavení</string>
@@ -500,7 +664,6 @@
<string name="label_cert_type">Typ</string>
<string name="error_key_not_found">Klíč nenalezen!</string>
<string name="error_key_processing">Chyba zpracování klíče!</string>
- <string name="key_no_passphrase">bez hesla</string>
<string name="key_unavailable">nedostupný</string>
<string name="secret_cannot_multiple">Vaše vlastní klíče lze mazat pouze jednotlivě!</string>
<string name="title_view_cert">Zobrazit detaily certifikátu</string>
@@ -509,8 +672,9 @@
<string name="error_no_encrypt_subkey">Není dostupný šifrovací podklíč!</string>
<string name="contact_show_key">Zobrazit klíč (%s)</string>
<string name="swipe_to_update">Potáhnout dolů pro aktualizaci z keyserveru</string>
+ <!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
</resources>
diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml
index 4fd310837..51ec2e1ee 100644
--- a/OpenKeychain/src/main/res/values-de/strings.xml
+++ b/OpenKeychain/src/main/res/values-de/strings.xml
@@ -7,12 +7,11 @@
<string name="title_encrypt_text">Verschlüsseln</string>
<string name="title_encrypt_files">Verschlüsseln</string>
<string name="title_decrypt">Entschlüsseln</string>
- <string name="title_unlock">Schlüssel entsperren</string>
<string name="title_add_subkey">Unterschlüssel hinzufügen</string>
<string name="title_edit_key">Schlüssel bearbeiten</string>
<string name="title_preferences">Einstellungen</string>
<string name="title_api_registered_apps">Apps</string>
- <string name="title_key_server_preference">Schlüsselserver</string>
+ <string name="title_key_server_preference">OpenPGP-Schlüsselserver</string>
<string name="title_change_passphrase">Passwort ändern</string>
<string name="title_share_fingerprint_with">Teile Fingerabdruck über…</string>
<string name="title_share_key">Teile Schlüssel über...</string>
@@ -21,8 +20,8 @@
<string name="title_encrypt_to_file">In eine Datei verschlüsseln</string>
<string name="title_decrypt_to_file">In eine Datei entschlüsseln</string>
<string name="title_import_keys">Schlüssel importieren</string>
- <string name="title_export_key">Schlüssel exportieren</string>
- <string name="title_export_keys">Schlüssel exportieren</string>
+ <string name="title_export_key">Backup erstellen</string>
+ <string name="title_export_keys">Backup erstellen</string>
<string name="title_key_not_found">Schlüssel nicht gefunden</string>
<string name="title_send_key">Auf Schlüsselserver hochladen</string>
<string name="title_certify_key">Schlüssel bestätigen</string>
@@ -30,33 +29,39 @@
<string name="title_help">Hilfe</string>
<string name="title_log_display">Log</string>
<string name="title_exchange_keys">Schlüssel austauschen</string>
- <string name="title_advanced_key_info">Erweiterte Schlüsselinformation</string>
+ <string name="title_advanced_key_info">Erweiterte Informationen</string>
<string name="title_delete_secret_key">DEINEN Schlüssel \'%s\' löschen?</string>
<string name="title_export_log">Log exportieren</string>
<string name="title_manage_my_keys">Meine Schlüssel verwalten</string>
<!--section-->
<string name="section_user_ids">Identitäten</string>
+ <string name="section_yubikey">YubiKey</string>
<string name="section_linked_system_contact">Verknüpfter Systemkontakt</string>
<string name="section_should_you_trust">Sollte ich diesem Schlüssel vertrauen?</string>
- <string name="section_proof_details">Nachweis überprüfen</string>
+ <string name="section_proof_details">Nachweis verifizieren</string>
<string name="section_cloud_evidence">Nachweise aus der Cloud</string>
<string name="section_keys">Unterschlüssel</string>
<string name="section_cloud_search">Cloudsuche</string>
- <string name="section_passphrase_cache">Passwort Zwischenspeicher</string>
+ <string name="section_passphrase_cache">Passwort/PIN-Handhabung</string>
+ <string name="section_proxy_settings">Proxy-Einstellungen</string>
+ <string name="section_gui">Oberfläche</string>
+ <string name="section_sync_settings">Synchronisierungseinstellungen</string>
<string name="section_certify">Bestätigen</string>
<string name="section_actions">Aktionen</string>
<string name="section_share_key">Schlüssel</string>
<string name="section_key_server">Schlüsselserver</string>
<string name="section_fingerprint">Fingerabdruck</string>
<string name="section_encrypt">Verschlüsseln</string>
- <string name="section_decrypt">Entschlüsseln</string>
+ <string name="section_decrypt">Entschlüsseln / Verifizieren</string>
<string name="section_current_expiry">Aktuelles Ablaufdatum</string>
<string name="section_new_expiry">Neues Ablaufdatum</string>
<!--button-->
<string name="btn_decrypt_verify_file">Datei entschlüsseln, verifizieren und speichern</string>
<string name="btn_encrypt_share_file">Datei verschlüsseln und teilen</string>
<string name="btn_encrypt_save_file">Datei verschlüsseln und speichern</string>
+ <string name="btn_save_file">Datei speichern</string>
<string name="btn_save">Speichern</string>
+ <string name="btn_view_log">Log anzeigen</string>
<string name="btn_do_not_save">Abbrechen</string>
<string name="btn_delete">Löschen</string>
<string name="btn_no_date">Kein Ablaufdatum</string>
@@ -66,22 +71,26 @@
<string name="btn_back">Zurück</string>
<string name="btn_no">Nein</string>
<string name="btn_match">Fingerabdrücke stimmen überein</string>
- <string name="btn_share_encrypted_signed">Nachricht verschlüsseln und teilen</string>
+ <string name="btn_share_encrypted_signed">Text verschlüsseln und teilen</string>
<string name="btn_copy_encrypted_signed">Text verschlüsseln und kopieren</string>
<string name="btn_view_cert_key">Beglaubigungsschlüssel anzeigen</string>
<string name="btn_create_key">Schlüssel erzeugen</string>
<string name="btn_add_files">Datei(en) hinzufügen</string>
- <string name="btn_add_share_decrypted_text">Entschlüsselten Text teilen</string>
- <string name="btn_decrypt_clipboard">Text aus Zwischenablage entschlüsseln</string>
- <string name="btn_decrypt_and_verify">und Signaturen überprüfen</string>
- <string name="btn_decrypt_files">Dateien entschlüsseln</string>
+ <string name="btn_share_decrypted_text">Entschlüsselten Text teilen</string>
+ <string name="btn_copy_decrypted_text">Entschlüsselten Text kopieren</string>
+ <string name="btn_decrypt_clipboard">Aus Zwischenablage lesen</string>
+ <string name="btn_decrypt_files">Datei auswählen</string>
<string name="btn_encrypt_files">Dateien verschlüsseln</string>
<string name="btn_encrypt_text">Text verschlüsseln</string>
<string name="btn_add_email">Weitere E-Mail-Adresse hinzufügen</string>
+ <string name="btn_unlock">Entsperren</string>
+ <string name="btn_add_keyserver">Hinzufügen</string>
+ <string name="btn_save_default">Als Standard speichern</string>
+ <string name="btn_saved">Gespeichert!</string>
<!--menu-->
<string name="menu_preferences">Einstellungen</string>
<string name="menu_help">Hilfe</string>
- <string name="menu_export_key">In Datei exportieren</string>
+ <string name="menu_export_key">Backup erstellen</string>
<string name="menu_delete_key">Schlüssel löschen</string>
<string name="menu_manage_keys">Meine Schlüssel verwalten</string>
<string name="menu_search">Suchen</string>
@@ -91,11 +100,12 @@
<string name="menu_select_all">Alles auswählen</string>
<string name="menu_export_all_keys">Alle Schlüssel exportieren</string>
<string name="menu_update_all_keys">Alle Schlüssel aktualisieren</string>
- <string name="menu_advanced">Erweiterte Infos anzeigen</string>
- <string name="menu_certify_fingerprint">Über Fingerabdruckvergleich bestätigen</string>
+ <string name="menu_advanced">Erweiterte Informationen</string>
+ <string name="menu_certify_fingerprint">Durch Vergleich der Fingerabdrücke bestätigen</string>
<string name="menu_export_log">Log exportieren</string>
+ <string name="menu_keyserver_add">Hinzufügen</string>
<!--label-->
- <string name="label_message">Nachricht</string>
+ <string name="label_message">Text</string>
<string name="label_file">Datei</string>
<string name="label_files">Datei(en)</string>
<string name="label_file_colon">Datei:</string>
@@ -109,22 +119,23 @@
<string name="label_file_ascii_armor">Aktiviere ASCII Armor</string>
<string name="label_write_version_header">Lass andere wissen dass du OpenKeychain nutzt</string>
<string name="label_write_version_header_summary">Fügt \'OpenKeychain v2.7\' zu OpenPGP-Signaturen, Daten und exportierten Schlüsseln hinzu</string>
- <string name="label_use_default_yubikey_pin">Standard-YubiKey-PIN verwenden</string>
- <string name="label_use_num_keypad_for_yubikey_pin">Zifferntastatur für YubiKey PIN verwenden</string>
- <string name="label_label_use_default_yubikey_pin_summary">Verwendet zum Zugriff auf YubiKeys über NFC die Standard-PIN (123456)</string>
- <string name="label_asymmetric_from">Signiert von:</string>
+ <string name="label_use_default_yubikey_pin">Voreingestellte YubiKey-PIN verwenden</string>
+ <string name="label_use_num_keypad_for_yubikey_pin">Zifferntastatur für YubiKey-PINs verwenden</string>
+ <string name="label_label_use_default_yubikey_pin_summary">Verwendet die voreingestellte PIN (123456) um YubiKeys über NFC anzusprechen</string>
+ <string name="label_asymmetric_from">Signiere mit:</string>
<string name="label_to">Verschlüsselt an:</string>
<string name="label_delete_after_encryption">Dateien 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>
<string name="label_hash_algorithm">Hash-Algorithmus</string>
<string name="label_symmetric">Mit Passwort verschlüsseln</string>
- <string name="label_passphrase_cache_ttl">Zwischenspeicherdauer</string>
- <string name="label_passphrase_cache_subs">Passwort pro Unterschlüssel zwischenspeichern</string>
- <string name="label_message_compression">Nachrichtenkomprimierung</string>
- <string name="label_file_compression">Datei Kompression</string>
- <string name="label_keyservers">Schlüsselserver</string>
+ <string name="label_passphrase_cache_ttl">Merkdauer von Passwörtern</string>
+ <string name="label_passphrase_cache_subs">Passwort pro Unterschlüssel merken</string>
+ <string name="label_message_compression">Textkomprimierung</string>
+ <string name="label_file_compression">Dateikomprimierung</string>
+ <string name="label_keyservers">OpenPGP-Schlüsselserver auswählen</string>
<string name="label_key_id">Schlüssel-ID</string>
+ <string name="label_key_created">Schlüssel erzeugt %s</string>
<string name="label_creation">Erstellungsdatum</string>
<string name="label_expiry">Ablaufdatum</string>
<string name="label_usage">Verwendungszweck</string>
@@ -137,15 +148,56 @@
<string name="label_send_key">Mit der Cloud synchronisieren</string>
<string name="label_fingerprint">Fingerabdruck</string>
<string name="expiry_date_dialog_title">Ablaufdatum festsetzen</string>
- <string name="label_first_keyserver_is_used">(Oberster Schlüsselserver wird bevorzugt)</string>
+ <string name="label_keyservers_title">Schlüsselserver</string>
+ <string name="label_keyserver_settings_hint">Ziehen zum Ändern der Sortierung, tippen zum Bearbeiten/Löschen</string>
+ <string name="label_selected_keyserver_title">Ausgewählter Schlüsselserver</string>
<string name="label_preferred">bevorzugt</string>
<string name="label_enable_compression">Komprimierung aktivieren</string>
<string name="label_encrypt_filenames">Dateinamen verschlüsseln</string>
<string name="label_hidden_recipients">Empfänger verbergen</string>
- <string name="pref_keyserver">Schlüsselserver durchsuchen</string>
- <string name="pref_keyserver_summary">HKP-Schlüsselserver durchsuchen</string>
- <string name="pref_keybase">Keybase.io durchsuchen</string>
- <string name="pref_keybase_summary">Keybase.io-Index durchsuchen</string>
+ <string name="label_verify_keyserver">Schlüsselserver verifizieren</string>
+ <string name="label_enter_keyserver_url">Schlüsselserver-URL eingeben</string>
+ <string name="label_keyserver_dialog_delete">Schlüsselserver löschen</string>
+ <string name="label_theme">Design</string>
+ <string name="pref_keyserver">OpenPGP-Schlüsselserver</string>
+ <string name="pref_keyserver_summary">Schlüssel auf ausgewählten OpenPGP-Schlüsselservern suchen (HKP-Protokoll)</string>
+ <string name="pref_keybase">keybase.io</string>
+ <string name="pref_keybase_summary">Schlüssel auf keybase.io suchen</string>
+ <string name="label_sync_settings_keyserver_title">Schlüssel automatisch aktualisieren</string>
+ <string name="label_sync_settings_keyserver_summary_on">Schlüssel, die älter als eine Woche sind, werden vom ausgewählten Schlüsselserver aktualisiert</string>
+ <string name="label_sync_settings_keyserver_summary_off">Schlüssel werden nicht automatisch aktualisiert</string>
+ <string name="label_sync_settings_contacts_title">Kontakte mit Schlüsseln synchronisieren</string>
+ <string name="label_sync_settings_contacts_summary_on">Verknüpfungen zwischen Schlüsseln und Kontakten mit übereinstimmenden E-Mail-Adressen, erfolgen komplett offline</string>
+ <string name="label_sync_settings_contacts_summary_off">Neue Schlüssel werden nicht mit Kontakten verknüpft</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <string name="keyserver_sync_settings_title">Schlüssel automatisch aktualisieren</string>
+ <!--Proxy Preferences-->
+ <string name="pref_proxy_tor_title">Tor aktivieren</string>
+ <string name="pref_proxy_tor_summary">Orbot muss installiert sein</string>
+ <string name="pref_proxy_normal_title">Anderen Proxy aktivieren</string>
+ <string name="pref_proxy_host_title">Proxy-Host</string>
+ <string name="pref_proxy_host_err_invalid">Proxy-Host darf nicht leer sein</string>
+ <string name="pref_proxy_port_title">Proxy-Port</string>
+ <string name="pref_proxy_port_err_invalid">Ungültige Portnummer eingegeben</string>
+ <string name="pref_proxy_type_title">Proxy-Typ</string>
+ <!--proxy type choices and values-->
+ <string name="pref_proxy_type_choice_http">HTTP</string>
+ <string name="pref_proxy_type_choice_socks">SOCKS</string>
+ <!--OrbotHelper strings-->
+ <string name="orbot_ignore_tor">Tor wird nicht verwendet</string>
+ <!--InstallDialogFragment strings-->
+ <string name="orbot_install_dialog_title">Orbot installieren, um Tor zu nutzen?</string>
+ <string name="orbot_install_dialog_install">Installieren</string>
+ <string name="orbot_install_dialog_content">Du musst Orbot installiert und aktiviert haben, um Netzwerverkehr hindurchleiten zu können. Möchtest du es installieren?</string>
+ <string name="orbot_install_dialog_cancel">Abbrechen</string>
+ <string name="orbot_install_dialog_ignore_tor">Tor nicht verwendet</string>
+ <!--StartOrbotDialogFragment strings-->
+ <string name="orbot_start_dialog_title">Orbot starten?</string>
+ <string name="orbot_start_dialog_content">Orbot scheint nicht zu laufen. Möchtest du es starten und mit Tor verbinden?</string>
+ <string name="orbot_start_btn">Orbot starten</string>
+ <string name="orbot_start_dialog_start">Orbot starten</string>
+ <string name="orbot_start_dialog_cancel">Abbrechen</string>
+ <string name="orbot_start_dialog_ignore_tor">Tor nicht verwendet</string>
<string name="user_id_no_name">&lt;kein Name&gt;</string>
<string name="none">&lt;keine&gt;</string>
<plurals name="n_keys">
@@ -179,6 +231,8 @@
<string name="filemanager_title_open">Öffnen...</string>
<string name="error">Fehler</string>
<string name="error_message">Fehler: %s</string>
+ <string name="theme_dark">Dunkel</string>
+ <string name="theme_light">Hell</string>
<!--key flags-->
<string name="flag_certify">Beglaubigen</string>
<string name="flag_sign">Signieren</string>
@@ -192,41 +246,47 @@
<string name="passphrase_for_symmetric_encryption">Symmetrische Verschlüsselung.</string>
<string name="passphrase_for">Passwort für \'%s\' eingeben</string>
<string name="pin_for">PIN für \'%s\' eingeben</string>
- <string name="yubikey_pin_for">PIN für Zugriff auf Yubikey für \'%s\' eingeben</string>
- <string name="nfc_text">Halte YubiKey gegen die Rückseite Deines Geräts.</string>
+ <string name="yubikey_pin_for">PIN des YubiKeys für \'%s\' eingeben</string>
+ <string name="nfc_text">Halte den YubiKey gegen die Rückseite deines Geräts in die Nähe der NFC-Markierung.</string>
+ <string name="nfc_wait">Halte den YubiKey an die Rückseite!</string>
+ <string name="nfc_finished">Entferne jetzt den YubiKey.</string>
+ <string name="nfc_try_again_text">Nimm den YubiKey jetzt weg und drücke ERNEUT VERSUCHEN.</string>
<string name="file_delete_confirmation_title">Originaldateien löschen?</string>
<string name="file_delete_confirmation">Die folgenden Dateien werden gelöscht: %s</string>
<string name="file_delete_successful">%1$d von %2$d Dateien wurden gelöscht.%3$s</string>
- <string name="no_file_selected">Zuerst eine Datei auswählen.</string>
+ <string name="no_file_selected">Keine Datei ausgewählt.</string>
<string name="encrypt_sign_successful">Erfolgreich signiert und/oder verschlüsselt.</string>
- <string name="encrypt_sign_clipboard_successful">Erfolgreich in die Zwischenablage signiert und/oder verschlüsselt.</string>
+ <string name="encrypt_sign_clipboard_successful">Erfolgreich in die Zwischenablage signiert und/oder verschlüsselt.</string>
<string name="select_encryption_key">Mindestens einen Schlüssel zum Verschlüsseln auswählen.</string>
- <string name="select_encryption_or_signature_key">Mindestens einen Schlüssel zum Verschlüsseln oder einen zum Signieren auswählen.</string>
+ <string name="error_no_encryption_or_signature_key">Mindestens einen Schlüssel zum Verschlüsseln oder Signieren auswählen.</string>
<string name="specify_file_to_encrypt_to">Bitte angeben in welche Datei verschlüsselt werden soll.\nWARNUNG: Datei wird überschrieben, wenn sie bereits existiert.</string>
<string name="specify_file_to_decrypt_to">Bitte angeben in welche Datei entschlüsselt werden soll.\nWARNUNG: Datei wird überschrieben, wenn sie bereits existiert.</string>
- <string name="specify_file_to_export_to">Bitte angeben in welche Datei exportiert werden soll.\nWARNUNG: Datei wird überschrieben, wenn sie bereits existiert.</string>
+ <string name="specify_backup_dest">Ein Backup ohne deine Schlüssel wird erstellt, bitte wähle eine Zieldatei.\nWARNUNG: Die Datei wird überschrieben, falls sie schon existiert!</string>
+ <string name="specify_backup_dest_single">Dieser Schlüssel wir geteilt, bitte wähle eine Zieldatei.\nWARNUNG: Die Datei wird überschrieben, falls sie schon existiert!</string>
+ <string name="specify_backup_dest_secret_single">Ein vollständiges Backup deiner Schlüssel wird erstellt, bitte wähle eine Zieldatei.\nWARNUNG: Die Datei wird überschrieben, falls sie schon existiert!</string>
+ <string name="specify_backup_dest_secret">Ein vollständiges Backup aller Schlüssel, inklusive deiner eigenen, wird erstellt, bitte wähle eine Zieldatei.\nWARNUNG: Die Datei wird überschrieben, falls sie schon existiert!</string>
<string name="key_deletion_confirmation_multi">Möchtest du wirklich alle ausgewählten Schlüssel löschen?</string>
- <string name="secret_key_deletion_confirmation">Nach dem Löschen wird es dir nicht mehr möglich sein mit diesem Schlüssel verschlüsselte Nachrichten zu lesen und zudem wirst du alle damit getätigten Bestätigungen verlieren!</string>
- <string name="public_key_deletetion_confirmation">Schlüssel löschen \'%s\'?</string>
+ <string name="secret_key_deletion_confirmation">Nach dem Löschen wird es dir nicht mehr möglich sein mit diesem Schlüssel verschlüsselte Nachrichten zu lesen, zudem wirst du alle damit getätigten Bestätigungen verlieren!</string>
+ <string name="public_key_deletetion_confirmation">Schlüssel \'%s\' löschen?</string>
<string name="also_export_secret_keys">Exportiere auch private Schlüssel</string>
- <string name="reinstall_openkeychain">Du bist auf einen bekannten Fehler in Android gestoßen. Bitte installiere OpenKeychain erneut, wenn du deine Kontakte mit Schlüsseln verknüpfen willst.</string>
+ <string name="reinstall_openkeychain">Es ist ein bekannter Fehler im Zusammenhang mit Android aufgetreten. Bitte installiere OpenKeychain erneut, wenn du deine Kontakte mit Schlüsseln verknüpfen willst.</string>
<string name="key_exported">1 Schlüssel erfolgreich exportiert.</string>
<string name="keys_exported">%d Schlüssel erfolgreich exportiert.</string>
<string name="no_keys_exported">Keine Schlüssel exportiert.</string>
<string name="key_creation_el_gamal_info">Beachte: Nur Unterschlüssel unterstützen ElGamal.</string>
<string name="key_not_found">Schlüssel %08X konnte nicht gefunden werden.</string>
- <string name="specify_file_to_export_log_to">Bitte Datei zum exportieren angeben.\nWARNUNG: Die Datei wird überschrieben, falls sie bereits existiert.</string>
+ <string name="specify_file_to_export_log_to">Bitte Datei zum Exportieren angeben.\nWARNUNG: Die Datei wird überschrieben, falls sie bereits existiert.</string>
<plurals name="bad_keys_encountered">
- <item quantity="one">%d kaputter privater Schlüssel ignoriert. Evtl. wurde er mit folgender Option exportiert:\n --export-secret-subkeys\nUnbedingt mit der Option\n --export-secret-keys\n exportieren.</item>
- <item quantity="other">%d kaputte private Schlüssel ignoriert. Evtl. wurden sie mit folgender Option exportiert:\n --export-secret-subkeys\nUnbedingt mit der Option\n --export-secret-keys\n exportieren.</item>
+ <item quantity="one">%d fehlerhafter privater Schlüssel ignoriert. Evtl. wurde er mit folgender Option exportiert:\n --export-secret-subkeys\nUnbedingt mit dieser Option exportieren:\n --export-secret-keys</item>
+ <item quantity="other">%d fehlerhafte private Schlüssel ignoriert. Evtl. wurden sie mit folgender Option exportiert:\n --export-secret-subkeys\nUnbedingt mit dieser Option exportieren:\n --export-secret-keys</item>
</plurals>
<string name="list_empty">Diese Liste ist leer!</string>
<string name="nfc_successful">Schlüssel erfolgreich mit NFC-Beam gesendet!</string>
<string name="key_copied_to_clipboard">Schlüssel wurde in die Zwischenablage kopiert!</string>
<string name="fingerprint_copied_to_clipboard">Fingerabdruck wurde in die Zwischenablage kopiert!</string>
- <string name="select_key_to_certify">Bitte einen Schlüssel auswählen der für die Bestätigung genutzt werden soll!</string>
+ <string name="select_key_to_certify">Bitte wähle einen Schlüssel aus, der für die Bestätigung genutzt werden soll!</string>
<string name="key_too_big_for_sharing">Schlüssel ist zu groß um so geteilt zu werden!</string>
- <string name="text_copied_to_clipboard">Text wurde in Zwischenablage kopiert!</string>
+ <string name="text_copied_to_clipboard">Text wurde in die Zwischenablage kopiert!</string>
<!--errors
no punctuation, all lowercase,
they will be put after "error_message", e.g. "Error: file not found"-->
@@ -235,55 +295,60 @@
<string name="error_file_not_found">Datei nicht gefunden</string>
<string name="error_no_secret_key_found">kein geeigneter privater Schlüssel gefunden</string>
<string name="error_external_storage_not_ready">Externes Laufwerk ist nicht bereit</string>
- <string name="error_key_size_minimum512bit">Schlüssel muss mindestens 512 Bit lang sein</string>
- <string name="error_unknown_algorithm_choice">Unbekannte Auswahl für Algorithmus</string>
- <string name="error_user_id_no_email">keine E-Mail gefunden</string>
+ <string name="error_key_size_minimum512bit">Schlüssellänge muss mindestens 512 Bit betragen</string>
+ <string name="error_unknown_algorithm_choice">Unbekannter Algorithmus ausgewählt </string>
+ <string name="error_user_id_no_email">keine E-Mail-Adresse gefunden</string>
<string name="error_key_needs_a_user_id">Mindestens eine Identität wird benötigt</string>
<string name="error_no_signature_passphrase">kein Passwort angegeben</string>
<string name="error_no_signature_key">kein Signaturschlüssel angegeben</string>
<string name="error_invalid_data">Kein gültiger verschlüsselter oder signierter OpenPGP-Inhalt!</string>
- <string name="error_integrity_check_failed">Integritätscheck fehlgeschlagen! Die Daten wurden modifiziert!</string>
+ <string name="error_integrity_check_failed">Integritätscheck fehlgeschlagen! Die Daten wurden verändert!</string>
<string name="error_wrong_passphrase">falsches Passwort</string>
<string name="error_could_not_extract_private_key">Privater Schlüssel konnte nicht extrahiert werden</string>
<!--errors without preceeding Error:-->
- <string name="error_jelly_bean_needed">Android 4.1 wird benötigt um Androids NFC Beam nutzen zu können!</string>
+ <string name="error_jelly_bean_needed">Android 4.1 wird benötigt um Androids NFC-Beam nutzen zu können!</string>
<string name="error_nfc_needed">NFC muss aktiviert sein!</string>
<string name="error_beam_needed">Beam muss aktiviert sein!</string>
<string name="error_nothing_import">Keine Schlüssel gefunden!</string>
+ <string name="error_nothing_import_selected">Keine Schlüssel für den Import ausgewählt!</string>
<string name="error_contacts_key_id_missing">Abrufen der Schlüsselkennung von den Kontakten ist fehlgeschlagen!</string>
<string name="error_generic_report_bug">Ein allgemeiner Fehler trat auf, bitte reiche einen Fehlerbericht ein!</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">Nicht Signiert</string>
<string name="decrypt_result_invalid_signature">Ungültige Signatur!</string>
- <string name="decrypt_result_signature_uncertified">Signiert von (Nicht beglaubigt!)</string>
- <string name="decrypt_result_signature_certified">Signiert von</string>
- <string name="decrypt_result_signature_expired_key">Schlüssel ist abgelaufen!</string>
- <string name="decrypt_result_signature_revoked_key">Schlüssel wurde zurückgezogen!</string>
- <string name="decrypt_result_signature_missing_key">Unbekannter öffentlicher Schlüssel</string>
+ <string name="decrypt_result_insecure_cryptography">Unsichere Signatur (Unsichere Kryptographie)!</string>
+ <string name="decrypt_result_signature_uncertified">Von einem <b>unbestätigten</b> Schlüssel signiert</string>
+ <string name="decrypt_result_signature_secret">Von deinem Schlüssel signiert</string>
+ <string name="decrypt_result_signature_certified">Von einem bestätigten Schlüssel signiert</string>
+ <string name="decrypt_result_signature_expired_key">Von einem <b>abgelaufenen</b> Schlüssel signiert!</string>
+ <string name="decrypt_result_signature_revoked_key">Von einem <b>widerrufenen</b> Schlüssel signiert!</string>
+ <string name="decrypt_result_signature_missing_key">Von einem <b>unbekannten öffentlichen Schlüssel</b> signiert</string>
<string name="decrypt_result_encrypted">Verschlüsselt</string>
<string name="decrypt_result_not_encrypted">Nicht Verschlüsselt</string>
+ <string name="decrypt_result_insecure">Unsichere Verschlüsselung</string>
<string name="decrypt_result_action_show">Anzeigen</string>
<string name="decrypt_result_action_Lookup">Nachschlagen</string>
- <string name="decrypt_invalid_text">Der Schlüssel ist entweder ungültig oder wurde zurückgezogen/ist abgelaufen. Sie können den Verfasser der Nachricht nicht eindeutig identifizieren. Wollen Sie die Nachricht trotzdem anzeigen?</string>
- <string name="decrypt_invalid_button">Ich verstehe das Risiko, Nachricht anzeigen!</string>
+ <string name="decrypt_invalid_text">Entweder die Signatur ist ungültig oder der Schlüssel wurde widerrufen. Es ist nicht sicher, wer den Text geschrieben hat. Soll er trotzdem angezeigt werden?</string>
+ <string name="decrypt_invalid_button">Ich kenne die Risiken, Nachricht anzeigen!</string>
<!--Add keys-->
<string name="add_keys_my_key">Mein Schlüssel:</string>
<!--progress dialogs, usually ending in '…'-->
<string name="progress_done">Fertig.</string>
<string name="progress_cancel">Abbrechen</string>
- <string name="progress_cancelling">Abbrechen...</string>
+ <string name="progress_cancelling">Wird abgebrochen...</string>
<string name="progress_saving">Wird gespeichert…</string>
<string name="progress_importing">Wird importiert…</string>
- <string name="progress_updating">Aktualisiere Schlüssel...</string>
+ <string name="progress_revoking_uploading">Schlüssel wird widerrufen und hochgeladen..</string>
+ <string name="progress_updating">Schlüssel werden aktualisiert...</string>
<string name="progress_exporting">Wird exportiert…</string>
<string name="progress_uploading">Wird hochgeladen...</string>
<string name="progress_building_key">Schlüssel wird erstellt…</string>
<string name="progress_building_master_key">Hauptschlüssel wird erstellt…</string>
- <string name="progress_generating_rsa">Erzeuge neuen RSA-Schlüssel...</string>
- <string name="progress_generating_dsa">Erzeuge neuen DSA-Schlüssel...</string>
- <string name="progress_generating_elgamal">Erzeuge neuen ElGamal-Schlüssel...</string>
- <string name="progress_generating_ecdsa">\'Erzeuge neuen ECDSA Schlüssel...</string>
- <string name="progress_generating_ecdh">\'Erzeuge neuen ECDH Schlüssel...</string>
+ <string name="progress_generating_rsa">Neuer RSA-Schlüssel wird erzeugt...</string>
+ <string name="progress_generating_dsa">Neuer DSA-Schlüssel wird erzeugt...</string>
+ <string name="progress_generating_elgamal">Neuen ElGamal-Schlüssel wird erzeugt...</string>
+ <string name="progress_generating_ecdsa">Neuer ECDSA-Schlüssel wird erzeugt...</string>
+ <string name="progress_generating_ecdh">Neuer ECDH-Schlüssel wird erzeugt...</string>
<string name="progress_modify">Schlüsselbund wird verändert...</string>
<string name="progress_modify_unlock">Schlüsselbund wird entsperrt...</string>
<string name="progress_modify_adduid">User-ID wird hinzugefügt...</string>
@@ -291,21 +356,24 @@
<string name="progress_modify_revokeuid">User-IDs werden widerrufen...</string>
<string name="progress_modify_primaryuid">Primäre User-ID wird geändert...</string>
<string name="progress_modify_subkeychange">Unterschlüssel werden verändert...</string>
- <string name="progress_modify_subkeyrevoke">Unterschlüssel wird widerrufen...</string>
- <string name="progress_modify_subkeystrip">Kürze Unterschlüssel...</string>
- <string name="progress_modify_subkeyadd">Unterschlüssel wird hinzugefügt...</string>
- <string name="progress_modify_passphrase">Passwort wird geändert...</string>
+ <string name="progress_modify_subkeyrevoke">Unterschlüssel werden widerrufen...</string>
+ <string name="progress_modify_subkeystrip">Unterschlüssel werden gekürzt...</string>
+ <string name="progress_modify_subkeyadd">Unterschlüssel werden hinzugefügt...</string>
+ <string name="progress_modify_passphrase">Passwort wird geändert…</string>
+ <string name="progress_modify_pin">PIN wird geändert…</string>
+ <string name="progress_modify_admin_pin">Admin-PIN wird geändert…</string>
<plurals name="progress_exporting_key">
<item quantity="one">Schlüssel wird exportiert…</item>
<item quantity="other">Schlüssel werden exportiert…</item>
</plurals>
+ <string name="progress_start">Vorgang wird vorbereitet…</string>
<string name="progress_extracting_signature_key">Signaturschlüssel wird extrahiert…</string>
<string name="progress_extracting_key">Schlüssel wird extrahiert…</string>
<string name="progress_preparing_streams">Datenstrom wird vorbereitet…</string>
<string name="progress_encrypting">Daten werden verschlüsselt…</string>
<string name="progress_decrypting">Daten werden entschlüsselt…</string>
<string name="progress_preparing_signature">Signatur wird vorbereitet…</string>
- <string name="progress_generating_signature">Signatur wird erstellt…</string>
+ <string name="progress_generating_signature">Signatur wird erzeugt…</string>
<string name="progress_processing_signature">Signatur wird verarbeitet…</string>
<string name="progress_verifying_signature">Signatur wird verifiziert…</string>
<string name="progress_signing">Wird signiert…</string>
@@ -313,13 +381,15 @@
<string name="progress_reading_data">Daten werden gelesen…</string>
<string name="progress_finding_key">Schlüssel wird gesucht…</string>
<string name="progress_decompressing_data">Daten werden entpackt…</string>
- <string name="progress_verifying_integrity">Integrität wird überprüft…</string>
+ <string name="progress_verifying_integrity">Integrität wird verifiziert…</string>
<string name="progress_deleting_securely">\'%s\' wird sicher gelöscht…</string>
- <string name="progress_deleting">Lösche Schlüssel</string>
- <string name="progress_con_saving">Zusammenführung: Im Cache speichern...</string>
- <string name="progress_con_reimport">Zusammenführung: Neuimportierung...</string>
+ <string name="progress_deleting">Lösche Schlüssel…</string>
+ <string name="progress_con_saving">Zusammenführung: Sichere in den Zwischenspeicher...</string>
+ <string name="progress_con_reimport">Zusammenführung: Reimportiere...</string>
+ <string name="progress_verifying_keyserver_url">Schlüsselserver wird verifiziert…</string>
+ <string name="progress_starting_orbot">Orbot wird gestartet…</string>
<!--action strings-->
- <string name="hint_cloud_search_hint">Via Name, E-Mail durchsuchen...</string>
+ <string name="hint_cloud_search_hint">Via Name, E-Mail suchen...</string>
<!--key bit length selections-->
<string name="key_size_512">512</string>
<string name="key_size_768">768</string>
@@ -331,8 +401,8 @@
<string name="key_size_8192">8192</string>
<string name="key_size_custom">Benutzerdefinierte Schlüssellänge</string>
<string name="key_size_custom_info">Benutzerdefinierte Schlüssellänge (in Bit):</string>
- <string name="key_size_custom_info_rsa">Die RSA-Schlüssellänge muss grösser als 1024 und höchstens 16384 sein. Sie muss auch ein Mehrfaches von 8 sein.</string>
- <string name="key_size_custom_info_dsa">Die DSA-Schlüssellänge muss mindestens 512 und höchstens 1024 sein. Auch muss sie ein Mehrfaches von 64 sein.</string>
+ <string name="key_size_custom_info_rsa">Die RSA-Schlüssellänge muss grösser als 1024 und kleiner als 16384 sein. Sie muss außerdem ein Vielfaches von 8 sein.</string>
+ <string name="key_size_custom_info_dsa">Die DSA-Schlüssellänge muss mindestens 512 und höchstens 1024 sein. Sie muss außerdem ein Vielfaches von 64 sein.</string>
<!--elliptic curve names-->
<string name="key_curve_nist_p256">NIST P-256</string>
<string name="key_curve_nist_p384">NIST P-384</string>
@@ -354,34 +424,37 @@
<string name="help_about_version">Version:</string>
<!--Import-->
<string name="import_tab_keyserver">Schlüsselserver</string>
- <string name="import_tab_cloud">Cloud durchsuchen</string>
+ <string name="import_tab_cloud">In der Cloud suchen</string>
<string name="import_tab_direct">Datei/Zwischenablage</string>
<string name="import_tab_qr_code">QR-Code/NFC</string>
<string name="import_import">Ausgewählte Schlüssel importieren</string>
- <string name="import_qr_code_wrong">Falsch formatierter QR-Code! Bitte erneut versuchen!</string>
- <string name="import_qr_code_too_short_fingerprint">Der Fingerabdruck ist zu kurz (&lt; 16 Zeichen)</string>
- <string name="import_qr_code_button">QR-Code scannen</string>
+ <string name="import_qr_code_wrong">QR-Code ist fehlerhaft! Bitte erneut versuchen!</string>
+ <string name="import_qr_code_fp">Fingerabdruck ist fehlerhaft oder zu kurz!</string>
+ <string name="import_qr_code_too_short_fingerprint">Fingerabdruck ist zu kurz!</string>
+ <string name="import_qr_code_button">QR-Code einscannen</string>
<string name="import_qr_code_text">Halte deine Kamera über den QR-Code!</string>
+ <!--Import from URL-->
+ <string name="import_url_warn_no_search_parameter">Kein Suchbegriff angegeben. Eine manuelle Suche auf diesem Schlüsselserver ist trotzdem möglich.</string>
<!--Generic result toast-->
- <string name="view_log">Details</string>
+ <string name="snackbar_details">Details</string>
<string name="with_warnings">, mit Warnungen</string>
- <string name="with_cancelled">. bis abgebrochen wurde</string>
+ <string name="with_cancelled">, bis abgebrochen wurde</string>
<!--Import result toast-->
<plurals name="import_keys_added_and_updated_1">
- <item quantity="one">1 Schlüssel erfolgreich importiert</item>
- <item quantity="other">%1$d Schlüssel erfolgreich importiert</item>
+ <item quantity="one">Schlüssel wurde erfolgreich importiert</item>
+ <item quantity="other">%1$d Schlüssel wurden erfolgreich importiert</item>
</plurals>
<plurals name="import_keys_added_and_updated_2">
- <item quantity="one">und aktuallisierter Schlüssel%2$s.</item>
- <item quantity="other">und aktuallisierte %1$d Schlüssel%2$s.</item>
+ <item quantity="one">und Schlüssel aktualisiert%2$s.</item>
+ <item quantity="other">und %1$d Schlüssel aktualisiert%2$s.</item>
</plurals>
<plurals name="import_keys_added">
- <item quantity="one">Schlüssel %2$s erfolgreich importiert</item>
- <item quantity="other">%1$d Schlüssel erfolgreich importiert: %2$s.</item>
+ <item quantity="one">Schlüssel erfolgreich importiert%2$s.</item>
+ <item quantity="other">%1$d Schlüssel erfolgreich importiert%2$s.</item>
</plurals>
<plurals name="import_keys_updated">
- <item quantity="one">Schlüssel %2$s erfolgreich aktualisiert.</item>
- <item quantity="other">%1$d Schlüssel erfolgreich aktualisiert: %2$s.</item>
+ <item quantity="one">Schlüssel erfolgreich aktualisiert%2$s.</item>
+ <item quantity="other">%1$d Schlüssel erfolgreich aktualisiert%2$s.</item>
</plurals>
<plurals name="import_keys_with_errors">
<item quantity="one">Import eines Schlüssels fehlgeschlagen!</item>
@@ -395,27 +468,32 @@
<string name="import_error_nothing_cancelled">Import abgebrochen.</string>
<!--Delete result toast-->
<plurals name="delete_ok_but_fail_1">
- <item quantity="one">Der Schlüssel wurde erfolgreich gelöscht.</item>
+ <item quantity="one">Schlüssel wurde erfolgreich gelöscht</item>
<item quantity="other">%1$d Schlüssel wurden erfolgreich gelöscht</item>
</plurals>
<plurals name="delete_ok_but_fail_2">
- <item quantity="one">, aber das Löschen eines Schlüssels%2$s ist fehlgeschlagen.</item>
- <item quantity="other">, aber das Löschen von %1$d Schlüsseln%2$s ist fehlgeschlagen.</item>
+ <item quantity="one">, aber das Löschen eines Schlüssels ist fehlgeschlagen%2$s.</item>
+ <item quantity="other">, aber das Löschen von %1$d Schlüsseln ist fehlgeschlagen%2$s.</item>
</plurals>
<plurals name="delete_ok">
- <item quantity="one">Erfolgreich gelöscht key%2$s.</item>
- <item quantity="other">Erfolgreich gelöscht %1$d keys%2$s.</item>
+ <item quantity="one">Schlüssel wurde erfolgreich gelöscht%2$s.</item>
+ <item quantity="other">%1$d Schlüssel wurden erfolgreich gelöscht%2$s.</item>
</plurals>
<plurals name="delete_fail">
<item quantity="one">Fehler beim Löschen eines Schlüssels%2$s.</item>
<item quantity="other">Fehler beim Löschen von %1$d Schlüsseln.</item>
</plurals>
<string name="delete_nothing">Nichts zu löschen.</string>
- <string name="delete_cancelled">Löschen abgebrochen.</string>
+ <string name="delete_cancelled">Löschvorgang abgebrochen.</string>
+ <!--Revoke result toast (snackbar)-->
+ <string name="revoke_ok">Schlüssel erfolgreich widerrufen.</string>
+ <string name="revoke_fail">Fehler beim Widerrufen des Schlüssels!</string>
+ <string name="revoke_nothing">Nichts zu widerrufen.</string>
+ <string name="revoke_cancelled">Widerrufvorgang abgebrochen.</string>
<!--Certify result toast-->
<plurals name="certify_keys_ok">
- <item quantity="one">Schlüssel%2$s erfolgreich beglaubigt.</item>
- <item quantity="other">%1$d Schlüssel%2$s erfolgreich beglaubigt.</item>
+ <item quantity="one">Schlüssel wurde erfolgreich beglaubigt%2$s.</item>
+ <item quantity="other">%1$d Schlüssel wurden erfolgreich beglaubigt%2$s.</item>
</plurals>
<plurals name="certify_keys_with_errors">
<item quantity="one">Beglaubigung fehlgeschlagen!</item>
@@ -442,31 +520,45 @@
<string name="api_settings_save_msg">Benutzerkonto wurde gespeichert</string>
<string name="api_settings_cancel">Abbrechen</string>
<string name="api_settings_revoke">Zugriff widerufen</string>
- <string name="api_settings_start">Starte Anwendung</string>
+ <string name="api_settings_start">Anwendung starten</string>
<string name="api_settings_delete_account">Benutzerkonto löschen</string>
<string name="api_settings_package_name">Paketname</string>
- <string name="api_settings_package_signature">SHA-256 der Paketsignatur</string>
+ <string name="api_settings_package_certificate">SHA-256 des Paketzertifikats</string>
<string name="api_settings_accounts">Benutzerkonten (veraltete API)</string>
<string name="api_settings_advanced">Erweiterte Informationen</string>
<string name="api_settings_allowed_keys">Erlaubte Schlüssel</string>
<string name="api_settings_settings">Einstellungen</string>
- <string name="api_settings_key">Benutzerkontoschlüssel:</string>
+ <string name="api_settings_key">Schlüssel des Kontos:</string>
<string name="api_settings_accounts_empty">Keine Benutzerkonten mit dieser Anwendung verknüpft.</string>
<string name="api_create_account_text">Für dieses Benutzerkonto ist kein Schlüssel konfiguriert. Bitte wähle einen deiner existierenden Schlüssel aus oder erzeuge einen neuen.\nApps können nur hier ausgewählte Schlüssel nutzen!</string>
<string name="api_update_account_text">Der Schlüssel für dieses Benutzerkonto wurde gelöscht. Bitte wähle einen anderen aus!\nApps können nur hier ausgewählte Schlüssel nutzen.</string>
- <string name="api_register_text">Folgende Anwendung möchte Nachrichten ver-/entschlüsseln und in deinem Namen signieren. Zugriff erlauben?\n\nVORSICHT: Solltest du nicht wissen warum dieses Fenster erscheint, erlaube den Zugriff nicht! Du kannst den Zugriff später über den \'Apps\'-Bildschirm widerrufen.</string>
+ <string name="api_register_text">Folgende Anwendung möchte Nachrichten ver-/entschlüsseln und in deinem Namen signieren.\nZugriff erlauben?\n\nVORSICHT: Solltest du nicht wissen warum dieser Bildschirm angezeigt wird, erlaube den Zugriff nicht! Du kannst den Zugriff später über den \'Apps\'-Bildschirm widerrufen.</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>
- <string name="api_select_pub_keys_missing_text">Für diese Identitäten wurden keine Schlüssel gefunden:</string>
- <string name="api_select_pub_keys_dublicates_text">Für diese Identität existiert mehr als ein Schlüssel:</string>
+ <string name="api_select_pub_keys_missing_text">Für diese E-Mail-Adressen wurden keine Schlüssel gefunden:</string>
+ <string name="api_select_pub_keys_dublicates_text">Es exisitert mehr als ein Schlüssel für diese E-Mail-Adressen:</string>
<string name="api_select_pub_keys_text">Bitte die Liste der Empfänger überprüfen!</string>
- <string name="api_select_pub_keys_text_no_user_ids">Bitte wählen sie einen Empfänger!</string>
- <string name="api_error_wrong_signature">Signaturüberprüfung fehlgeschlagen! Haben Sie diese Anwendung aus einer anderen Quelle installiert? Wenn Sie eine Attacke ausschliessen können, sollten Sie die Registrierung der App in OpenKeychain widerrufen und die Anwendung erneut registrieren.</string>
+ <string name="api_select_pub_keys_text_no_user_ids">Bitte die Empfänger auswählen!</string>
+ <string name="api_error_wrong_signature">Signaturverifikation fehlgeschlagen! Wurde diese App aus einer anderen Quelle installiert? Wenn du eine Attacke ausschließen kannst, solltest du die Registrierung der App in OpenKeychain widerrufen und die App erneut registrieren.</string>
<string name="api_select_sign_key_text">Bitte wähle einen deiner vorhandenen Schlüssel aus oder erzeuge einen neuen.</string>
+ <string name="api_select_keys_text">Keiner der erlaubten Schlüssel kann den Inhalt entschlüsseln, bitte die erlaubten Schlüssel auswählen.</string>
<!--Share-->
<string name="share_qr_code_dialog_title">Über QR-Code teilen</string>
<string name="share_nfc_dialog">Über NFC teilen</string>
+ <!--retry upload dialog-->
+ <string name="retry_up_dialog_title">Hochladen fehlgeschlagen</string>
+ <string name="retry_up_dialog_message">Hochladen fehlgeschlagen. Soll der Vorgang wiederholt werden?</string>
+ <string name="retry_up_dialog_btn_reupload">Vorgang wiederholen</string>
+ <string name="retry_up_dialog_btn_cancel">Vorgang abbrechen</string>
+ <!--Delete or revoke private key dialog-->
+ <string name="del_rev_dialog_message">Wenn du diesen Schlüssel nicht mehr länger nutzen möchtest, sollte er widerrufen und hochgeladen werden. Wähle \'NUR LÖSCHEN\' aus, wenn du den Schlüssel aus OpenKeychain entfernen, ihn aber an anderer Stelle weiternutzen möchtest.</string>
+ <string name="del_rev_dialog_title">Schlüssel \'%s\' widerrufen/löschen</string>
+ <string name="del_rev_dialog_btn_revoke">Widerrufen und hochladen</string>
+ <string name="del_rev_dialog_btn_delete">Nur löschen</string>
+ <!--Delete Or Revoke Dialog spinner-->
+ <string name="del_rev_dialog_choice_delete">Nur löschen</string>
+ <string name="del_rev_dialog_choice_rev_upload">Widerrufen und hochladen</string>
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">1 Schlüssel ausgewählt.</item>
@@ -477,10 +569,10 @@
<string name="key_list_filter_show_certified">Nur beglaubigte Schlüssel anzeigen</string>
<!--Key view-->
<string name="key_view_action_edit">Schlüssel bearbeiten</string>
- <string name="key_view_action_encrypt">Text Verschlüsseln</string>
+ <string name="key_view_action_encrypt">Text verschlüsseln</string>
<string name="key_view_action_encrypt_files">Dateien</string>
<string name="key_view_action_certify">Schlüssel bestätigen</string>
- <string name="key_view_action_update">Von Schlüsselserver aktualisieren</string>
+ <string name="key_view_action_update">Vom Schlüsselserver aktualisieren</string>
<string name="key_view_action_share_with">Teilen über...</string>
<string name="key_view_action_share_nfc">Über NFC teilen</string>
<string name="key_view_action_upload">Auf Schlüsselserver hochladen</string>
@@ -496,7 +588,7 @@
<string name="user_id_info_uncertified_title">Nicht beglaubigt</string>
<string name="user_id_info_uncertified_text">Diese Identität wurde noch nicht beglaubigt. Du kannst nicht sicher sein, dass diese Identität wirklich zu einer bestimmten Person gehört.</string>
<string name="user_id_info_invalid_title">Ungültig</string>
- <string name="user_id_info_invalid_text">Irgend etwas ist mit dieser Identität nicht in Ordnung!</string>
+ <string name="user_id_info_invalid_text">Etwas ist mit dieser Identität nicht in Ordnung!</string>
<!--Key trust-->
<string name="key_trust_already_verified">Du hast diesen Schlüssel bereits bestätigt!</string>
<string name="key_trust_it_is_yours">Dies ist einer deiner Schlüssel!</string>
@@ -506,27 +598,27 @@
<string name="key_trust_old_keys">Es ist möglicherweise in Ordnung dies zu nutzen, um eine alte Nachricht zu entschlüsseln, die aus der Zeit stammt, als der Schlüssel noch gültig war.</string>
<string name="key_trust_no_cloud_evidence">Kein Nachweis aus der Cloud zur Vertrauenswürdigkeit dieses Schlüssels.</string>
<string name="key_trust_start_cloud_search">Suche beginnen</string>
- <string name="key_trust_results_prefix">Keybase.io bietet \"Nachweise\" die versichern, dass der Schlüsselinhaber:</string>
- <string name="key_trust_header_text">Hinweis: Keybase.io-Nachweise sind ein experimentelles Feature von OpenKeychain. Wir rufen dazu auf, zusätzlich zur Überprüfung, QR-Codes zu nutzen oder Schlüssel via NFC auszutauschen.</string>
+ <string name="key_trust_results_prefix">Keybase.io bietet \"Nachweise\" die bestätigen, dass der Schlüsselinhaber:</string>
+ <string name="key_trust_header_text">Hinweis: Keybase.io-Nachweise sind ein experimentelles Feature von OpenKeychain. Wir rufen dazu auf, zusätzlich zur Bestätigung, QR-Codes zu nutzen oder Schlüssel via NFC auszutauschen.</string>
<!--keybase proof stuff-->
- <string name="keybase_narrative_twitter">Schreibt auf Twitter als</string>
- <string name="keybase_narrative_github">Ist auf GitHub bekannt als</string>
- <string name="keybase_narrative_dns">Kontrolliert diese Domainnamen</string>
- <string name="keybase_narrative_web_site">Kann auf diesen Webseiten veröffentlichen</string>
- <string name="keybase_narrative_reddit">Veröffentlicht auf Reddit als</string>
- <string name="keybase_narrative_coinbase">Ist auf Coinbase bekannt als</string>
- <string name="keybase_narrative_hackernews">Veröffentlicht auf Hacker News als</string>
- <string name="keybase_narrative_unknown">Unbekannter Nachweistyp</string>
+ <string name="keybase_narrative_twitter">Auf Twitter schreibt, als %s</string>
+ <string name="keybase_narrative_github">Auf GitHub bekannt ist, als %s</string>
+ <string name="keybase_narrative_dns">Kontrolliert den/die Domainname(n) %s</string>
+ <string name="keybase_narrative_web_site">Auf diese Webseite(n) veröffentlichen kann %s</string>
+ <string name="keybase_narrative_reddit">Auf Reddit veröffentlicht, als %s</string>
+ <string name="keybase_narrative_coinbase">Auf Coinbase bekannt ist, als %s</string>
+ <string name="keybase_narrative_hackernews">Auf Hacker News veröffentlicht, als %s</string>
+ <string name="keybase_narrative_unknown">Unbekannter Nachweistyp %s</string>
<string name="keybase_proof_failure">Leider kann dieser Nachweis nicht überprüft werden.</string>
- <string name="keybase_unknown_proof_failure">Unbekanntes Problem mit dem Nachweis-Prüfer</string>
+ <string name="keybase_unknown_proof_failure">Unbekanntes Problem mit dem Nachweisprüfer</string>
<string name="keybase_problem_fetching_evidence">Problem mit dem Nachweis</string>
- <string name="keybase_key_mismatch">Schlüssel-Fingerabdruck stimmt nicht mit dem Fingerabdruck im Nachweis überein</string>
+ <string name="keybase_key_mismatch">Fingerabdruck des Schlüssels stimmt nicht mit dem Fingerabdruck im Nachweis überein</string>
<string name="keybase_dns_query_failure">Abfrage des DNS-TXT-Eintrags fehlgeschlagen</string>
- <string name="keybase_no_prover_found">Kein Nachweis-Prüfer gefunden für</string>
+ <string name="keybase_no_prover_found">Kein Nachweisprüfer gefunden für</string>
<string name="keybase_message_payload_mismatch">Entschlüsselter Nachweis entspricht nicht dem erwarteten Wert</string>
- <string name="keybase_message_fetching_data">Hole Nachweis</string>
- <string name="keybase_proof_succeeded">Dieser Nachweis wurde überprüft!</string>
- <string name="keybase_a_post">Ein Posting</string>
+ <string name="keybase_message_fetching_data">Nachweis wird abgerufen</string>
+ <string name="keybase_proof_succeeded">Dieser Nachweis wurde verifiziert!</string>
+ <string name="keybase_a_post">Ein Posting / eine Veröffentlichung</string>
<string name="keybase_fetched_from">abgerufen von</string>
<string name="keybase_for_the_domain">für die Domain</string>
<string name="keybase_contained_signature">enthält eine Nachricht, die nur vom Schlüsselinhaber hätte erzeugt werden können.</string>
@@ -536,30 +628,34 @@
<string name="keybase_github_proof">Ein Gist</string>
<string name="keybase_reddit_proof">Eine JSON-Datei</string>
<string name="keybase_reddit_attribution">von Reddit zugeordnet an</string>
- <string name="keybase_verify">Überprüfen</string>
+ <string name="keybase_verify">Verifizieren</string>
<!--Edit key-->
<string name="edit_key_action_change_passphrase">Passwort ändern</string>
<string name="edit_key_action_add_identity">Identität hinzufügen</string>
<string name="edit_key_action_add_subkey">Unterschlüssel hinzufügen</string>
- <string name="edit_key_edit_user_id_title">Eine Aktion auswählen</string>
+ <string name="edit_key_edit_user_id_title">Wähle eine Aktion aus!</string>
<string-array name="edit_key_edit_user_id">
- <item>Als primäre Identität definieren</item>
+ <item>Als Hauptidentität festlegen</item>
<item>Identität widerrufen</item>
</string-array>
<string-array name="edit_key_edit_user_id_revert_revocation">
- <item>Widerruf widerrufen</item>
+ <item>Widerruf rückgängig machen</item>
</string-array>
<string name="edit_key_edit_user_id_revoked">Diese Identität wurde widerrufen. Dies kann nicht rückgängig gemacht werden.</string>
- <string name="edit_key_edit_subkey_title">Aktion auswählen!</string>
+ <string name="edit_key_edit_subkey_title">Wähle eine Aktion aus!</string>
<string-array name="edit_key_edit_subkey">
<item>Ablaufdatum ändern</item>
<item>Unterschlüssel widerrufen</item>
<item>Unterschlüssel kürzen</item>
+ <item>Unterschlüssel auf YubiKey / Smartcard verschieben</item>
</string-array>
<string name="edit_key_new_subkey">neuer Unterschlüssel</string>
- <string name="edit_key_select_flag">Mindestens ein Attribut wählen!</string>
+ <string name="edit_key_select_flag">Bitte mindestens ein Attribut wählen!</string>
<string name="edit_key_error_add_identity">Füge mindestens eine Identität hinzu!</string>
- <string name="edit_key_error_add_subkey">Fügen Sie mindestens einen Unterschlüssel hinzu!</string>
+ <string name="edit_key_error_add_subkey">Füge mindestens einen Unterschlüssel hinzu!</string>
+ <string name="edit_key_error_bad_nfc_algo">Hash-Algorithmus wird von dieser Smartcard nicht unterstützt!</string>
+ <string name="edit_key_error_bad_nfc_size">Die Schlüssellänge wird von dieser Smartcard nicht unterstützt!</string>
+ <string name="edit_key_error_bad_nfc_stripped">Schlüssel kann nicht auf Smartcard verschoben werden (entweder er ist gekürzt oder \'auf Karte umgeleitet\')!</string>
<!--Create key-->
<string name="create_key_upload">Mit der Cloud synchronisieren</string>
<string name="create_key_empty">Dieses Feld wird benötigt</string>
@@ -567,23 +663,40 @@
<string name="create_key_final_text">Du hast folgende Identität eingegeben:</string>
<string name="create_key_final_robot_text">Einen Schlüssel zu erzeugen braucht eine Weile, trink in der Zeit einen Kaffee...</string>
<string name="create_key_rsa">(3 Unterschlüssel, RSA, 4096 Bit)</string>
- <string name="create_key_custom">(Benutzerdefinierte Schlüsseleinstellung)</string>
- <string name="create_key_name_text">Verknüpfe einen Namen mit diesem Schlüssel. Es kann ein voller Name, z.B. \"John Doe\", oder ein Spitzname, z.B. \"Johnny\", sein.</string>
+ <string name="create_key_custom">(Benutzerdefinierte Schlüsselkonfiguration)</string>
+ <string name="create_key_name_text">Verknüpfe einen Namen mit diesem Schlüssel. Es kann ein voller Name wie z.B. \"John Doe\", oder ein Spitzname wie z.B. \"Johnny\", sein.</string>
<string name="create_key_email_text">Gib deine Haupt-E-Mail-Adresse ein, die du für sichere Kommunikation nutzen möchtest.</string>
- <string name="create_key_passphrase_text">Wähle ein starkes Passwort. Es schützt deinen Schlüssel, falls dein Gerät gestohlen wird.</string>
+ <string name="create_key_passphrase_text">Wähle ein starkes Passwort. Es schützt den Schlüssel, wenn dein Gerät gestohlen werden sollte.</string>
<string name="create_key_hint_full_name">Vollständiger Name oder Spitzname</string>
<string name="create_key_edit">Schlüsselkonfiguration ändern</string>
<string name="create_key_add_email">E-Mail-Adresse hinzufügen</string>
<string name="create_key_add_email_text">Es sind zusätzliche E-Mail-Adressen mit diesem Schlüssel verknüpft, die zur sicheren Kommunikation verwendet werden können.</string>
- <string name="create_key_email_already_exists_text">E-Mail wurde bereits hinzugefügt</string>
+ <string name="create_key_email_already_exists_text">E-Mail-Adresse wurde bereits hinzugefügt</string>
+ <string name="create_key_email_invalid_email">Format der E-Mail-Adresse ist ungültig</string>
+ <string name="create_key_yubi_key_pin_text">Bitte die PIN gut merken, sie wird benötigt um später deinen YubiKey verwenden zu können. Schreibe zudem die Admin-PIN auf und hinterlege sie an einem sicheren Ort.</string>
+ <string name="create_key_yubi_key_pin">PIN</string>
+ <string name="create_key_yubi_key_admin_pin">Admin-PIN</string>
+ <string name="create_key_yubi_key_pin_repeat_text">Zum Fortfahren bitte PIN und Admin-PIN eingeben.</string>
+ <string name="create_key_yubi_key_pin_repeat">PIN wiederholen</string>
+ <string name="create_key_yubi_key_admin_pin_repeat">Admin-PIN wiederholen</string>
+ <string name="create_key_yubi_key_pin_not_correct">PIN ist nicht richtig!</string>
<!--View key-->
<string name="view_key_revoked">Widerrufen: Schlüssel darf nicht mehr genutzt werden!</string>
<string name="view_key_expired">Abgelaufen: Der Kontakt muss die Gültigkeit des Schlüssels verlängern!</string>
- <string name="view_key_expired_secret">Abgelaufen: Du kannst die Gültigkeit des Schlüssels verlängern indem du ihn editierst.</string>
+ <string name="view_key_expired_secret">Abgelaufen: Du kannst die Gültigkeit des Schlüssels verlängern, indem du ihn bearbeitest.</string>
<string name="view_key_my_key">Mein Schlüssel</string>
<string name="view_key_verified">Bestätigter Schlüssel</string>
- <string name="view_key_unverified">Unbestätigt: QR-Code scannen, um den Schlüssel zu bestätigen!</string>
+ <string name="view_key_unverified">Unbestätigt: QR-Code einscannen, um den Schlüssel zu bestätigen!</string>
<string name="view_key_fragment_no_system_contact">&lt;keine&gt;</string>
+ <!--Add/Edit keyserver-->
+ <string name="add_keyserver_dialog_title">Schlüsselserver hinzufügen</string>
+ <string name="edit_keyserver_dialog_title">Schlüsselserver bearbeiten</string>
+ <string name="add_keyserver_verified">Schlüsselserver verifiziert!</string>
+ <string name="add_keyserver_without_verification">Schlüsselserver ohne Verifikation hinzugefügt.</string>
+ <string name="add_keyserver_invalid_url">Ungültige URL!</string>
+ <string name="add_keyserver_connection_failed">Verbindung zum Schlüsselserver fehlgeschlagen. Bitte überprüfe die URL und deine Internetverbindung.</string>
+ <string name="keyserver_preference_deleted">%s gelöscht</string>
+ <string name="keyserver_preference_cannot_delete_last">Kann letzten Schlüsselserver nicht löschen. Mindestens einer wird benötigt!</string>
<!--Navigation Drawer-->
<string name="nav_keys">Schlüssel</string>
<string name="nav_encrypt_decrypt">Verschlüsseln/Entschlüsseln</string>
@@ -591,6 +704,7 @@
<string name="drawer_open">Menü öffnen</string>
<string name="drawer_close">Menü schließen</string>
<string name="my_keys">Meine Schlüssel</string>
+ <string name="nav_backup">Backup</string>
<!--hints-->
<string name="encrypt_content_edit_text_hint">Text eingeben</string>
<!--certs-->
@@ -607,72 +721,72 @@
<string name="msg_internal_error">Interner Fehler!</string>
<string name="msg_cancelled">Vorgang abgebrochen.</string>
<!--Import Public log entries-->
- <string name="msg_ip_apply_batch">Eingabeoperationen werden angewendet.</string>
- <string name="msg_ip_bad_type_secret">Es wurde versucht einen geheimen Schlüsselbund als Öffentlich zu importieren. Dies ist ein Fehler, bitte reiche einen Fehlerbericht ein!</string>
+ <string name="msg_ip_apply_batch">Stapel-Einfügeoperationen werden angewendet.</string>
+ <string name="msg_ip_bad_type_secret">Es wurde versucht einen privaten Schlüsselbund als öffentlichen zu importieren. Dies ist ein Fehler, bitte reiche einen Fehlerbericht ein!</string>
<string name="msg_ip_delete_old_fail">Kein alter Schlüssel gelöscht (Einen neuen erzeugen?)</string>
<string name="msg_ip_delete_old_ok">Alte Schlüssel aus der Datenbank löschen</string>
- <string name="msg_ip_encode_fail">Vorgang aufgrund Kodierungsfehler fehlgeschlagen</string>
- <string name="msg_ip_error_io_exc">Vorgang augrund Eingabe/Ausgabe-Fehler fehlgeschlagen</string>
+ <string name="msg_ip_encode_fail">Vorgang aufgrund eines Kodierungsfehlers fehlgeschlagen</string>
+ <string name="msg_ip_error_io_exc">Vorgang aufgrund eines Ein-/Ausgabefehlers fehlgeschlagen</string>
<string name="msg_ip_error_op_exc">Vorgang aufgrund eines Datenbankfehlers fehlgeschlagen</string>
- <string name="msg_ip_error_remote_ex">Vorgang augrund internem Fehler fehlgeschlagen</string>
+ <string name="msg_ip_error_remote_ex">Vorgang aufgrund eines internen Fehlers fehlgeschlagen</string>
<string name="msg_ip">Importiere öffentlichen Schlüsselbund %s</string>
<string name="msg_ip_insert_keyring">Schlüsselbunddaten werden kodiert</string>
<string name="msg_ip_insert_keys">Analysiere Schlüssel</string>
- <string name="msg_ip_prepare">Datenbank-Transaktionen werden vorbereitet</string>
+ <string name="msg_ip_prepare">Datenbankvorgänge werden vorbereitet</string>
<string name="msg_ip_master">Hauptschlüssel %s wird verarbeitet</string>
<string name="msg_ip_master_expired">Schlüsselbund abgelaufen am %s</string>
<string name="msg_ip_master_expires">Schlüsselbund läuft ab am %s</string>
<string name="msg_ip_master_flags_unspecified">Haupt-Attribute: Unspezifiziert (alle angenommen)</string>
- <string name="msg_ip_master_flags_cesa">Haupt-Attribute: beglaubigen, verschlüsseln, signieren, authentifizieren</string>
- <string name="msg_ip_master_flags_cesx">Haupt-Attribute: beglaubigen, verschlüsseln, signieren</string>
- <string name="msg_ip_master_flags_cexa">Haupt-Attribute: beglaubigen, verschlüsseln, authentifizieren</string>
- <string name="msg_ip_master_flags_cexx">Haupt-Attribute: beglaubigen, verschlüsseln</string>
- <string name="msg_ip_master_flags_cxsa">Haupt-Attribute: beglaubigen, signieren, authentifizieren</string>
- <string name="msg_ip_master_flags_cxsx">Haupt-Attribute: beglaubigen, signieren</string>
- <string name="msg_ip_master_flags_cxxa">Haupt-Attribute: beglaubigen, authentifizieren</string>
+ <string name="msg_ip_master_flags_cesa">Haupt-Attribute: beglaubigen, verschlüsseln, signieren und authentifizieren</string>
+ <string name="msg_ip_master_flags_cesx">Haupt-Attribute: beglaubigen, verschlüsseln und signieren</string>
+ <string name="msg_ip_master_flags_cexa">Haupt-Attribute: beglaubigen, verschlüsseln und authentifizieren</string>
+ <string name="msg_ip_master_flags_cexx">Haupt-Attribute: beglaubigen und verschlüsseln</string>
+ <string name="msg_ip_master_flags_cxsa">Haupt-Attribute: beglaubigen, signieren und authentifizieren</string>
+ <string name="msg_ip_master_flags_cxsx">Haupt-Attribute: beglaubigen und signieren</string>
+ <string name="msg_ip_master_flags_cxxa">Haupt-Attribute: beglaubigen und authentifizieren</string>
<string name="msg_ip_master_flags_cxxx">Haupt-Attribute: beglaubigen</string>
- <string name="msg_ip_master_flags_xesa">Haupt-Attribute: verschlüsseln, signieren, authentifizieren</string>
- <string name="msg_ip_master_flags_xesx">Haupt-Attribute: verschlüsseln, signieren</string>
- <string name="msg_ip_master_flags_xexa">Haupt-Attribute: verschlüsseln, authentifizieren</string>
- <string name="msg_ip_master_flags_xexx">Haupt-Attribute: verschlüsseln</string>
- <string name="msg_ip_master_flags_xxsa">Haupt-Attribute: signieren, authentifizieren</string>
+ <string name="msg_ip_master_flags_xesa">Haupt-Attribute: verschlüsseln, signieren und authentifizieren</string>
+ <string name="msg_ip_master_flags_xesx">Haupt-Attribute: verschlüsseln und signieren</string>
+ <string name="msg_ip_master_flags_xexa">Haupt-Attribute: verschlüsseln und authentifizieren</string>
+ <string name="msg_ip_master_flags_xexx">Haupt-Attribut: verschlüsseln</string>
+ <string name="msg_ip_master_flags_xxsa">Haupt-Attribute: signieren und authentifizieren</string>
<string name="msg_ip_master_flags_xxsx">Haupt-Attribute: signieren</string>
<string name="msg_ip_master_flags_xxxa">Haupt-Attribute: authentifizieren</string>
- <string name="msg_ip_master_flags_xxxx">Haupt-Attribute: Keine</string>
- <string name="msg_ip_merge_public">Füge importierte Daten in existierend öffentlichen Schlüsselbund ein</string>
- <string name="msg_ip_merge_secret">Importierte Daten werden in vorhandenen öffentlichen Schlüsselbund eingefügt</string>
- <string name="msg_ip_subkey">Unterschlüssel %s wird bearbeitet</string>
- <string name="msg_ip_subkey_expired">Unterschlüssel am %s abgelaufen</string>
- <string name="msg_ip_subkey_expires">Unterschlüssel läuft am %s ab</string>
- <string name="msg_ip_subkey_flags_unspecified">Unterschlüssel Attribute: Unspezifiziert (alle angenommen)</string>
- <string name="msg_ip_subkey_flags_cesa">Unterschlüssel Attribute: beglaubigen, verschlüsseln, signieren, authentifizieren</string>
- <string name="msg_ip_subkey_flags_cesx">Unterschlüssel Attribute: beglaubigen, verschlüsseln, signieren</string>
- <string name="msg_ip_subkey_flags_cexa">Unterschlüssel Attribute: beglaubigen, verschlüsseln, authentifizieren</string>
- <string name="msg_ip_subkey_flags_cexx">Unterschlüssel Attribute: beglaubigen, verschlüsseln</string>
- <string name="msg_ip_subkey_flags_cxsa">Unterschlüssel Attribute: beglaubigen, signieren, authentifizieren</string>
- <string name="msg_ip_subkey_flags_cxsx">Unterschlüssel Attribute: beglaubigen, signieren</string>
- <string name="msg_ip_subkey_flags_cxxa">Unterschlüssel Attribute: beglaubigen, authentifizieren</string>
- <string name="msg_ip_subkey_flags_cxxx">Unterschlüssel Attribute: beglaubigen</string>
- <string name="msg_ip_subkey_flags_xesa">Unterschlüssel Attribute: verschlüsseln, signieren, authentifizieren</string>
- <string name="msg_ip_subkey_flags_xesx">Unterschlüssel Attribute: verschlüsseln, signieren</string>
- <string name="msg_ip_subkey_flags_xexa">Unterschlüssel Attribute: verschlüsseln, authentifizieren</string>
- <string name="msg_ip_subkey_flags_xexx">Unterschlüssel Attribute: verschlüsseln</string>
- <string name="msg_ip_subkey_flags_xxsa">Unterschlüssel Attribute: signieren, authentifizieren</string>
- <string name="msg_ip_subkey_flags_xxsx">Unterschlüssel Attribute: signieren</string>
- <string name="msg_ip_subkey_flags_xxxa">Unterschlüssel Attribute: authentifizieren</string>
- <string name="msg_ip_subkey_flags_xxxx">Unterschlüssel Attribute: Keine</string>
+ <string name="msg_ip_master_flags_xxxx">Haupt-Attribute: keine</string>
+ <string name="msg_ip_merge_public">Importierte Daten werden in vorhandenen öffentlichen Schlüsselbund eingefügt</string>
+ <string name="msg_ip_merge_secret">Importierte Daten werden in vorhandenen privaten Schlüsselbund eingefügt</string>
+ <string name="msg_ip_subkey">Verarbeite Unterschlüssel %s</string>
+ <string name="msg_ip_subkey_expired">Unterschlüssel abgelaufen am %s</string>
+ <string name="msg_ip_subkey_expires">Unterschlüssel läuft ab am %s</string>
+ <string name="msg_ip_subkey_flags_unspecified">Unterschlüssel-Attribute: Unspezifiziert (alle angenommen)</string>
+ <string name="msg_ip_subkey_flags_cesa">Unterschlüssel-Attribute: beglaubigen, verschlüsseln, signieren und authentifizieren</string>
+ <string name="msg_ip_subkey_flags_cesx">Unterschlüssel-Attribute: beglaubigen, verschlüsseln und signieren</string>
+ <string name="msg_ip_subkey_flags_cexa">Unterschlüssel-Attribute: beglaubigen, verschlüsseln und authentifizieren</string>
+ <string name="msg_ip_subkey_flags_cexx">Unterschlüssel-Attribute: beglaubigen und verschlüsseln</string>
+ <string name="msg_ip_subkey_flags_cxsa">Unterschlüssel-Attribute: beglaubigen, signieren und authentifizieren</string>
+ <string name="msg_ip_subkey_flags_cxsx">Unterschlüssel-Attribute: beglaubigen und signieren</string>
+ <string name="msg_ip_subkey_flags_cxxa">Unterschlüssel-Attribute: beglaubigen und authentifizieren</string>
+ <string name="msg_ip_subkey_flags_cxxx">Unterschlüssel-Attribut: beglaubigen</string>
+ <string name="msg_ip_subkey_flags_xesa">Unterschlüssel-Attribute: verschlüsseln, signieren und authentifizieren</string>
+ <string name="msg_ip_subkey_flags_xesx">Unterschlüssel-Attribute: verschlüsseln und signieren</string>
+ <string name="msg_ip_subkey_flags_xexa">Unterschlüssel-Attribute: verschlüsseln und authentifizieren</string>
+ <string name="msg_ip_subkey_flags_xexx">Unterschlüssel-Attribut: verschlüsseln</string>
+ <string name="msg_ip_subkey_flags_xxsa">Unterschlüssel-Attribute: signieren und authentifizieren</string>
+ <string name="msg_ip_subkey_flags_xxsx">Unterschlüssel-Attribut: signieren</string>
+ <string name="msg_ip_subkey_flags_xxxa">Unterschlüssel-Attribut: authentifizieren</string>
+ <string name="msg_ip_subkey_flags_xxxx">Unterschlüssel-Attribute: keine</string>
<string name="msg_ip_success">Öffentlicher Schlüsselbund erfolgreich importiert</string>
- <string name="msg_ip_success_identical">Schlüsselbund enthält keine neuen Daten, es gibt nichts zu tun.</string>
- <string name="msg_ip_reinsert_secret">Geheimen Schlüssel wiedereinfügen</string>
- <string name="msg_ip_uid_cert_bad">Fehlerhafte Beglaubigung aufgetreten!</string>
+ <string name="msg_ip_success_identical">Schlüsselbund enthält keine neuen Daten, es gibt nichts zu tun</string>
+ <string name="msg_ip_reinsert_secret">Privaten Schlüssel wiedereinfügen</string>
+ <string name="msg_ip_uid_cert_bad">Fehlerhafte Beglaubigung gefunden!</string>
<string name="msg_ip_uid_cert_error">Fehler beim Verarbeiten der Beglaubigung!</string>
- <string name="msg_ip_uid_cert_nonrevoke">Besitzt bereits eine nicht widerrufbare Beglaubigung, überspringe.</string>
- <string name="msg_ip_uid_cert_old">Beglaubgigung ist älter als Vorherige, überspringe.</string>
- <string name="msg_ip_uid_cert_new">Beglaubigung ist aktueller, ersetze Vorherhige.</string>
+ <string name="msg_ip_uid_cert_nonrevoke">Besitzt bereits eine nicht widerrufbare Beglaubigung, wird übersprungen.</string>
+ <string name="msg_ip_uid_cert_old">Beglaubgigung ist älter als Vorherige, wird übersprungen.</string>
+ <string name="msg_ip_uid_cert_new">Beglaubigung ist aktueller, Vorherhige wird ersetzt.</string>
<string name="msg_ip_uid_cert_good">Korrekte Beglaubigung von %1$s gefunden</string>
<string name="msg_ip_uid_cert_good_revoke">Korrekten Beglaubigungwiderruf von %1$s gefunden</string>
<plurals name="msg_ip_uid_certs_unknown">
- <item quantity="one">Ignoriere eine Beglaubigung, ausgestellt von unbekanntm öffentlichen Schlüssel</item>
+ <item quantity="one">Ignoriere eine Beglaubigung, ausgestellt von unbekanntem öffentlichen Schlüssel</item>
<item quantity="other">Ignoriere %s Beglaubigungen, ausgestellt von unbekannten öffentlichen Schlüsseln</item>
</plurals>
<string name="msg_ip_uid_classifying_zero">User-IDs werden klassifiziert (keine vertrauenswürdigen Schlüssel verfügbar)</string>
@@ -683,9 +797,9 @@
<string name="msg_ip_uid_reorder">User-IDs werden neu geordnet</string>
<string name="msg_ip_uid_processing">User-ID %s wird verarbeitet</string>
<string name="msg_ip_uid_revoked">User-ID wurde widerrufen</string>
- <string name="msg_ip_uat_processing_image">Bilder werden verarbeitet</string>
- <string name="msg_ip_uat_processing_unknown">Unbekannte Benutzerattribute werden verarbeitet</string>
- <string name="msg_ip_uat_cert_bad">Ungültige Beglaubigung gefunden!</string>
+ <string name="msg_ip_uat_processing_image">Bild-Benutzerattribut wird verarbeitet</string>
+ <string name="msg_ip_uat_processing_unknown">Unbekanntes Benutzerattribut wird verarbeitet</string>
+ <string name="msg_ip_uat_cert_bad">Fehlerhafte Beglaubigung gefunden!</string>
<string name="msg_ip_uat_cert_error">Fehler beim Verarbeiten der Beglaubigung!</string>
<string name="msg_ip_uat_cert_nonrevoke">Nicht widerrufbare Beglaubigung bereits vorhanden, überspringe.</string>
<string name="msg_ip_uat_cert_old">Beglaubgigung ist älter als Vorherige, überspringe.</string>
@@ -698,50 +812,51 @@
</plurals>
<string name="msg_ip_uat_classifying">Klassifiziere Benutzerattribute</string>
<string name="msg_ip_uat_revoked">Benutzerattribut wurde widerrufen</string>
- <string name="msg_is_bad_type_public">Es wurde versucht einen öffentlichen Schlüsselbund als Geheim zu importieren. Dies ist ein Fehler, bitte reiche einen Fehlerbericht ein!</string>
- <string name="msg_is_bad_type_uncanon">Es wurde versucht, einen Schlüsselbund ohne Anpassung zu importieren. Dies ist ein Fehler, bitte reiche einen Fehlerbericht ein!</string>
+ <string name="msg_is_bad_type_public">Es wurde versucht einen öffentlichen Schlüsselbund als privaten zu importieren. Dies ist ein Fehler, bitte reiche einen Fehlerbericht ein!</string>
+ <string name="msg_is_bad_type_uncanon">Es wurde versucht, einen Schlüsselbund ohne vorschriftsmäßiges Format zu importieren. Dies ist ein Fehler, bitte reiche einen Fehlerbericht ein!</string>
<!--Import Secret log entries-->
- <string name="msg_is">Importiere geheimen Schlüssel %s</string>
+ <string name="msg_is">Importiere privaten Schlüssel %s</string>
<string name="msg_is_db_exception">Datenbankfehler!</string>
- <string name="msg_is_importing_subkeys">Geheime Unterschlüssel werden bearbeitet</string>
+ <string name="msg_is_importing_subkeys">Private Unterschlüssel werden verarbeitet</string>
<string name="msg_is_error_io_exc">Fehler bei Kordierung des Schlüsselbunds</string>
<string name="msg_is_merge_public">Importierte Daten werden in vorhandenen öffentlichen Schlüsselbund eingefügt</string>
- <string name="msg_is_merge_secret">Importierte Daten werden in vorhandenen öffentlichen Schlüsselbund eingefügt</string>
+ <string name="msg_is_merge_secret">Importierte Daten werden in vorhandenen privaten Schlüsselbund eingefügt</string>
<string name="msg_is_merge_special">Eigenbeglaubigungsdaten aus öffentlichem Schlüsselbund werden eingefügt</string>
- <string name="msg_is_pubring_generate">Generiere öffentlichen Schlüsselbund aus geheimem Schlüsselbund</string>
- <string name="msg_is_subkey_nonexistent">Unterschlüssel %s in privatem Schlüssel nicht verfügbar.</string>
- <string name="msg_is_subkey_ok">Geheimen Unterschlüssel %s als verfügbar markiert</string>
- <string name="msg_is_subkey_empty">Geheimen Unterschlüssel %s als verfügbar markiert, ohne Passwort</string>
- <string name="msg_is_subkey_pin">Geheimen Unterschlüssel %s als verfügbar markiert, mit PIN</string>
+ <string name="msg_is_pubring_generate">Öffentlicher Schlüsselbund wird aus privatem Schlüsselbund erzeugt</string>
+ <string name="msg_is_subkey_nonexistent">Unterschlüssel %s in privatem Schlüssel nicht verfügbar</string>
+ <string name="msg_is_subkey_ok">Privater Unterschlüssel %s als verfügbar markiert</string>
+ <string name="msg_is_subkey_empty">Privater Unterschlüssel %s als verfügbar, mit leerem Passwort, markiert</string>
+ <string name="msg_is_subkey_pin">Privater Unterschlüssel %s als verfügbar markiert, mit PIN</string>
<string name="msg_is_subkey_stripped">Privater Unterschlüssel %s als gekürzt markiert</string>
- <string name="msg_is_subkey_divert">Privater Unterschlüssel %s als \'Umgeleitet zu Smartcard/NFC\' markiert</string>
+ <string name="msg_is_subkey_divert">Privater Unterschlüssel %s als \'auf Karte umgeleitet\' markiert</string>
<string name="msg_is_success_identical">Schlüsselbund enthält keine neuen Daten, es gibt nichts zu tun</string>
- <string name="msg_is_success">Geheimer Schlüsselbund erfolgreich importiert</string>
+ <string name="msg_is_success">Privater Schlüsselbund erfolgreich importiert</string>
<!--Keyring Canonicalization log entries-->
- <string name="msg_kc_public">Öffentlicher Schlüsselbund %s wird in vorschriftsgemäße Form gebracht</string>
- <string name="msg_kc_secret">Geheimer Schlüsselbund %s wird in vorschriftsgemäße Form gebracht</string>
+ <string name="msg_kc_public">Öffentlicher Schlüsselbund %s wird in vorschriftsmäßiges Format gebracht</string>
+ <string name="msg_kc_secret">Privater Schlüsselbund %s wird in vorschriftsmäßiges Format gebracht</string>
<string name="msg_kc_error_v3">Dies ist ein OpenPGP Schlüssel der Version 3, welche hinfällig sind und nicht weiter unterstützt werden!</string>
<string name="msg_kc_error_no_uid">Schlüsselbund hat keine gültigen User-IDs!</string>
<string name="msg_kc_error_master_algo">Der Hauptschlüssel verwendet einen unbekannten (%s) Algorithmus!</string>
- <string name="msg_kc_error_dup_key">Unterschlüssel %s kommt zweimal im Schlüsselbund vor. Schlüsselbund ist fehlerhaft, wird nicht importiert!</string>
- <string name="msg_kc_master">Verarbeite Hauptschlüssel</string>
+ <string name="msg_kc_error_dup_key">Unterschlüssel %s kommt zweimal im Schlüsselbund vor. Schlüsselbund ist fehlerhaft und wird nicht importiert!</string>
+ <string name="msg_kc_master">Hauptschlüssel wird verarbeitet</string>
<string name="msg_kc_master_bad_type">Hauptschlüsselbeglaubigung unbekannter Art wird entfernt (%s)</string>
- <string name="msg_kc_master_bad_local">Entferne Hauptschlüsselbeglaubigung mit \'Lokal\'-Attribut</string>
+ <string name="msg_kc_master_bad_local">Hauptschlüsselbeglaubigung mit \'Lokal\'-Attribut wird entfernt</string>
<string name="msg_kc_master_bad_err">Fehlerhafte Hauptschlüsselbeglaubigung wird entfernt</string>
<string name="msg_kc_master_bad_time">Schlüsselbund-Widerrufszertifikat mit zukünftigem Zeitstempel wird entfernt</string>
<string name="msg_kc_master_bad_type_uid">User-ID-Beglaubigung an falscher Position wird entfernt</string>
<string name="msg_kc_master_bad">Fehlerhafte Hauptschlüsselbeglaubigung wird entfernt</string>
- <string name="msg_kc_master_local">Entferne Hauptschlüsselbeglaubigung mit \'Lokal\'-Attribut</string>
- <string name="msg_kc_revoke_dup">Entferne redundante Schlüsselbund-Widerrufszertifikat</string>
+ <string name="msg_kc_master_local">Hauptschlüsselbeglaubigung mit \'Lokal\'-Attribut wird entfernt</string>
+ <string name="msg_kc_revoke_dup">Entferne redundantes Schlüsselbund-Widerrufszertifikat</string>
<string name="msg_kc_notation_dup">Entferne redundante Vermerk-Beglaubigung</string>
<string name="msg_kc_notation_empty">Entferne leere Vermerk-Beglaubigung</string>
<string name="msg_kc_sub">Verarbeite Unterschlüssel %s</string>
<string name="msg_kc_sub_bad">Entferne ungültige Unterschlüssel-Zwischenbeglaubigung</string>
<string name="msg_kc_sub_bad_err">Entferne fehlerhafte Unterschlüssel-Zwischenbeglaubigung</string>
- <string name="msg_kc_sub_bad_local">Entferne Unterschlüssel-Zwischenbeglaubigung mit \"Lokal\"-Attribut</string>
- <string name="msg_kc_sub_bad_keyid">Ungleiche Zwischenausstellerkennung beim Unterschlüssels</string>
+ <string name="msg_kc_sub_bad_local">Zwischenbeglaubigung des Unterschlüssels mit \"Lokal\"-Attribut wird entfernt</string>
+ <string name="msg_kc_sub_bad_keyid">Unterschlüssel-Zwischenausstellerkennung stimmt nicht überein</string>
<string name="msg_kc_sub_bad_time">Entferne Unterschlüssel-Zwischenbeglaubigung mit zukünftigem Zeitstempel</string>
- <string name="msg_kc_sub_bad_type">Unbekannte Unterschlüsselbeglaubigungart: %s</string>
+ <string name="msg_kc_sub_bad_time_early">Die Unterschlüssel-Zwischenbeglaubigung hat einen früheren Zeitstempel als sein Hauptschlüssel!</string>
+ <string name="msg_kc_sub_bad_type">Unbekannte Unterschlüsselbeglaubigungsart: %s</string>
<string name="msg_kc_sub_dup">Entferne redundate Unterschlüssel-Zwischenbeglaubigung</string>
<string name="msg_kc_sub_primary_bad">Entferne Unterschlüssel-Zwischenbeglaubigung aufgrund ungültiger primärer Zwischenbeglaubigung</string>
<string name="msg_kc_sub_primary_bad_err">Entferne Unterschlüssel-Zwischenbeglaubigung aufgrund fehlerhafter primärer Zwischenbeglaubigung</string>
@@ -752,19 +867,19 @@
<string name="msg_kc_sub_revoke_dup">Entferne redundantes Unterschlüssel-Widerrufszertifikat</string>
<string name="msg_kc_sub_unknown_algo">Unterschlüssel verwendet unbekannten Algorithmus, wird nicht importiert...</string>
<string name="msg_kc_sub_algo_bad_encrpyt">Der Unterschlüssel soll für die Verschlüsselung genutzt werden, der zu verwendende Algorithmus unterstützt dies jedoch nicht.</string>
- <string name="msg_kc_sub_algo_bad_sign">Der Unterschlüssel soll zum Unterschreiben genutzt werden, der zu verwendende Algorithmus unterstützt dies jedoch nicht.</string>
- <string name="msg_kc_success">Schlüsselbund wurde erfolgreich in vorschriftsgemäße Form gebracht, keine Änderungen</string>
+ <string name="msg_kc_sub_algo_bad_sign">Der Unterschlüssel soll zum Signieren genutzt werden, der zu verwendende Algorithmus unterstützt dies jedoch nicht.</string>
+ <string name="msg_kc_success">Schlüsselbund wurde erfolgreich in vorschriftsmäßiges Format gebracht, keine Änderungen</string>
<plurals name="msg_kc_success_bad">
- <item quantity="one">Schlüsselbund wurde erfolgreich in vorschriftsgemäße Form gebracht, eine fehlerhafte Beglaubigung entfernt</item>
- <item quantity="other">Schlüsselbund wurde erfolgreich in vorschriftsgemäße Form gebracht, %d fehlerhafte Beglaubigungen entfernt</item>
+ <item quantity="one">Schlüsselbund wurde erfolgreich in vorschriftsmäßiges Format gebracht, eine fehlerhafte Beglaubigung wurde entfernt</item>
+ <item quantity="other">Schlüsselbund wurde erfolgreich in vorschriftsmäßiges Format gebracht, %d fehlerhafte Beglaubigungen wurden entfernt</item>
</plurals>
- <string name="msg_kc_success_bad_and_red">Schlüsselbund wurde erfolgreich in vorschriftsgemäße Form gebracht, %1$s fehlerhafte und %2$s redundante Beglaubigungen entfernt</string>
+ <string name="msg_kc_success_bad_and_red">Schlüsselbund wurde erfolgreich in vorschriftsmäßiges Format gebracht, %1$s fehlerhafte und %2$s redundante Beglaubigungen wurden entfernt</string>
<plurals name="msg_kc_success_redundant">
- <item quantity="one">Schlüsselbund wurde erfolgreich in vorschriftsgemäße Form gebracht, eine redundante Beglaubigung entfernt</item>
- <item quantity="other">Schlüsselbund wurde erfolgreich in vorschriftsgemäße Form gebracht, %d redundante Beglaubigungen entfernt</item>
+ <item quantity="one">Schlüsselbund wurde erfolgreich in vorschriftsmäßiges Format gebracht, eine redundante Beglaubigung wurde entfernt</item>
+ <item quantity="other">Schlüsselbund wurde erfolgreich in vorschriftsmäßiges Format gebracht, %d redundante Beglaubigungen wurden entfernt</item>
</plurals>
<string name="msg_kc_uid_bad_err">Fehlerhafte Eigenbeglaubigung für User-ID \'%s\' wird entfernt</string>
- <string name="msg_kc_uid_bad_local">Entferne User-ID-Beglaubigung mit \'Lokal\'-Attribut</string>
+ <string name="msg_kc_uid_bad_local">User-ID-Beglaubigung mit \'Lokal\'-Attribut wird entfernt</string>
<string name="msg_kc_uid_bad_time">User-ID mit zukünftigem Zeitstempel wird entfernt</string>
<string name="msg_kc_uid_bad_type">User-ID-Beglaubigung unbekannter Art wird entfernt (%s)</string>
<string name="msg_kc_uid_bad">Fehlerhafte Eigenbeglaubigung für User-ID \'%s\' wird entfernt</string>
@@ -775,13 +890,13 @@
<string name="msg_kc_uid_no_cert">Keine gültige Eigenbeglaubigung für User-ID \'%s\' gefunden, wird aus Schlüsselbund entfernt</string>
<string name="msg_kc_uid_remove">Ungültige User-ID \'%s\' wird entfernt</string>
<string name="msg_kc_uid_dup">Doppelte User-ID \'%s\' wird entfernt. Der Schlüsselbund enthielt zwei davon. Hieraus könnten fehlende Beglaubigungen resultieren!</string>
- <string name="msg_kc_uid_warn_encoding">User-ID nicht als UTF-8 bestätigt!</string>
- <string name="msg_kc_uat_jpeg">Das Benutzerattribut JPEG wird verarbeitet</string>
- <string name="msg_kc_uat_unknown">Ein unbekanntes Benutzerattribut wird verarbeitet</string>
+ <string name="msg_kc_uid_warn_encoding">User-ID nicht als UTF-8 verifiziert!</string>
+ <string name="msg_kc_uat_jpeg">JPEG-Benutzerattribut wird verarbeitet</string>
+ <string name="msg_kc_uat_unknown">Unbekanntes Benutzerattribut wird verarbeitet</string>
<string name="msg_kc_uat_bad_err">Entferne fehlerhafte Eigenbeglaubigung für Benutzerattribut</string>
- <string name="msg_kc_uat_bad_local">Entferne Benutzerattributs-Beglaubigung mit \'Lokal\'-Attribut</string>
- <string name="msg_kc_uat_bad_time">Entferne fehlerhaftes Benutzerattribute mit zukünftigem Zeitstempel</string>
- <string name="msg_kc_uat_bad_type">Entferne Benutzerattributbeglaubigung unbekannter Art (%s)</string>
+ <string name="msg_kc_uat_bad_local">Benutzerattributsbeglaubigung mit \'Lokal\'-Attribut wird entfernt</string>
+ <string name="msg_kc_uat_bad_time">Entferne Benutzerattribut mit zukünftigem Zeitstempel</string>
+ <string name="msg_kc_uat_bad_type">Entferne Benutzerattributsbeglaubigung unbekannter Art (%s)</string>
<string name="msg_kc_uat_bad">Entferne fehlerhafte Eigenbeglaubigung für Benutzerattribut</string>
<string name="msg_kc_uat_cert_dup">Entferne abgelaufene Eigenbeglaubigung für Benutzerattribut</string>
<string name="msg_kc_uat_dup">Doppeltes Benutzerattribut wird entfernt. Der Schlüsselbund enthielt zwei davon. Hieraus könnten fehlende Beglaubigungen resultieren!</string>
@@ -790,91 +905,105 @@
<string name="msg_kc_uat_revoke_old">Entferne abgelaufenes Widerrufszertifikat der Benutzerattribute</string>
<string name="msg_kc_uat_no_cert">Keine gültige Eigenbeglaubigung für das Benutzerattribut gefunden, wird aus Schlüsselbund entfernt</string>
<string name="msg_kc_uat_remove">Ungültiges Benutzerattribut wird entfernt</string>
- <string name="msg_kc_uat_warn_encoding">User-ID nicht als UTF-8 bestätigt!</string>
+ <string name="msg_kc_uat_warn_encoding">User-ID nicht als UTF-8 verifiziert!</string>
<!--Keyring merging log entries-->
- <string name="msg_mg_error_secret_dummy">Neuer öffentlicher Unterschlüssel gefunden, aber Erstellung von geheimen Unterschlüsseldummys wird nicht unterstützt!</string>
+ <string name="msg_mg_error_secret_dummy">Neuer öffentlicher Unterschlüssel gefunden, aber Erzeugung von privaten Unterschlüsseldummys wird nicht unterstützt!</string>
<string name="msg_mg_error_heterogeneous">Versuch Schlüsselbünde mit unterschiedlichen Fingerabdrücken zusammenzuführen!</string>
<string name="msg_mg_error_encode">Schwerer Fehler bei Kodierung der Signatur!</string>
<string name="msg_mg_public">Wird in öffentlichen Schlüsselbund %s eingefügt
</string>
- <string name="msg_mg_secret">Wird in geheimen Schlüsselbund %s eingefügt</string>
+ <string name="msg_mg_secret">In privaten Schlüsselbund %s einfügen</string>
<string name="msg_mg_new_subkey">Neuer Unterschlüssel %s wird hinzugefügt</string>
<string name="msg_mg_found_new">%s neue Beglaubigungen in Schlüsselbund gefunden</string>
- <string name="msg_mg_unchanged">Nichts zum zusammenführen</string>
+ <string name="msg_mg_unchanged">Nichts zusammenzuführen</string>
<!--createSecretKeyRing-->
- <string name="msg_cr">Neuer Masterschlüssel wird erzeugt</string>
- <string name="msg_cr_error_no_master">Keine Hauptschlüsseloptionen definiert!</string>
+ <string name="msg_cr">Neuer Hauptschlüssel wird erzeugt</string>
+ <string name="msg_cr_error_no_master">Keine Hauptschlüsseloptionen spezifiziert!</string>
<string name="msg_cr_error_no_user_id">Schlüsselbünde müssen mindestens eine User-ID enthalten!</string>
<string name="msg_cr_error_no_certify">Hauptschlüssel benötigt das Attribut beglaubigen!</string>
<string name="msg_cr_error_null_expiry">Ablaufdatum kann bei Schlüsselerstellung nicht \'gleiche wie vorher\' sein. Dies ist ein Programmierfehler, bitte reiche einen Fehlerbericht ein!</string>
- <string name="msg_cr_error_keysize_512">Schlüsselgröße muss größer/gleich 512 sein!</string>
- <string name="msg_cr_error_no_curve">Keine Schlüsselgröße angegeben. Dies ist ein Progammierfehler, bitte reiche einen Fehlerbericht ein!</string>
- <string name="msg_cr_error_no_keysize">Keine Elliptische Kurve angegeben. Dies ist ein Progammierfehler, bitte reiche einen Fehlerbericht ein!</string>
+ <string name="msg_cr_error_keysize_512">Schlüssellänge muss größer/gleich 512 sein!</string>
+ <string name="msg_cr_error_no_curve">Keine Schlüssellänge spezifiziert! Dies ist ein Progammierfehler, bitte reiche einen Fehlerbericht ein!</string>
+ <string name="msg_cr_error_no_keysize">Keine Elliptische Kurve spezifiziert! Dies ist ein Progammierfehler, bitte reiche einen Fehlerbericht ein!</string>
<string name="msg_cr_error_internal_pgp">Interner OpenPGP Fehler!</string>
<string name="msg_cr_error_unknown_algo">Unbekannter Algorithmus ausgewählt. Dies ist ein Progammierfehler, bitte reiche einen Fehlerbericht ein!</string>
- <string name="msg_cr_error_flags_dsa">Falsche Schlüsselattribute gewählt, DSA kann nicht zum verschlüsseln verwendet werden!</string>
- <string name="msg_cr_error_flags_elgamal">Falsche Schlüsselattribute gewählt, ElGamal kann nicht zum signieren verwendet werden!</string>
- <string name="msg_cr_error_flags_ecdsa">Falsche Schlüsselattribute gewählt, ECDSA kann nicht zum verschlüsseln verwendet werden!</string>
- <string name="msg_cr_error_flags_ecdh">Falsche Schlüsselattribute gewählt, ECDH kann nicht zum signieren verwendet werden!</string>
+ <string name="msg_cr_error_flags_dsa">Falsche Schlüsselattribute ausgewählt, DSA kann nicht zum Verschlüsseln verwendet werden!</string>
+ <string name="msg_cr_error_flags_elgamal">Falsche Schlüsselattribute ausgewählt, ElGamal kann nicht zum Signieren verwendet werden!</string>
+ <string name="msg_cr_error_flags_ecdsa">Falsche Schlüsselattribute ausgewählt, ECDSA kann nicht zum Verschlüsseln verwendet werden!</string>
+ <string name="msg_cr_error_flags_ecdh">Falsche Schlüsselattribute ausgewählt, ECDH kann nicht zum Signieren verwendet werden!</string>
<!--modifySecretKeyRing-->
- <string name="msg_mr">Verändere Schlüsselbund %s</string>
- <string name="msg_mf_error_divert_serial">Die Seriennummer eines \"Umgeleitet zu SmartCard/NFC\"-Schlüssels muss 16 Byte lang sein! Dies ist ein Progammierfehler, bitte reiche einen Fehlerbericht ein!</string>
+ <string name="msg_mr">Schlüsselbund %s wird verändert</string>
+ <string name="msg_mf_divert">Kryptographische Vorgänge werden auf Smartcard umgeleitet</string>
+ <string name="msg_mf_error_divert_newsub">Erzeugung neuer Unterschlüssel wird für \'auf Karte umgeleitete\' Hauptschlüssel nicht unterstützt!</string>
+ <string name="msg_mf_error_divert_serial">Die Seriennummer eines \'auf Karte umgeleiteten\' Schlüssels muss 16 Byte lang sein! Dies ist ein Programmierfehler, bitte reiche einen Fehlerbericht ein!</string>
<string name="msg_mf_error_encode">Kodierungsfehler!</string>
<string name="msg_mf_error_fingerprint">Tatsächlicher Fingerabdruck des Schlüssels entspricht nicht dem Erwarteten!</string>
<string name="msg_mf_error_keyid">Keine Schlüssel-ID. Dies ist ein interner Fehler, bitte reiche einen Fehlerbericht ein!</string>
<string name="msg_mf_error_integrity">Interner Fehler, Integritätsprüfung fehlgeschlagen!</string>
- <string name="msg_mf_error_master_none">Keine Hauptbeglaubigung zum arbeiten gefunden! (Alle widerrufen?)</string>
- <string name="msg_mf_error_noexist_primary">Falsche primäre User-ID festgelegt!</string>
- <string name="msg_mf_error_noexist_revoke">Falsche User-ID für Widerruf festgelegt!</string>
- <string name="msg_mf_error_restricted">Es wurde versucht eine beschränkte Operation ohne Passwort auszuführen! Dies ist ein Programmierfehle, bitte reiche einen Fehlerbericht ein!</string>
+ <string name="msg_mf_error_master_none">Keine Hauptbeglaubigung zum damit Arbeiten gefunden! (Alle widerrufen?)</string>
+ <string name="msg_mf_error_noexist_primary">Falsche primäre User-ID spezifiziert!</string>
+ <string name="msg_mf_error_noexist_revoke">Falsche User-ID für Widerruf spezifiziert!</string>
+ <string name="msg_mf_error_restricted">Versuch einen eingeschränkten Vorgang ohne Passwort auszuführen! Dies ist ein Programmierfehler, bitte reiche einen Fehlerbericht ein!</string>
<string name="msg_mf_error_revoked_primary">Widerrufene User-IDs können nicht primäre IDs sein!</string>
<string name="msg_mf_error_null_expiry">Ablaufdatum kann bei Unterschlüsselerstellung nicht \"identisch wie vorher\" sein. Dies ist ein Programmierfehler, bitte reiche einen Fehlerbericht ein!</string>
+ <string name="msg_mf_error_noop">Nichts zu machen!</string>
<string name="msg_mf_error_passphrase_master">Schwerer Fehler beim Entschlüsseln des Hauptschlüssels! Dies ist wahrscheinlich ein Programmierfehler, bitte reiche einen Fehlerbericht ein!</string>
<string name="msg_mf_error_pgp">Interner OpenPGP Fehler!</string>
<string name="msg_mf_error_sig">Signaturfehler!</string>
- <string name="msg_mf_master">Verändere Hauptbeglaubigungen</string>
+ <string name="msg_mf_error_sub_stripped">Gekürzter Unterschlüssel %s kann nicht verändert werden!</string>
+ <string name="msg_mf_error_subkey_missing">Versuch mit fehlendem Unterschlüssel %s zu arbeiten!</string>
+ <string name="msg_mf_error_conflicting_nfc_commands">Der Schlüssel kann nicht im selben Vorgang auf die Smartcard verschoben werden, der auf der Smartcard eine Signatur erzeugt.</string>
+ <string name="msg_mf_error_duplicate_keytocard_for_slot">Smartcard unterstützt nur einen Slot pro Schlüsseltyp.</string>
+ <string name="msg_mf_error_invalid_flags_for_keytocard">Ungeeignete Schlüsselattribute für Schlüssel auf Smartcard.</string>
+ <string name="msg_mf_master">Hauptbeglaubigungen werden verändert</string>
<string name="msg_mf_notation_empty">Füge leeres Vermerk-Paket hinzu</string>
<string name="msg_mf_notation_pin">Füge PIN-Vermerk-Paket hinzu</string>
<string name="msg_mf_passphrase">Passwort für Schlüsselbund wird geändert</string>
+ <string name="msg_mf_pin">PIN auf Karte wird geändert</string>
+ <string name="msg_mf_admin_pin">Admin-PIN auf Karte wird geändert</string>
<string name="msg_mf_passphrase_key">Erneute Verschlüsselung des Unterschlüssels %s mit neuem Passwort</string>
- <string name="msg_mf_passphrase_empty_retry">Setzen des neuen Passworts fehlgeschlagen, versuche es nochmal mit leerem altem Passwort</string>
- <string name="msg_mf_passphrase_fail">Passwort für Unterschlüssel konnte nicht geändert werden! (Hat er ein anderes als die anderen Schlüssel?)</string>
+ <string name="msg_mf_passphrase_empty_retry">Festlegen eines neuen Passworts fehlgeschlagen, erneuter Versuch mit leerem altem Passwort</string>
+ <string name="msg_mf_passphrase_fail">Passwort des Unterschlüssels konnte nicht geändert werden! (Hat er ein anderes Passwort als die anderen Schlüssel?)</string>
<string name="msg_mf_primary_replace_old">Beglaubigung von vorheriger primärer User-ID wird ersetzt</string>
<string name="msg_mf_primary_new">Neue Beglaubigung für neue primäre User-ID wird erzeugt</string>
- <string name="msg_mf_subkey_change">Unterschlüssel %s wird geändert</string>
- <string name="msg_mf_error_subkey_missing">Versuch mit fehlendem Unterschlüssel %s zu arbeiten!</string>
+ <string name="msg_mf_restricted_mode">Wechsle zu eingeschränktem Vorgangsmodus</string>
+ <string name="msg_mf_subkey_change">Unterschlüssel %s wird verändert</string>
+ <string name="msg_mf_require_divert">Leite Kryptographische Vorgänge auf Smartcard um</string>
+ <string name="msg_mf_require_passphrase">Für die Vorgänge ist ein Passwort erforderlich</string>
<string name="msg_mf_subkey_new">Füge neuen Unterschlüssel vom Typ %s hinzu</string>
<string name="msg_mf_subkey_new_id">Neue Unterschlüsselkennung: %s</string>
<string name="msg_mf_error_past_expiry">Ablaufdatum kann nicht in der Vergangenheit liegen!</string>
<string name="msg_mf_subkey_revoke">Widerrufe Unterschlüssel %s</string>
<string name="msg_mf_subkey_strip">Kürze Unterschlüssel %s</string>
+ <string name="msg_mf_keytocard_start">Verschiebe Unterschlüssel %s auf Smartcard</string>
+ <string name="msg_mf_keytocard_finish">%1$s auf Smartcard %2$s verschoben</string>
<string name="msg_mf_success">Schlüsselbund erfolgreich verändert</string>
<string name="msg_mf_uid_add">User-ID %s wird hinzugefügt</string>
- <string name="msg_mf_uid_primary">Primäre User-ID wird geändert zu %s</string>
+ <string name="msg_mf_uid_primary">Primäre User-ID wird geändert in %s</string>
<string name="msg_mf_uid_revoke">Widerrufe User-ID %s</string>
<string name="msg_mf_uid_error_empty">User-ID darf nicht leer sein!</string>
<string name="msg_mf_uat_error_empty">Benutzerattribut darf nicht leer sein!</string>
- <string name="msg_mf_uat_add_image">Benutzerattribut vom Typ Bild wird hinzugefügt</string>
- <string name="msg_mf_uat_add_unknown">Benutzerattribut unbekannter Art wird hinzugefügt</string>
+ <string name="msg_mf_uat_add_image">Bild-Benutzerattribut wird hinzugefügt</string>
+ <string name="msg_mf_uat_add_unknown">Unbekannter Benutzerattributstyp wird hinzugefügt</string>
<string name="msg_mf_unlock_error">Fehler beim entsperren des Schlüsselbunds</string>
<string name="msg_mf_unlock">Schlüsselbund wird entsperrt</string>
<!--Consolidate-->
<string name="msg_con">Datenbank wird zusammengeführt</string>
- <string name="msg_con_error_bad_state">Zusammenführung wurde gestartet während keine Datenbank zwischengespeichert war! Dies ist wahrscheinlich ein Programmierfehler, bitte reiche einen Fehlerbericht ein!</string>
- <string name="msg_con_error_concurrent">Zusammenführung abgebrochen, läuft bereits auf einem anderen Thread!</string>
- <string name="msg_con_save_secret">Geheime Schlüsselbünde werden gespeichert</string>
+ <string name="msg_con_error_bad_state">Zusammenführung wurde gestartet, während keine Datenbank zwischengespeichert war! Dies ist wahrscheinlich ein Programmierfehler, bitte reiche einen Fehlerbericht ein!</string>
+ <string name="msg_con_error_concurrent">Zusammenführung abgebrochen, läuft bereits in einem anderen Thread!</string>
+ <string name="msg_con_save_secret">Private Schlüsselbünde werden gespeichert</string>
<string name="msg_con_save_public">Öffentliche Schlüsselbünde werden gespeichert</string>
<string name="msg_con_db_clear">Datenbank wird geleert</string>
<string name="msg_con_success">Datenbank erfolgreich zusammengeführt!</string>
<string name="msg_con_critical_in">Beginne kritische Phase!</string>
<string name="msg_con_critical_out">Verlasse kritische Phase</string>
- <string name="msg_con_delete_public">Lösche Cache-Datei des öffentlichen Schlüsselbundes</string>
- <string name="msg_con_delete_secret">Lösche Cache-Datei des geheimen Schlüsselbundes</string>
+ <string name="msg_con_delete_public">Lösche Zwischenspeicherdatei des öffentlichen Schlüsselbundes</string>
+ <string name="msg_con_delete_secret">Lösche Zwischenspeicherdatei des privaten Schlüsselbundes</string>
<string name="msg_con_error_db">Fehler bei der Öffnung der Datenbank!</string>
- <string name="msg_con_error_io_public">Ein-/Ausgabefehler beim Schreiben von öffentlichen Schlüsseln in den Cache!</string>
- <string name="msg_con_error_io_secret">Ein-/Ausgabefehler beim Schreiben von geheimen Schlüsseln in den Cache!</string>
- <string name="msg_con_error_public">Fehler beim reimportieren der öffentlichen Schlüssel!</string>
- <string name="msg_con_error_secret">Fehler beim reimportieren der geheimen Schlüssel!</string>
+ <string name="msg_con_error_io_public">Ein-/Ausgabefehler beim Schreiben von öffentlichen Schlüsseln in den Zwischenspeicher!</string>
+ <string name="msg_con_error_io_secret">Ein-/Ausgabefehler beim Schreiben von privaten Schlüsseln in den Zwischenspeicher!</string>
+ <string name="msg_con_error_public">Fehler beim Reimportieren der öffentlichen Schlüssel!</string>
+ <string name="msg_con_error_secret">Fehler beim reimportieren der privaten Schlüssel!</string>
<string name="msg_con_recover">Zusammenführungsvorgang wird fortgesetzt</string>
<string name="msg_con_recursive">Rekursive Zusammenführung wird übersprungen</string>
<string name="msg_con_recover_unknown">Zusammenführungsvorgang aus unbekanntem Zustand wird fortgesetzt</string>
@@ -884,34 +1013,36 @@
</plurals>
<string name="msg_con_reimport_public_skip">Keine öffentlichen Schlüssel für Reimport, überspringe...</string>
<plurals name="msg_con_reimport_secret">
- <item quantity="one">Reimportiere einen geheimen Schlüssel</item>
- <item quantity="other">Reimportiere %d geheime Schlüssel</item>
+ <item quantity="one">Reimportiere einen privaten Schlüssel</item>
+ <item quantity="other">Reimportiere %d private Schlüssel</item>
</plurals>
- <string name="msg_con_reimport_secret_skip">Keine geheimen Schlüssel für Reimport, überspringe...</string>
- <string name="msg_con_warn_delete_public">Fehler beim Löschen der Öffentlichen Cache-Datei</string>
- <string name="msg_con_warn_delete_secret">Fehler beim Löschen der Geheimen Cache-Datei</string>
+ <string name="msg_con_reimport_secret_skip">Keine privaten Schlüssel für Reimport, überspringe...</string>
+ <string name="msg_con_warn_delete_public">Ausnahmefehler beim Löschen der öffentlichen Zwischenspeicherdatei</string>
+ <string name="msg_con_warn_delete_secret">Ausnahmefehler beim Löschen der privaten Zwischenspeicherdatei</string>
<!--Edit Key (higher level than modify)-->
- <string name="msg_ed">Schlüsselvorgang wird durchgeführt</string>
+ <string name="msg_ed">Schlüsselvorgang wird ausgeführt</string>
<string name="msg_ed_caching_new">Neues Passwort wird zwischengespeichert</string>
<string name="msg_ed_error_no_parcel">\"SaveKeyringParcel\" fehlt! (Dies ist ein Fehler, bitte reiche einen Fehlerbericht ein!)</string>
<string name="msg_ed_error_key_not_found">Schlüssel nicht gefunden!</string>
- <string name="msg_ed_fetching">Zu ändernder Schlüssel wird abgerufen (%s)</string>
+ <string name="msg_ed_error_extract_public_upload">Fehler beim Extrahieren des öffentlichen Schlüssels fürs Hochladen!</string>
+ <string name="msg_ed_fetching">Zu bearbeitender Schlüssel wird abgerufen (%s)</string>
<string name="msg_ed_success">Schlüsselvorgang erfolgreich</string>
<!--Promote key-->
- <string name="msg_pr">Befördere öffentlichen Schlüssel zum geheimen Schlüssel</string>
- <string name="msg_pr_error_already_secret">Der Schlüssel ist bereits ein geheimer Schlüssel!</string>
+ <string name="msg_pr">Öffentlicher Schlüssel zu privatem Schlüssel hochgestuft</string>
+ <string name="msg_pr_all">Stufe alle Unterschlüssel hoch</string>
<string name="msg_pr_error_key_not_found">Schlüssel nicht gefunden!</string>
- <string name="msg_pr_fetching">Zu ändernder Schlüssel wird abgerufen (%s)</string>
- <string name="msg_pr_success">Schlüssel erfolgreich befördert</string>
+ <string name="msg_pr_fetching">Zu bearbeitender Schlüssel wird abgerufen (%s)</string>
+ <string name="msg_pr_subkey_match">Stufe Unterschlüssel hoch: %s</string>
+ <string name="msg_pr_subkey_nomatch">Unterschlüssel nicht auf YubiKey: %s</string>
+ <string name="msg_pr_success">Schlüssel erfolgreich hochgestuft</string>
<!--Other messages used in OperationLogs-->
- <string name="msg_ek_error_divert">Bearbeiten von NFC-Schlüsseln wird (noch) nicht unterstützt!</string>
- <string name="msg_ek_error_dummy">Kann Schlüsselbund nicht mit gekürztem Hauptschlüssel bearbeiten!</string>
+ <string name="msg_ek_error_dummy">Schlüsselbund mit gekürztem Hauptschlüssel kann nicht bearbeitet werden!</string>
<string name="msg_ek_error_not_found">Schlüssel nicht gefunden!</string>
<!--Messages for DecryptVerify operation-->
<string name="msg_dc_askip_no_key">Daten mit unbekanntem Schlüssel verschlüsselt, überspringe...</string>
<string name="msg_dc_askip_not_allowed">Daten mit nicht zugelassenem Schlüssel verschlüsselt, überspringe...</string>
<string name="msg_dc_asym">Block asymmetrisch verschlüsselter Daten für Schlüssel %s gefunden</string>
- <string name="msg_dc_charset">Ein Zeichensatz-Header wurde gefunden: \'%s\'</string>
+ <string name="msg_dc_charset">Zeichensatz-Header gefunden: \'%s\'</string>
<string name="msg_dc_clear_data">Verarbeite Klartextdaten</string>
<string name="msg_dc_clear_decompress">Entpacke komprimierte Daten</string>
<string name="msg_dc_clear_meta_file">Dateiname: %s</string>
@@ -920,35 +1051,39 @@
<string name="msg_dc_clear_meta_size_unknown">Dateigröße unbekannt</string>
<string name="msg_dc_clear_meta_time">Änderungszeit: %s</string>
<string name="msg_dc_clear_signature_bad">Signaturprüfung NICHT OK!</string>
- <string name="msg_dc_error_unsupported_hash_algo">Nicht unterstützter und potentiell unsicherer Hash-Algorithmus!</string>
- <string name="msg_dc_clear_signature_check">Überprüfe Signaturdaten</string>
+ <string name="msg_dc_clear_signature_check">Signaturdaten werden verifiziert</string>
<string name="msg_dc_clear_signature_ok">Signaturprüfung OK</string>
<string name="msg_dc_clear_signature">Speichere Signatur für später</string>
<string name="msg_dc_clear">Verarbeite Klartextdaten</string>
- <string name="msg_dc_error_bad_passphrase">Fehler bei Schlüsselentsperrung, falsches Passwort!</string>
+ <string name="msg_dc_error_bad_passphrase">Fehler beim Entsperren des Schlüssels, falsches Passwort!</string>
+ <string name="msg_dc_error_sym_passphrase">Fehler beim Entschlüsseln der Daten, falsche Passphrase?</string>
+ <string name="msg_dc_error_corrupt_data">Daten beschädigt!</string>
<string name="msg_dc_error_extract_key">Unbekannter Fehler bei Schlüsselentsperrung!</string>
<string name="msg_dc_error_integrity_check">Integritätsprüfungsfehler!</string>
- <string name="msg_dc_error_integrity_missing">Fehlende Integritätsprüfung Dies kann passieren, wenn die Verschlüsselungsanwendung veraltet ist oder durch einen Downgrade-Angriff.</string>
- <string name="msg_dc_error_invalid_siglist">Keine gültigen Signaturdaten gefunden!</string>
- <string name="msg_dc_error_io">Ein-/Ausgabefehler während Vorgang aufgetreten!</string>
- <string name="msg_dc_error_no_data">Keine verschlüsselten Daten in Datenstrom gefunden!</string>
- <string name="msg_dc_error_no_key">Keine verschlüsselten Daten mit bekanntem geheimen Schlüssel in Datenstrom gefunden!</string>
- <string name="msg_dc_error_pgp_exception">Es ist ein OpenPGP-Ausnahmefehler während des Vorgangs aufgetreten!</string>
+ <string name="msg_dc_error_invalid_data">Kein gültiger OpenPGP-verschlüsselter oder -signierter Inhalt gefunden!</string>
+ <string name="msg_dc_error_io">Beim Einlesen der Eingangsdaten ist ein Fehler aufgetreten!</string>
+ <string name="msg_dc_error_input">Fehler beim Öffnen des eingehenden Datenstroms!</string>
+ <string name="msg_dc_error_no_data">Keine verschlüsselten Daten im Datenstrom gefunden!</string>
+ <string name="msg_dc_error_no_key">Keine verschlüsselten Daten mit bekanntem privatem Schlüssel im Datenstrom gefunden!</string>
+ <string name="msg_dc_error_pgp_exception">Während eines Vorgangs ist ein OpenPGP-Ausnahmefehler aufgetreten!</string>
<string name="msg_dc_integrity_check_ok">Integritätsprüfung OK!</string>
<string name="msg_dc_ok_meta_only">Es wurden nur Metadaten angefragt, überspringe Entschlüsselung</string>
- <string name="msg_dc_ok">Entschlüsselung/Überprüfung abgeschlossen</string>
- <string name="msg_dc_pass_cached">Verwende Passwort aus Zwischenspeicher</string>
- <string name="msg_dc_pending_nfc">NFC-Token benötigt, verlange Benutzereingabe...</string>
- <string name="msg_dc_pending_passphrase">Passwort benötigt, verlange Benutezreingabe...</string>
- <string name="msg_dc_prep_streams">Bereite Datenkette </string>
+ <string name="msg_dc_ok">Entschlüsselung/Verifikation abgeschlossen</string>
+ <string name="msg_dc_pass_cached">Passwort aus Zwischenspeicher verwenden</string>
+ <string name="msg_dc_pending_nfc">NFC-Token benötigt, Benutzereingabe wird angefordert…</string>
+ <string name="msg_dc_pending_passphrase">Passwort erforderlich, Benutzereingabe wird angefordert…</string>
+ <string name="msg_dc_prep_streams">Bereite Datenströme zur Entschlüsselung vor</string>
<string name="msg_dc">Starte Entschlüsselungsvorgang...</string>
<string name="msg_dc_sym_skip">Symmetrische Daten nicht erlaubt, überspringe...</string>
<string name="msg_dc_sym">Block symmetrisch verschlüsselter Daten gefunden</string>
<string name="msg_dc_trail_asym">Anhängende, asymmetrisch verschlüsselte Daten für Schlüssel %s gefunden</string>
- <string name="msg_dc_trail_sym">Anhang gefunden, symmetrisch verschlüsselte Daten</string>
+ <string name="msg_dc_trail_sym">Anhängende, symmetrisch verschlüsselte Daten gefunden</string>
<string name="msg_dc_trail_unknown">Anhängende Daten unbekannter Art gefunden</string>
- <string name="msg_dc_unlocking">Geheimer Schlüssel wird entsperrt</string>
- <string name="msg_dc_old_symmetric_encryption_algo">Ein potentiell unsicherer Verschlüsselungsalgorithmus wurde verwendet!</string>
+ <string name="msg_dc_unlocking">Privater Schlüssel wird entsperrt</string>
+ <string name="msg_dc_insecure_symmetric_encryption_algo">Es wurde ein unsicherer Verschlüsselungsalgorithmus verwendet! Das kann vorkommen wenn die Anwendung veraltet ist, oder durch einen Angriff.</string>
+ <string name="msg_dc_insecure_hash_algo">Es wurde ein unsicherer Hash-Algorithmus verwendet! Das kann vorkommen wenn die Anwendung veraltet ist, oder durch einen Angriff.</string>
+ <string name="msg_dc_insecure_mdc_missing">Modifikationserkennungscode-Paket (engl. MDC packet) fehlt! Das kann passieren wenn die Verschlüsselungsanwendung veraltet ist, oder durch einen Zurückstufungsangriff.</string>
+ <string name="msg_dc_insecure_key">Unsicherer Schlüssel: Entweder ist die Bitlänge von RSA/DSA/ElGamal zu kurz oder die ECC-Kurve bzw. der ECC-Algorithmus wird als unsicher angesehen! Das kann vorkommen wenn die Anwendung veraltet ist, oder durch einen Angriff.</string>
<!--Messages for VerifySignedLiteralData operation-->
<string name="msg_vl">Starte Signaturprüfung</string>
<string name="msg_vl_error_no_siglist">Keine Signaturliste in signierten Literaldaten</string>
@@ -958,40 +1093,38 @@
<string name="msg_vl_clear_meta_mime">MIME-Typ: %s</string>
<string name="msg_vl_clear_meta_time">Änderungszeit: %s</string>
<string name="msg_vl_clear_meta_size">Dateigröße: %s</string>
- <string name="msg_vl_clear_signature_check">Überprüfe Signaturdaten</string>
+ <string name="msg_vl_clear_signature_check">Signaturdaten werden verifiziert</string>
<string name="msg_vl_error_integrity_check">Integritätsprüfung fehlgeschlagen!</string>
<string name="msg_vl_ok">OK</string>
<!--Messages for SignEncrypt operation-->
<string name="msg_se">Starte Signier-/Verschlüsselungsvorgang</string>
<string name="msg_se_input_bytes">Verarbeite Eingabe aus Bytearray</string>
- <string name="msg_se_input_uri">Verarbeite Eingabe von URI</string>
- <string name="msg_se_error_no_input">Keine Eingabe gegeben!</string>
- <string name="msg_se_error_input_uri_not_found">Fehler beim öffnen der URI zum lesen!</string>
- <string name="msg_se_error_output_uri_not_found">Fehler beim öffnen der URI zum schreiben!</string>
- <string name="msg_se_error_too_many_inputs">Mehr Eingaben als Ausgaben angegeben! Dies ist vermutlich ein Programmierfehler, diesen bitte melden!</string>
- <string name="msg_se_warn_output_left">Es sind Ausgaben, aber keine Eingaben mehr übrig! Dies ist vermutlich ein Programmierfehler, diesen bitte melden!</string>
- <string name="msg_se_success">Signier-/Verschlüsselungsvorgang erfolgreich!</string>
+ <string name="msg_se_input_uri">Verarbeite Eingabe aus URI</string>
+ <string name="msg_se_error_no_input">Keine Eingabe vorhanden!</string>
+ <string name="msg_se_error_input_uri_not_found">Fehler beim Öffnen des URI zum Lesen!</string>
+ <string name="msg_se_error_output_uri_not_found">Fehler beim Öffnen des URI zum Schreiben!</string>
+ <string name="msg_se_error_too_many_inputs">Mehr Eingaben als Ausgaben spezifiziert! Dies ist vermutlich ein Programmierfehler, diesen bitte melden!</string>
+ <string name="msg_se_success">Signier-/Verschlüsselungsvorgang erfolgreich</string>
<!--Messages for PgpSignEncrypt operation-->
<string name="msg_pse_asymmetric">Bereite öffentliche Schlüssel für Verschlüsselung vor</string>
<string name="msg_pse_clearsign_only">Signieren von Klartexteingaben wird nicht unterstützt!</string>
<string name="msg_pse_compressing">Bereite Kompression vor</string>
<string name="msg_pse_encrypting">Daten werden verschlüsselt</string>
<string name="msg_pse_error_bad_passphrase">Falsches Passwort!</string>
- <string name="msg_pse_error_hash_algo">Angeforderter Hash-Algorithmus wird von diesem Schlüssel nicht unterstützt!</string>
- <string name="msg_pse_error_io">Ein-/Ausgabefehler während Vorgang aufgetreten!</string>
- <string name="msg_pse_error_key_sign">Gewählter Signaturschlüssel kann keine Daten signieren!</string>
+ <string name="msg_pse_error_io">Während eines Vorgangs ist ein Ein-/Ausgabefehler aufgetreten!</string>
+ <string name="msg_pse_error_key_sign">Ausgewählter Signaturschlüssel kann keine Daten signieren!</string>
<string name="msg_pse_error_sign_key">Fehler bei Abruf des Signaturschlüssels!</string>
<string name="msg_pse_error_nfc">NFC-Datenfehler!</string>
<string name="msg_pse_error_no_passphrase">Kein Passwort angegeben!</string>
<string name="msg_pse_error_pgp">Interner OpenPGP Fehler!</string>
- <string name="msg_pse_error_sig">OpenPGP-Signaturfehler aufgetreten!</string>
+ <string name="msg_pse_error_sig">Während eines Vorgang ist ein OpenPGP-Signatur-Ausnahmefehler aufgetreten!</string>
<string name="msg_pse_error_unlock">Unbekannter Fehler bei Schlüsselentsperrung!</string>
<string name="msg_pse_key_ok">Verschlüssele für Schlüssel: %s</string>
<string name="msg_pse_key_unknown">Fehlender Schlüssel für Verschlüsselung: %s</string>
- <string name="msg_pse_key_warn">Falscher Schlüssel für Verschlüsselung: %s</string>
+ <string name="msg_pse_key_warn">Fehlerhafter Schlüssel für Verschlüsselung: %s</string>
<string name="msg_pse_ok">Signierungs-/Verschlüsselungsvorgang erfolgreich!</string>
- <string name="msg_pse_pending_nfc">NFC-Token benötigt, verlange Benutzereingabe...</string>
- <string name="msg_pse_pending_passphrase">Passwort benötigt, verlange Benutzereingabe...</string>
+ <string name="msg_pse_pending_nfc">NFC-Token benötigt, Benutzereingabe wird angefordert…</string>
+ <string name="msg_pse_pending_passphrase">Passwort erforderlich, Benutzereingabe wird angefordert…</string>
<string name="msg_pse_signing">Signiere Daten (ohne Verschlüsselung)</string>
<string name="msg_pse_signing_cleartext">Erstelle Klartextsignatur</string>
<string name="msg_pse_signing_detached">Abgetrennte Signatur wird erstellt</string>
@@ -1011,16 +1144,17 @@
<string name="msg_crt_error_master_not_found">Hauptschlüssel nicht gefunden!</string>
<string name="msg_crt_error_nothing">Keine beglaubigten Schlüssel!</string>
<string name="msg_crt_error_unlock">Fehler beim Entsperren des Hauptschlüssels!</string>
- <string name="msg_crt_error_divert">Beglaubigung über NFC wird (noch) nicht unterstützt!</string>
<string name="msg_crt">Schlüsselbünde werden beglaubigt</string>
<string name="msg_crt_master_fetch">Beglaubigender Hauptschlüssel wird abgerfufen</string>
+ <string name="msg_crt_nfc_return">Kehre zu NFC-Bildschirm zurück</string>
<string name="msg_crt_save">Beglaubigter Schlüssel %s wird gespeichert</string>
<string name="msg_crt_saving">Schlüsselbünde werden gespeichert</string>
<string name="msg_crt_unlock">Hauptschlüssel wird entsperrt</string>
<string name="msg_crt_success">Identitäten erfolgreich beglaubigt</string>
<string name="msg_crt_warn_not_found">Schlüssel nicht gefunden!</string>
<string name="msg_crt_warn_cert_failed">Erzeugen der Beglaubigung fehlgeschlagen!</string>
- <string name="msg_crt_warn_save_failed">Speichern fehlgeschlagen!</string>
+ <string name="msg_crt_warn_save_failed">Speichervorgang fehlgeschlagen!</string>
+ <string name="msg_crt_warn_upload_failed">Hochladevorgang fehlgeschlagen!</string>
<string name="msg_crt_upload_success">Schlüssel wurde erfolgreich hochgeladen</string>
<plurals name="msg_import">
<item quantity="one">Schlüssel wird importiert</item>
@@ -1029,42 +1163,47 @@
<string name="msg_import_fetch_error_decode">Fehler beim Dekodieren des abgerufenen Schlüsselbundes!</string>
<string name="msg_import_fetch_error">Schlüssel konnte nicht abgerufen werden! (Netzwerkprobleme?)</string>
<string name="msg_import_fetch_keybase">Empfange von keybase.io: %s</string>
- <string name="msg_import_fetch_keyserver_error">Konnte Schlüssel nicht von Keybase empfangen!</string>
- <string name="msg_import_fetch_keyserver">Von einem Schlüsselserver abrufen: %s</string>
- <string name="msg_import_fetch_keyserver_ok">Schlüsselabruf erfolgreich!</string>
- <string name="msg_import_keyserver">Schlüsselserver %s wird verwendet</string>
+ <string name="msg_import_fetch_keyserver_error">Konnte Schlüssel nicht vom Schlüsselserver abrufen: %s</string>
+ <string name="msg_import_fetch_keyserver">Empfange von Schlüsselserver: %s</string>
+ <string name="msg_import_fetch_keyserver_ok">Schlüssel erfolgreich heruntergeladen</string>
+ <string name="msg_import_keyserver">Verwende Schlüsselserver %s</string>
<string name="msg_import_fingerprint_error">Fingerabdruck des abgerufenen Schlüssels entspricht nicht dem Erwarteten!</string>
<string name="msg_import_fingerprint_ok">Fingerabdrucktest in Ordnung</string>
<string name="msg_import_merge">Abgerufene Daten werden eingefügt</string>
- <string name="msg_import_error">Importieren fehlgeschlagen!</string>
- <string name="msg_import_error_io">Der Import ist aufgrund eines Eingabe/Ausgabe-Fehlers fehlgeschlagen!</string>
+ <string name="msg_import_merge_error">Fehler beim Zusammenführen der abgerufenen Daten!</string>
+ <string name="msg_import_error">Importvorgang fehlgeschlagen!</string>
+ <string name="msg_import_error_io">Importvorgang ist aufgrund eines Ein-/Ausgabefehlers fehlgeschlagen!</string>
<string name="msg_import_partial">Importvorgang erfolgreich, mit Fehlern!</string>
<string name="msg_import_success">Importvorgang erfolgreich!</string>
<plurals name="msg_export">
- <item quantity="one">Ein Schlüssel wird exportiert</item>
+ <item quantity="one">Schlüssel wird exportiert</item>
<item quantity="other">%d Schlüssel werden exportiert</item>
</plurals>
+ <string name="msg_export_file_name">Dateiname: %s</string>
<string name="msg_export_all">Exportiere alle Schlüssel</string>
<string name="msg_export_public">Exportiere öffentlichen Schlüssel %s</string>
+ <string name="msg_export_upload_public">Öffentlicher Schlüssel %s wird hochgeladen</string>
<string name="msg_export_secret">Exportiere privaten Schlüssel %s</string>
- <string name="msg_export_error_no_file">Kein Dateiname angegeben!</string>
+ <string name="msg_export_error_no_file">Kein Dateiname spezifiziert!</string>
<string name="msg_export_error_fopen">Fehler beim Öffnen der Datei !</string>
- <string name="msg_export_error_no_uri">Keine URI angegeben!</string>
+ <string name="msg_export_error_no_uri">Keine URI spezifiziert!</string>
<string name="msg_export_error_uri_open">Fehler beim Öffnen des URI-Streams!</string>
<string name="msg_export_error_storage">Speicher ist nicht Schreibbereit !</string>
<string name="msg_export_error_db">Datenbankfehler!</string>
<string name="msg_export_error_io">Eingabe/Ausgabe Fehler!</string>
<string name="msg_export_error_key">Fehlber bei der Vorverarbeitung der Schlüsseldaten!</string>
+ <string name="msg_export_error_upload">Fehler beim Hochladen des Schlüssels zum Server! Bitte überprüfe deine Internetverbindung</string>
<string name="msg_export_success">Exportvorgang erfolgreich!</string>
+ <string name="msg_export_upload_success">Hochladen auf Schlüsselserver erfolgreich</string>
<string name="msg_del_error_empty">Nichts zu löschen!</string>
- <string name="msg_del_error_multi_secret">Geheime Schlüssel können nur einzeln gelöscht werden!</string>
+ <string name="msg_del_error_multi_secret">Private Schlüssel können nur einzeln gelöscht werden!</string>
<plurals name="msg_del">
<item quantity="one">Schlüssel wird gelöscht</item>
<item quantity="other">%d Schlüssel werden gelöscht</item>
</plurals>
<string name="msg_del_key">Lösche Schlüssel %s</string>
<string name="msg_del_key_fail">Fehler beim Löschen von Schlüssel %s</string>
- <string name="msg_del_consolidate">Datenbank wird nach Löschung des geheimen Schlüssels zusammengeführt</string>
+ <string name="msg_del_consolidate">Datenbank wird nach Löschung des privaten Schlüssels zusammengeführt</string>
<plurals name="msg_del_ok">
<item quantity="one">Schlüssel erfolgreich gelöscht</item>
<item quantity="other">%d Schlüssel erfolgreich gelöscht</item>
@@ -1073,36 +1212,63 @@
<item quantity="one">Fehler beim Löschen eines Schlüssels</item>
<item quantity="other">Fehler beim Löschen von %d Schlüsseln</item>
</plurals>
+ <string name="msg_revoke_error_empty">Nichts zu widerrufen!</string>
+ <string name="msg_revoke_error_not_found">Schlüssel zum Widerrufen nicht gefunden!</string>
+ <string name="msg_revoke_key">Widerrufe Schlüssel %s</string>
+ <string name="msg_revoke_key_fail">Fehler beim Widerrufen des Schlüssels</string>
+ <string name="msg_revoke_ok">Schlüssel erfolgreich widerrufen</string>
<string name="msg_acc_saved">Benutzerkonto gespeichert</string>
<string name="msg_download_success">Erfolgreich heruntergeladen!</string>
<string name="msg_download_no_valid_keys">Keine gültigen Schlüssel in der Datei/Zwischenablage gefunden!</string>
<string name="msg_download_no_pgp_parts">NOCH ZU MACHEN: Plurale!</string>
<plurals name="error_import_non_pgp_part">
- <item quantity="one">Ein Teil der geladenen Datei ist ein gültiges OpenPGP Objekt aber kein OpenPGP Schlüssel</item>
- <item quantity="other">Teile der geladenen Dateien sind gültige OpenPGP Objekte aber keine OpenPGP Schlüssel</item>
+ <item quantity="one">ein Teil der geladenen Datei ist ein gültiges OpenPGP-Objekt, aber kein OpenPGP-Schlüssel</item>
+ <item quantity="other">Teile der geladenen Dateien sind gültige OpenPGP-Objekte, aber keine OpenPGP-Schlüssel</item>
</plurals>
<string name="msg_download_query_too_short">Die Suchanfrage ist zu kurz, bitte die Suchanfrage verfeinern!</string>
<string name="msg_download_too_many_responses">Schlüsselsuchanfrage lieferte zu viele Kandidaten, bitte die Suchanfrage verfeinern!</string>
<string name="msg_download_query_too_short_or_too_many_responses">Entweder keine oder zu viele Schlüssel wurden gefunden, bitte die Suchanfrage prä­zi­sie­ren!</string>
<string name="msg_download_query_failed">Beim Suchen der Schlüssel ist ein Fehler aufgetreten.</string>
+ <!--Messages for Keybase Verification operation-->
+ <string name="msg_keybase_verification">Versuche Keybase-Verifikation für %s</string>
+ <string name="msg_keybase_error_no_prover">Kein Nachweis-Prüfer gefunden für %s</string>
+ <string name="msg_keybase_error_fetching_evidence">Problem beim Holen des Nachweises</string>
+ <string name="msg_keybase_error_key_mismatch">Schlüssel-Fingerabdruck stimmt nicht mit dem Fingerabdruck im Nachweis überein</string>
+ <string name="msg_keybase_error_dns_fail">Abfrage des DNS-TXT-Eintrags fehlgeschlagen</string>
+ <string name="msg_keybase_error_specific">%s</string>
+ <string name="msg_keybase_error_msg_payload_mismatch">Entschlüsselter Nachweis entspricht nicht dem erwarteten Wert</string>
<!--Messages for Export Log operation-->
<string name="msg_export_log_start">Exportiere Protokoll</string>
<string name="msg_export_log_error_fopen">Fehler beim Öffnen der Datei</string>
- <string name="msg_export_log_error_no_file">Kein Dateiname angegeben!</string>
- <string name="msg_export_log_error_writing">Ein-/Ausgabefehler beim schreiben in die Datei!</string>
+ <string name="msg_export_log_error_no_file">Kein Dateiname spezifiziert!</string>
+ <string name="msg_export_log_error_writing">Ein-/Ausgabefehler beim Schreiben in die Datei!</string>
<string name="msg_export_log_success">Protokoll erfolgreich exportiert!</string>
<!--PassphraseCache-->
- <string name="passp_cache_notif_click_to_clear">Klicken um Passworte aus Zwischenspeicher zu löschen</string>
- <string name="passp_cache_notif_n_keys">OpenKeychain hat %d Passworte zwischengespeichert</string>
- <string name="passp_cache_notif_keys">Zwischengespeicherte Passworte:</string>
- <string name="passp_cache_notif_clear">Cache löschen</string>
+ <string name="passp_cache_notif_click_to_clear">Passwörter löschen</string>
+ <plurals name="passp_cache_notif_n_keys">
+ <item quantity="one">%d Passwort gemerkt</item>
+ <item quantity="other">%d Passwörter gemerkt</item>
+ </plurals>
+ <string name="passp_cache_notif_keys">Gemerkte Passwörter</string>
+ <string name="passp_cache_notif_clear">Passwörter vergessen</string>
<string name="passp_cache_notif_pwd">Passwort</string>
+ <!--Keyserver sync-->
+ <string name="keyserver_sync_orbot_notif_title">Synchronisierung aus der Cloud erfordert Orbot</string>
+ <string name="keyserver_sync_orbot_notif_msg">Zum Starten von Orbot tippen</string>
+ <string name="keyserver_sync_orbot_notif_start">Orbot starten</string>
+ <string name="keyserver_sync_orbot_notif_ignore">Direkt</string>
<!--First Time-->
<string name="first_time_text1">Hol dir deine Privatsphäre mit OpenKeychain zurück!</string>
<string name="first_time_create_key">Meinen Schlüssel erzeugen</string>
<string name="first_time_import_key">Schlüssel aus Datei importieren</string>
<string name="first_time_yubikey">YubiKey NEO verwenden</string>
<string name="first_time_skip">Setup überspringen</string>
+ <string name="first_time_blank_yubikey">Möchtest du diesen leeren YubiKey NEO mit OpenKeychain verwenden?\n\nBitte nimm den YubiKey nun weg, du wirst aufgefordert, wenn er erneut benötigt wird!</string>
+ <string name="first_time_blank_yubikey_yes">Diesen YubiKey verwenden</string>
+ <string name="backup_text">Backups, die deine eigenen Schlüssel beinhalten, dürfen unter keinen Umständen an anderen Personen gegeben werden.</string>
+ <string name="backup_all">Alle Schlüssel + deine eigenen Schlüssel</string>
+ <string name="backup_public_keys">Alle Schlüssel</string>
+ <string name="backup_section">Backup</string>
<!--unsorted-->
<string name="section_certifier_id">Beglaubiger</string>
<string name="section_cert">Beglaubigungsdetails</string>
@@ -1111,7 +1277,7 @@
<string name="empty_certs">Keine Beglaubigungen für diesen Schlüssel</string>
<string name="certs_text">Nur geprüfte Eigenbeglaubigungen und geprüfte Beglaubigungen, die mit deinen Schlüsseln erzeugt wurden, werden hier angezeigt.</string>
<string name="section_uids_to_certify">Identitäten für</string>
- <string name="certify_text">Die Schlüssel, die importiert werden, enthalten \"Identitäten\": Namen und E-Mail-Adressen. Wähle genau diejenigen zum Bestätigen aus, die deinen Erwartungen entsprechen.</string>
+ <string name="certify_text">Die zu importierenden Schlüssel enthalten \"Identitäten\": Namen und E-Mail-Adressen. Wähle genau diejenigen zum Bestätigen aus, die deinen Erwartungen entsprechen.</string>
<string name="certify_fingerprint_text">Vergleiche den angezeigten Fingerabdruck zeichenweise mit dem, der auf dem Gerät deines Gegenübers angezeigt wird.</string>
<string name="certify_fingerprint_text2">Stimmen die angezeigten Fingerabdrücke überein?</string>
<string name="label_revocation">Widerrufsgrund</string>
@@ -1119,47 +1285,111 @@
<string name="error_key_not_found">Schlüssel nicht gefunden!</string>
<string name="error_key_processing">Fehler bei der Verarbeitung des Schlüssels!</string>
<string name="key_stripped">nicht verfügbar</string>
- <string name="key_divert">Umgeleitet zu SmartCard/NFC</string>
+ <string name="key_divert">auf Smartcard umleiten</string>
<string name="key_no_passphrase">kein Passwort</string>
<string name="key_unavailable">nicht verfügbar</string>
<string name="secret_cannot_multiple">Deine eigenen Schlüssel können nur einzeln gelöscht werden!</string>
<string name="title_view_cert">Beglaubigungsdetails anzeigen</string>
<string name="unknown_algorithm">unbekannt</string>
- <string name="can_sign_not">Kann nicht unterschreiben</string>
+ <string name="can_sign_not">kann nicht signieren</string>
<string name="error_no_encrypt_subkey">Kein Unterschlüssel zum Verschlüsseln verfügbar!</string>
- <string name="info_no_manual_account_creation">Erstelle OpenKeychain-Benutzerkonten nicht manuell.\nFür mehr Informationen sieh in die Hilfe.</string>
<string name="contact_show_key">Schlüssel anzeigen (%s)</string>
- <string name="swipe_to_update">Nach unten wischen um von Schlüsselserver zu aktualisieren</string>
+ <string name="swipe_to_update">Nach unten wischen, um vom Schlüsselserver zu aktualisieren</string>
<string name="error_no_file_selected">Mindestens eine Datei zum Verschlüsseln auswählen!</string>
- <string name="error_multi_not_supported">Das speichern von mehreren Dateien wird nicht unterstützt. Dies ist eine Einschränkung der aktuellen Android Version.</string>
+ <string name="error_multi_files">Das speichern von mehreren Dateien wird nicht unterstützt. Dies ist eine Einschränkung der aktuellen Android Version.</string>
+ <string name="error_multi_clipboard">Verschlüsselung mehrerer Dateien in die Zwischenablage wird nicht unterstützt.</string>
+ <string name="error_detached_signature">Nur-signieren-Vorgang von Binärdateien wird nicht unterstützt, bitte mindestens einen Verschlüsselungsschlüssel auswählen.</string>
+ <string name="error_empty_text">Zu verschlüsselnden Text eingeben</string>
<string name="key_colon">Schlüssel:</string>
<string name="exchange_description">Um einen Schlüsselaustausch zu starten wähle auf der rechten Seite die Teilnehmer aus, drücke dann den \"Austausch starten\"-Knopf.\n\nDu wirst zusätzlich zwei Fragen gestellt bekommen um sicherzustellen, dass nur die richtigen Teilnehmer am Austausch beteiligt sind und deren Fingerabdrücke korrekt sind.</string>
<string name="btn_start_exchange">Austausch starten</string>
<string name="user_id_none"><![CDATA[<kein>]]></string>
+ <!--Android Account-->
+ <string name="account_no_manual_account_creation">OpenKeychain-Benutzerkonten können nicht manuell angelegt werden.</string>
+ <string name="account_privacy_title">Datenschutz</string>
+ <string name="account_privacy_text">OpenKeychain synchronisiert deine Kontakte nicht mit dem Internet. Es verknüpft lediglich Kontakte mit Schlüsseln auf der Basis von Namen und E-Mail-Adressen. Das alles findet offline auf deinem Gerät statt.</string>
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
<string name="title_unlock_method">Wähle eine Entsperrmethode</string>
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<string name="enter_passphrase">Passwort eingeben</string>
<string name="passphrase">Passwort</string>
<string name="noPassphrase">Kein Passwort</string>
- <string name="no_passphrase_set">Kein Passwort gesetzt</string>
- <string name="passphrases_match">Die Passwörter stimmten nicht überein</string>
+ <string name="no_passphrase_set">Kein Passwort festgelegt</string>
+ <string name="passphrases_match">Passwörter stimmen überein</string>
<string name="passphrase_saved">Passwort gespeichert</string>
- <string name="passphrase_invalid">Falsches Passwort</string>
+ <string name="passphrase_invalid">Passwort ungültig</string>
<string name="missing_passphrase">Fehlendes Passwort</string>
<string name="passphrase_again">Wiederholen</string>
<string name="lockpattern">Entsperrcode</string>
<string name="lockpatternNFC">NFC + Entsperrcode</string>
<string name="unlock_method">Entsperrmethode</string>
- <string name="set_passphrase">Passwort setzen</string>
+ <string name="set_passphrase">Passwort festlegen</string>
<string name="draw_lockpattern">Zeichne Entsperrcode</string>
<string name="nfc_title">NFC</string>
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
- <string name="nfc_wrong_tag">Falscher Tag. Bitte versuche es erneut.</string>
+ <string name="nfc_wrong_tag">Falscher Tag, bitte versuche es erneut.</string>
<string name="enable_nfc">Bitte aktiviere NFC in deinen Einstellungen.</string>
<string name="no_nfc_support">Dieses Gerät unterstützt kein NFC</string>
<string name="nfc_write_succesful">Erfolgreich auf den NFC-Tag geschrieben</string>
<string name="unlocked">Entsperrt</string>
<string name="nfc_settings">Einstellungen</string>
+ <string name="snack_yubikey_view">Ansicht</string>
+ <string name="snack_yubikey_import">Import</string>
+ <string name="button_bind_key">Schlüssel verbinden</string>
+ <string name="yubikey_serno">Seriennummer: %s</string>
+ <string name="yubikey_key_holder">Schlüsselinhaber:</string>
+ <string name="yubikey_key_holder_not_set">Schlüsselnhaber: &lt;nicht gesetzt&gt;</string>
+ <string name="yubikey_status_bound">YubiKey stimmt überein und ist mit dem Schlüssel gekoppelt</string>
+ <string name="yubikey_status_unbound">YubiKey stimmt überein und kann mit dem Schlüssel gekoppelt werden</string>
+ <string name="yubikey_status_partly">YubiKey stimmt überein und ist teilweise mit dem Schlüssel gekoppelt</string>
+ <string name="yubikey_create">Halte den YubiKey gegen die Rückseite deines Geräts.</string>
+ <string name="btn_import">Import</string>
+ <string name="snack_yubi_other">Anderer Schlüssel auf YubiKey gespeichert!</string>
+ <string name="error_nfc">NFC-Fehler: %s</string>
+ <plurals name="error_pin">
+ <item quantity="one">PIN falsch!\n%d Versuch verbleibend.</item>
+ <item quantity="other">PIN falsch!\n%d Versuche verbleibend.</item>
+ </plurals>
+ <string name="error_nfc_terminated">YubiKey befindet sich in beendetem Zustand.</string>
+ <string name="error_nfc_wrong_length">Eingegebene PIN zu kurz. PINs müssen aus mindestens 6 Ziffern bestehen.</string>
+ <string name="error_nfc_conditions_not_satisfied">Nutzungsbedingungen werden nicht erfüllt. </string>
+ <string name="error_nfc_security_not_satisfied">Sicherheitsstatus nicht erfüllt.</string>
+ <string name="error_nfc_authentication_blocked">PIN nach zu vielen Versuchen gesperrt.</string>
+ <string name="error_nfc_data_not_found">Schlüssel oder Objekt nicht gefunden.</string>
+ <string name="error_nfc_unknown">Unbekannter Fehler</string>
+ <string name="error_nfc_bad_data">YubiKey meldete ungültige Daten.</string>
+ <string name="error_nfc_chaining_error">YubiKey erwartete das letzte Kommando in einer Kette.</string>
+ <string name="error_nfc_header">YubiKey meldete ungültige %s Byte.</string>
+ <string name="error_nfc_tag_lost">YubiKey wurde zu früh abgenommen. Halte den YubiKey an die Rückseite, bis der Vorgang beendet ist.</string>
+ <string name="error_nfc_try_again">Erneut versuchen</string>
+ <string name="error_pin_nodefault">Standard-PIN abgelehnt</string>
+ <string name="error_temp_file">Erstellen der temporären Datei fehlgeschlagen.</string>
+ <string name="btn_delete_original">Originaldatei löschen</string>
+ <string name="snack_encrypt_filenames_on">Dateinamen <b>sind</b> verschlüsselt.</string>
+ <string name="snack_encrypt_filenames_off">Dateinamen <b>sind nicht</b> verschlüsselt.</string>
+ <string name="snack_armor_on">Ausgabe als Text kodiert.</string>
+ <string name="snack_armor_off">Ausgabe als Binärdatei kodiert.</string>
+ <string name="snack_compression_on">Komprimierung <b>aktiviert</b>.</string>
+ <string name="snack_compression_off">Komprimierung <b>deaktiviert</b>.</string>
+ <string name="error_loading_keys">Fehler beim Laden der Schlüssel!</string>
+ <string name="error_empty_log">(Fehler, Protokoll leer)</string>
+ <string name="error_reading_text">Konnte Eingabe zur Entschlüsselung nicht lesen!</string>
+ <string name="filename_unknown">&lt;kein Dateiname&gt;</string>
+ <string name="filename_unknown_text">&lt;Klartextdaten&gt;</string>
+ <string name="intent_show">Signierten/verschlüsselten Inhalt anzeigen</string>
+ <string name="view_internal">In OpenKeychain ansehen</string>
+ <string name="error_preparing_data">Fehler beim Vorbereiten der Daten!</string>
+ <string name="label_clip_title">Verschlüsselte Daten</string>
+ <string name="progress_processing">Wird verarbeitet...</string>
+ <string name="error_saving_file">Fehler beim Speichern der Datei!</string>
+ <string name="file_saved">Datei gespeichert!</string>
+ <string name="file_delete_ok">Originaldatei gelöscht.</string>
+ <string name="file_delete_none">Keine Datei gelöscht! (bereits gelöscht?)</string>
+ <string name="file_delete_exception">Originaldatei konnte nicht gelöscht werden!</string>
+ <string name="error_clipboard_empty">Zwischenablage ist leer!</string>
+ <string name="error_clipboard_copy">Fehler beim Kopieren der Daten in die Zwischenablage!</string>
+ <string name="error_scan_fp">Fehler beim Scannen des Fingerabdrucks!</string>
+ <string name="error_scan_match">Fingerabdrücke stimmten nicht überein!</string>
+ <string name="error_expiry_past">Ablaufdatum liegt in der Vergangenheit!</string>
</resources>
diff --git a/OpenKeychain/src/main/res/values-es/strings.xml b/OpenKeychain/src/main/res/values-es/strings.xml
index 2d94eb6a2..7ace8f3dd 100644
--- a/OpenKeychain/src/main/res/values-es/strings.xml
+++ b/OpenKeychain/src/main/res/values-es/strings.xml
@@ -7,13 +7,12 @@
<string name="title_encrypt_text">Encriptar</string>
<string name="title_encrypt_files">Encriptar</string>
<string name="title_decrypt">Descifrar</string>
- <string name="title_unlock">Desbloquear clave</string>
<string name="title_add_subkey">Añadir subclave</string>
<string name="title_edit_key"> Editar clave</string>
<string name="title_preferences">Configuración</string>
<string name="title_api_registered_apps">Aplicaciones</string>
- <string name="title_key_server_preference">Servidores de claves</string>
- <string name="title_change_passphrase">Cambiar frase de contraseña</string>
+ <string name="title_key_server_preference">Servidores de claves OpenPGP</string>
+ <string name="title_change_passphrase">Cambiar contraseña</string>
<string name="title_share_fingerprint_with">Compartir huella digital con...</string>
<string name="title_share_key">Compartir clave con...</string>
<string name="title_share_file">Compartir archivo con...</string>
@@ -21,8 +20,8 @@
<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_export_key">Hacer copia de seguridad de la clave</string>
+ <string name="title_export_keys">Hacer copia de seguridad de las claves</string>
<string name="title_key_not_found">Clave no encontrada</string>
<string name="title_send_key">Cargar al servidor de claves</string>
<string name="title_certify_key">Confirmar clave</string>
@@ -30,33 +29,39 @@
<string name="title_help">Ayuda</string>
<string name="title_log_display">Registro (log)</string>
<string name="title_exchange_keys">Intercambiar claves</string>
- <string name="title_advanced_key_info">Información avanzada de clave</string>
+ <string name="title_advanced_key_info">Información extendida</string>
<string name="title_delete_secret_key">¿Borrar clave \'%s\'?</string>
<string name="title_export_log">Exportar registro (log)</string>
<string name="title_manage_my_keys">Administrar mis claves</string>
<!--section-->
<string name="section_user_ids">Identificaciones</string>
+ <string name="section_yubikey">YubiKey</string>
<string name="section_linked_system_contact">Contacto de sistema enlazado</string>
<string name="section_should_you_trust">¿Debe confiar en esta clave?</string>
<string name="section_proof_details">Comprobante de verificación</string>
<string name="section_cloud_evidence">Comprobantes desde la nube</string>
<string name="section_keys">Subclaves</string>
<string name="section_cloud_search">Búsqueda en la nube</string>
- <string name="section_passphrase_cache">Caché de frase-contraseña</string>
+ <string name="section_passphrase_cache">Manejo de contraseña/PIN</string>
+ <string name="section_proxy_settings">Configuración para proxy</string>
+ <string name="section_gui">Interfaz</string>
+ <string name="section_sync_settings">Sincronizar configuración</string>
<string name="section_certify">Confirmar</string>
<string name="section_actions">Acciones</string>
<string name="section_share_key">Clave</string>
<string name="section_key_server">Servidor de claves</string>
<string name="section_fingerprint">Huella digital</string>
<string name="section_encrypt">Encriptar</string>
- <string name="section_decrypt">Desencriptar</string>
+ <string name="section_decrypt">Descifrar / Verificar</string>
<string name="section_current_expiry">Caducidad actual</string>
<string name="section_new_expiry">Nueva caducidad</string>
<!--button-->
<string name="btn_decrypt_verify_file">Desencriptar, verificar, y guardar fichero</string>
<string name="btn_encrypt_share_file">Encriptar y compartir fichero</string>
<string name="btn_encrypt_save_file">Encriptar y guardar fichero</string>
+ <string name="btn_save_file">Guardar fichero</string>
<string name="btn_save">Guardar</string>
+ <string name="btn_view_log">Ver registro (log)</string>
<string name="btn_do_not_save">Cancelar</string>
<string name="btn_delete">Eliminar</string>
<string name="btn_no_date">No caduca</string>
@@ -71,17 +76,21 @@
<string name="btn_view_cert_key">Ver clave de verificación</string>
<string name="btn_create_key">Crear clave</string>
<string name="btn_add_files">Añadir archivo(s)</string>
- <string name="btn_add_share_decrypted_text">Compartir texto desencriptado</string>
- <string name="btn_decrypt_clipboard">Descifrar texto desde el portapapeles</string>
- <string name="btn_decrypt_and_verify">y verificar firmas</string>
- <string name="btn_decrypt_files">Desencriptar ficheros</string>
+ <string name="btn_share_decrypted_text">Compartir texto descifrado</string>
+ <string name="btn_copy_decrypted_text">Copiar texto descifrado</string>
+ <string name="btn_decrypt_clipboard">Leer del portapapeles</string>
+ <string name="btn_decrypt_files">Seleccionar fichero de entrada</string>
<string name="btn_encrypt_files">Encriptar ficheros</string>
<string name="btn_encrypt_text">Encriptar texto</string>
<string name="btn_add_email">Añadir dirección de correo electrónico adicional</string>
+ <string name="btn_unlock">Desbloquear</string>
+ <string name="btn_add_keyserver">Añadir</string>
+ <string name="btn_save_default">Guardar como predeterminado</string>
+ <string name="btn_saved">¡Guardado!</string>
<!--menu-->
<string name="menu_preferences">Ajustes</string>
<string name="menu_help">Ayuda</string>
- <string name="menu_export_key">Exportar hacia archivo</string>
+ <string name="menu_export_key">Hacer copia de seguridad hacia fichero</string>
<string name="menu_delete_key">Borrar clave</string>
<string name="menu_manage_keys">Administrar mis claves</string>
<string name="menu_search">Buscar</string>
@@ -91,19 +100,20 @@
<string name="menu_select_all">Seleccionar todo</string>
<string name="menu_export_all_keys">Exportar todas las claves</string>
<string name="menu_update_all_keys">Actualizar todas las claves</string>
- <string name="menu_advanced">Mostrar información avanzada</string>
+ <string name="menu_advanced">Información extendida</string>
<string name="menu_certify_fingerprint">Confirmar mediante comparación de la huella digital</string>
<string name="menu_export_log">Exportar registro</string>
+ <string name="menu_keyserver_add">Añadir</string>
<!--label-->
<string name="label_message">Texto</string>
<string name="label_file">Archivo</string>
<string name="label_files">Archivo(s)</string>
<string name="label_file_colon">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_no_passphrase">Sin contraseña</string>
+ <string name="label_passphrase">Contraseña</string>
<string name="label_unlock">Desbloqueando...</string>
- <string name="label_passphrase_again">Repetir frase-contraseña</string>
- <string name="label_show_passphrase">Mostrar frase-contraseña</string>
+ <string name="label_passphrase_again">Repetir contraseña</string>
+ <string name="label_show_passphrase">Mostrar contraseña</string>
<string name="label_algorithm">Algoritmo</string>
<string name="label_ascii_armor">Armadura ASCII del fichero</string>
<string name="label_file_ascii_armor">Habilitar armadura ASCII</string>
@@ -112,19 +122,20 @@
<string name="label_use_default_yubikey_pin">Utilizar PIN predeterminado de la YubiKey</string>
<string name="label_use_num_keypad_for_yubikey_pin">Utilizar teclado numérico para el PIN de la YubiKey</string>
<string name="label_label_use_default_yubikey_pin_summary">Utiliza el PIN predeterminado (123456) para acceder a las YubiKeys sobre NFC</string>
- <string name="label_asymmetric_from">Firmado por:</string>
+ <string name="label_asymmetric_from">Firmar con:</string>
<string name="label_to">Cifrar hacia:</string>
<string name="label_delete_after_encryption">Borrar ficheros después del cifrado</string>
<string name="label_delete_after_decryption">Eliminar fichero después del descifrado</string>
<string name="label_encryption_algorithm">Algoritmo de cifrado</string>
<string name="label_hash_algorithm">Algoritmo de identificación criptográfica (hash)</string>
- <string name="label_symmetric">Cifrar con frase-contraseña</string>
- <string name="label_passphrase_cache_ttl">Duración en caché</string>
- <string name="label_passphrase_cache_subs">Almacenar en caché frases-contraseña por subclave</string>
+ <string name="label_symmetric">Cifrar con contraseña</string>
+ <string name="label_passphrase_cache_ttl">Recordar hora</string>
+ <string name="label_passphrase_cache_subs">Recordar contraseñas por subclave</string>
<string name="label_message_compression">Compresión de texto</string>
<string name="label_file_compression">Compresión del fichero</string>
- <string name="label_keyservers">Servidores de claves</string>
+ <string name="label_keyservers">Seleccione servidores de claves OpenPGP</string>
<string name="label_key_id">ID de clave</string>
+ <string name="label_key_created">Clave creada el %s</string>
<string name="label_creation">Creación</string>
<string name="label_expiry">Caducidad</string>
<string name="label_usage">Uso</string>
@@ -137,15 +148,56 @@
<string name="label_send_key">Sincronizar con la nube</string>
<string name="label_fingerprint">Huella digital</string>
<string name="expiry_date_dialog_title">Establer la fecha de vencimiento</string>
- <string name="label_first_keyserver_is_used">(Se prefiere el primer servidor de claves listado)</string>
+ <string name="label_keyservers_title">Servidores de claves</string>
+ <string name="label_keyserver_settings_hint">Arrastrar para cambiar orden, pulsar para editar/borrar</string>
+ <string name="label_selected_keyserver_title">Servidor de claves seleccionado</string>
<string name="label_preferred">preferido</string>
<string name="label_enable_compression">Habilitar compresión</string>
<string name="label_encrypt_filenames">Cifrar nombres de ficheros</string>
<string name="label_hidden_recipients">Ocultar receptores</string>
- <string name="pref_keyserver">Buscar en servidor de claves</string>
- <string name="pref_keyserver_summary">Buscar en servidor de claves HKP</string>
- <string name="pref_keybase">Buscar en Keybase.io</string>
- <string name="pref_keybase_summary">Buscar en el índice de Keybase.io</string>
+ <string name="label_verify_keyserver">Verificar servidor de claves</string>
+ <string name="label_enter_keyserver_url">Introduzca URL de servidor de claves</string>
+ <string name="label_keyserver_dialog_delete">Borrar servidor de claves</string>
+ <string name="label_theme">Tema decorativo</string>
+ <string name="pref_keyserver">Servidores de claves OpenPGP</string>
+ <string name="pref_keyserver_summary">Busca claves en los servidores de claves OpenPGP seleccionados (protocolo HKP)</string>
+ <string name="pref_keybase">keybase.io</string>
+ <string name="pref_keybase_summary">Busca claves en keybase.io</string>
+ <string name="label_sync_settings_keyserver_title">Actualizar claves automáticamente</string>
+ <string name="label_sync_settings_keyserver_summary_on">Las claves con más de una semana de vida se actualizarán desde el servidor de claves preferido</string>
+ <string name="label_sync_settings_keyserver_summary_off">Las claves no se actualizarán automáticamente</string>
+ <string name="label_sync_settings_contacts_title">Sincronizar contactos con claves</string>
+ <string name="label_sync_settings_contacts_summary_on">Las claves se vincularon a contactos con correos coincidentes, ocurre de forma completamente desconectada</string>
+ <string name="label_sync_settings_contacts_summary_off">Las claves nuevas no se vincularán a contactos</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <string name="keyserver_sync_settings_title">Actualizar claves automáticamente</string>
+ <!--Proxy Preferences-->
+ <string name="pref_proxy_tor_title">Habilitar Tor</string>
+ <string name="pref_proxy_tor_summary">Requiere que Orbot esté instalado</string>
+ <string name="pref_proxy_normal_title">Habilitar otro proxy</string>
+ <string name="pref_proxy_host_title">Servidor proxy</string>
+ <string name="pref_proxy_host_err_invalid">El servidor proxy (interpuesto) no puede estar vacío</string>
+ <string name="pref_proxy_port_title">Puerto de proxy</string>
+ <string name="pref_proxy_port_err_invalid">Número de puerto introducido no válido</string>
+ <string name="pref_proxy_type_title">Tipo de proxy</string>
+ <!--proxy type choices and values-->
+ <string name="pref_proxy_type_choice_http">HTTP</string>
+ <string name="pref_proxy_type_choice_socks">SOCKS</string>
+ <!--OrbotHelper strings-->
+ <string name="orbot_ignore_tor">No usar Tor</string>
+ <!--InstallDialogFragment strings-->
+ <string name="orbot_install_dialog_title">¿Instalar Orbot para usar Tor?</string>
+ <string name="orbot_install_dialog_install">Instalar</string>
+ <string name="orbot_install_dialog_content">Tiene que tener Orbot instalado y activado para proxyficar el tráfico a través de él. ¿Desea instalarlo?</string>
+ <string name="orbot_install_dialog_cancel">Cancelar</string>
+ <string name="orbot_install_dialog_ignore_tor">No usar Tor</string>
+ <!--StartOrbotDialogFragment strings-->
+ <string name="orbot_start_dialog_title">¿Iniciar Orbot?</string>
+ <string name="orbot_start_dialog_content">Orbot no parece estar corriendo. ¿Desea iniciarlo y conectar a Tor?</string>
+ <string name="orbot_start_btn">Iniciar Orbot</string>
+ <string name="orbot_start_dialog_start">Iniciar Orbot</string>
+ <string name="orbot_start_dialog_cancel">Cancelar</string>
+ <string name="orbot_start_dialog_ignore_tor">No usar Tor</string>
<string name="user_id_no_name">&lt;sin nombre&gt;</string>
<string name="none">&lt;ninguna&gt;</string>
<plurals name="n_keys">
@@ -179,32 +231,40 @@
<string name="filemanager_title_open">Abrir...</string>
<string name="error">Error</string>
<string name="error_message">Error: %s</string>
+ <string name="theme_dark">Oscuro</string>
+ <string name="theme_light">Claro</string>
<!--key flags-->
<string name="flag_certify">Certificar</string>
<string name="flag_sign">Firmar</string>
<string name="flag_encrypt">Cifrar</string>
<string name="flag_authenticate">Autentificar</string>
<!--sentences-->
- <string name="wrong_passphrase">Frase de contraseña incorrecta.</string>
+ <string name="wrong_passphrase">Contraseña incorrecta.</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">Por favor, introduce una frase de contraseña.</string>
+ <string name="passphrases_do_not_match">Las contraseñas no coincidieron.</string>
+ <string name="passphrase_must_not_be_empty">Introduzca una contraseña.</string>
<string name="passphrase_for_symmetric_encryption">Cifrado simétrico.</string>
- <string name="passphrase_for">Introducir la frase de contraseña para \'%s\'</string>
+ <string name="passphrase_for">Introduzca contraseña para \'%s\'</string>
<string name="pin_for">Introduzca el PIN para \'%s\'</string>
<string name="yubikey_pin_for">Introduzca el PIN para acceder a la YubiKey para \'%s\'</string>
- <string name="nfc_text">Sostenga la YubiKey contra el reverso de su dispositivo.</string>
+ <string name="nfc_text">Sostenga la YubiKey contra el marcador NFC en el reverso de su dispositivo.</string>
+ <string name="nfc_wait">¡Mantenga la YubiKey en el reverso!</string>
+ <string name="nfc_finished">Retire la YubiKey ahora.</string>
+ <string name="nfc_try_again_text">Retire la YubiKey ahora y pulse INTENTAR DE NUEVO.</string>
<string name="file_delete_confirmation_title">¿Borrar ficheros originales?</string>
<string name="file_delete_confirmation">Los siguientes ficheros se borrarán:%s</string>
<string name="file_delete_successful">%1$d de %2$d ficheros han sido borrados.%3$s</string>
- <string name="no_file_selected">Selecciona un archivo antes.</string>
+ <string name="no_file_selected">Ningún fichero seleccionado.</string>
<string name="encrypt_sign_successful">Firmado y/o cifrado con éxito.</string>
<string name="encrypt_sign_clipboard_successful">Firmado y/o cifrado del portapapeles con éxito.</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="error_no_encryption_or_signature_key">Seleccionar al menos una clave de cifrado o una clave de firma.</string>
<string name="specify_file_to_encrypt_to">Por favor especifique hacia qué fichero cifrar.\nADVERTENCIA: ¡El fichero se sobreescribirá si existe!</string>
<string name="specify_file_to_decrypt_to">Por favor especifique hacia qué fichero descifrar.\nADVERTENCIA: ¡El fichero se sobreescribirá si existe!</string>
- <string name="specify_file_to_export_to">Por favor especifique hacia qué fichero exportar.\nADVERTENCIA: ¡El fichero se sobreescribirá si existe!</string>
+ <string name="specify_backup_dest">Se realizará una copia de seguridad excluyendo sus claves, por favor, especifique un fichero de destino.\nADVERTENCIA: ¡El fichero se sobreescribirá si existe!</string>
+ <string name="specify_backup_dest_single">Esta clave se compartirá, por favor, especifique un fichero de destino.\nADVERTENCIA: ¡El fichero se sobreescribirá si existe!</string>
+ <string name="specify_backup_dest_secret_single">Se realizará una copia de seguridad completa de su clave, por favor, especifique un fichero de destino.\nADVERTENCIA: ¡El fichero se sobreescribirá si existe!</string>
+ <string name="specify_backup_dest_secret">Se realizará una copia de seguridad completa de todas sus claves incluyendo las suyas, por favor, especifique un fichero de destino.\nADVERTENCIA: ¡El fichero se sobreescribirá si existe!</string>
<string name="key_deletion_confirmation_multi">¿De verdad quiere borrar todas las claves seleccionadas?</string>
<string name="secret_key_deletion_confirmation">¡Después del borrado no podrá leer mensajes cifrados con esta clave y perderá todas las confirmaciones de clave hechas con ella!</string>
<string name="public_key_deletetion_confirmation">¿Borrar clave \'%s\'?</string>
@@ -237,34 +297,38 @@
<string name="error_external_storage_not_ready">el almacenamiento externo no está preparado</string>
<string name="error_key_size_minimum512bit">el tamaño de clave debe ser de al menos 512bit</string>
<string name="error_unknown_algorithm_choice">elegido algoritmo desconocido</string>
- <string name="error_user_id_no_email">no se ha encontrado un email</string>
+ <string name="error_user_id_no_email">no se encontró ninguna dirección de correo</string>
<string name="error_key_needs_a_user_id">necesita al menos una identificación</string>
- <string name="error_no_signature_passphrase">no has proporcionado una frase de contraseña</string>
+ <string name="error_no_signature_passphrase">no se estableció contraseña.</string>
<string name="error_no_signature_key">no has proporcionado una clave de firma</string>
<string name="error_invalid_data">¡No es un contenido cifrado o firmado con OpenPGP válido!</string>
<string name="error_integrity_check_failed">¡ha fallado la comprobación de integridad! ¡Los datos han sido modificados!</string>
- <string name="error_wrong_passphrase">frase de contraseña incorrecta</string>
+ <string name="error_wrong_passphrase">contraseña incorrecta</string>
<string name="error_could_not_extract_private_key">no se puede extraer la clave privada</string>
<!--errors without preceeding Error:-->
<string name="error_jelly_bean_needed">¡Necesita Android 4.1 para usar la característica NFC Beam (haz NFC) de Android!</string>
<string name="error_nfc_needed">¡NFC ha de estar habilitado!</string>
<string name="error_beam_needed">¡Beam ha de estar habilitado!</string>
<string name="error_nothing_import">¡No se encontraron claves!</string>
+ <string name="error_nothing_import_selected">¡No se seleccionaron claves a importar!</string>
<string name="error_contacts_key_id_missing">¡Fallo al obtener clave de identificación desde los contactos!</string>
<string name="error_generic_report_bug">Ha ocurrido un error genérico, por favor, informa de este bug a OpenKeychain</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">No firmado</string>
<string name="decrypt_result_invalid_signature">¡Firma no válida!</string>
- <string name="decrypt_result_signature_uncertified">Firmado por (¡no certificada!)</string>
- <string name="decrypt_result_signature_certified">Firmado por</string>
- <string name="decrypt_result_signature_expired_key">¡La clave ha caducado!</string>
- <string name="decrypt_result_signature_revoked_key">¡La clave ha sido revocada!</string>
- <string name="decrypt_result_signature_missing_key">Clave pública desconocida</string>
+ <string name="decrypt_result_insecure_cryptography">¡Firma no válida (criptografía no segura)!</string>
+ <string name="decrypt_result_signature_uncertified">Firmado por clave <b>no confirmada</b></string>
+ <string name="decrypt_result_signature_secret">Firmado por la clave de usted</string>
+ <string name="decrypt_result_signature_certified">Firmado por clave confirmada</string>
+ <string name="decrypt_result_signature_expired_key">¡Firmado por clave <b>caducada</b>!</string>
+ <string name="decrypt_result_signature_revoked_key">¡Firmado por clave <b>revocada</b>!</string>
+ <string name="decrypt_result_signature_missing_key">Firmado por <b>clave pública desconocida</b></string>
<string name="decrypt_result_encrypted">Cifrado</string>
<string name="decrypt_result_not_encrypted">No cifrado</string>
+ <string name="decrypt_result_insecure">Cifrado no seguro</string>
<string name="decrypt_result_action_show">Mostrar</string>
<string name="decrypt_result_action_Lookup">Buscar</string>
- <string name="decrypt_invalid_text">O bien la firma no es válida, o la clave ha sido revocada o ha caducado. No puede estar seguro de quién escribió el texto. ¿Aún quiere mostrarlo?</string>
+ <string name="decrypt_invalid_text">O bien la firma no es válida o la clave ha sido revocada. No puede estar seguro de quién escribió el texto. ¿Quiere aún mostrarlo?</string>
<string name="decrypt_invalid_button">Comprendo los riesgos, ¡muéstralo!</string>
<!--Add keys-->
<string name="add_keys_my_key">Mi clave:</string>
@@ -274,6 +338,7 @@
<string name="progress_cancelling">cancelando...</string>
<string name="progress_saving">guardando...</string>
<string name="progress_importing">importando...</string>
+ <string name="progress_revoking_uploading">Revocando y subiendo clave...</string>
<string name="progress_updating">Actualizando claves...</string>
<string name="progress_exporting">exportando...</string>
<string name="progress_uploading">subiendo...</string>
@@ -294,11 +359,14 @@
<string name="progress_modify_subkeyrevoke">revocando subclaves...</string>
<string name="progress_modify_subkeystrip">desnudando claves...</string>
<string name="progress_modify_subkeyadd">añadiendo subclaves...</string>
- <string name="progress_modify_passphrase">cambiando frase-contraseña...</string>
+ <string name="progress_modify_passphrase">cambiando contraseña...</string>
+ <string name="progress_modify_pin">cambiando PIN...</string>
+ <string name="progress_modify_admin_pin">cambiando el PIN de Admin...</string>
<plurals name="progress_exporting_key">
<item quantity="one">exportando clave...</item>
<item quantity="other">exportando claves...</item>
</plurals>
+ <string name="progress_start">preparando operación...</string>
<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>
@@ -318,6 +386,8 @@
<string name="progress_deleting">borrando claves...</string>
<string name="progress_con_saving">consolidación: guardando en caché...</string>
<string name="progress_con_reimport">consolidación: reimportando</string>
+ <string name="progress_verifying_keyserver_url">verificando servidor de claves...</string>
+ <string name="progress_starting_orbot">Iniciando Orbot...</string>
<!--action strings-->
<string name="hint_cloud_search_hint">Buscar mediante Nombre, Correo electrónico...</string>
<!--key bit length selections-->
@@ -359,11 +429,14 @@
<string name="import_tab_qr_code">Código QR/NFC</string>
<string name="import_import">Importar las claves seleccionadas</string>
<string name="import_qr_code_wrong">¡El código QR está deformado! ¡Por favor, prueba de nuevo!</string>
- <string name="import_qr_code_too_short_fingerprint">La huella de validación (fingerprint) de clave es demasiado corta (&lt; 16 caracteres)</string>
+ <string name="import_qr_code_fp">¡La huella de validación de clave está malformada o es demasiado corta!</string>
+ <string name="import_qr_code_too_short_fingerprint">¡La huella de validación de clave es demasiado corta!</string>
<string name="import_qr_code_button">Escanear código QR</string>
<string name="import_qr_code_text">¡Sitúe su cámara sobre el código QR!</string>
+ <!--Import from URL-->
+ <string name="import_url_warn_no_search_parameter">No se definió consulta de búsqueda. Aún puede buscar manualmente en este servidor de claves.</string>
<!--Generic result toast-->
- <string name="view_log">Detalles</string>
+ <string name="snackbar_details">Detalles</string>
<string name="with_warnings">, con advertencias</string>
<string name="with_cancelled">, hasta que sea cancelado</string>
<!--Import result toast-->
@@ -412,6 +485,11 @@
</plurals>
<string name="delete_nothing">No hay nada que borrar.</string>
<string name="delete_cancelled">Operación de borrado cancelada.</string>
+ <!--Revoke result toast (snackbar)-->
+ <string name="revoke_ok">Clave revocada con éxito.</string>
+ <string name="revoke_fail">¡Error revocando clave!</string>
+ <string name="revoke_nothing">Nada que revocar.</string>
+ <string name="revoke_cancelled">Se canceló la operación de revocado.</string>
<!--Certify result toast-->
<plurals name="certify_keys_ok">
<item quantity="one">Clave%2$s certificada con éxito.</item>
@@ -431,10 +509,10 @@
<string name="intent_send_encrypt">Cifrar con OpenKeychain</string>
<string name="intent_send_decrypt">Descifrar con OpenKeychain</string>
<!--Remote API-->
- <string name="api_settings_show_info">Mostrar información avanzada</string>
- <string name="api_settings_hide_info">Ocultar información avanzada</string>
- <string name="api_settings_show_advanced">Mostrar la configuración avanzada</string>
- <string name="api_settings_hide_advanced">Ocultar la configuración avanzada</string>
+ <string name="api_settings_show_info">Mostrar información extendida</string>
+ <string name="api_settings_hide_info">Ocultar información extendida</string>
+ <string name="api_settings_show_advanced">Mostrar configuración extendida</string>
+ <string name="api_settings_hide_advanced">Ocultar configuración extendida</string>
<string name="api_settings_no_key">No se ha seleccionado ninguna clave</string>
<string name="api_settings_select_key">Seleccionar clave</string>
<string name="api_settings_create_key">Crear nueva clave</string>
@@ -445,9 +523,9 @@
<string name="api_settings_start">Iniciar aplicación</string>
<string name="api_settings_delete_account">Borrar cuenta</string>
<string name="api_settings_package_name">Nombre de paquete</string>
- <string name="api_settings_package_signature">SHA-256 de firma de paquete</string>
- <string name="api_settings_accounts">Cuentas (API desechada)</string>
- <string name="api_settings_advanced">Información avanzada</string>
+ <string name="api_settings_package_certificate">SHA-256 del certificado del paquete</string>
+ <string name="api_settings_accounts">Cuentas (API antigua)</string>
+ <string name="api_settings_advanced">Información extendida</string>
<string name="api_settings_allowed_keys">Claves permitidas</string>
<string name="api_settings_settings">Configuración</string>
<string name="api_settings_key">Clave de la cuenta:</string>
@@ -458,15 +536,29 @@
<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 encontraron claves para estas identificaciones:</string>
- <string name="api_select_pub_keys_dublicates_text">Existe más de una clave para estas identificaciones:</string>
+ <string name="api_select_pub_keys_missing_text">No se encontraron claves para estas direcciones de correo electrónico:</string>
+ <string name="api_select_pub_keys_dublicates_text">Existe más de una clave para estas direcciones de correo electrónico:</string>
<string name="api_select_pub_keys_text">¡Por favor, revisa la lista de destinatarios!</string>
<string name="api_select_pub_keys_text_no_user_ids">¡Por favor seleccione los receptores!</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 OpenKeychain y regístrala de nuevo.</string>
<string name="api_select_sign_key_text">Por favor, seleccione una de sus claves existentes o cree una nueva.</string>
+ <string name="api_select_keys_text">Ninguna de las claves permitidas puede descifrar el contenido. Por favor, seleccione las claves permitidas.</string>
<!--Share-->
<string name="share_qr_code_dialog_title">Compartir con código QR</string>
<string name="share_nfc_dialog">Compartir con NFC</string>
+ <!--retry upload dialog-->
+ <string name="retry_up_dialog_title">Fallo al subir</string>
+ <string name="retry_up_dialog_message">Fallo al subir. ¿Desea reintentar la operación?</string>
+ <string name="retry_up_dialog_btn_reupload">Reintentar operación</string>
+ <string name="retry_up_dialog_btn_cancel">Cancelar operación</string>
+ <!--Delete or revoke private key dialog-->
+ <string name="del_rev_dialog_message">Si ya no desea usar esta clave, debe ser revocada y subida. Seleccione \'SÓLO BORRAR\' si desea eliminar la clave de OpenKeychain pero continuar usándola desde cualquier otro sitio.</string>
+ <string name="del_rev_dialog_title">Revocar/Borrar Clave \'%s\'</string>
+ <string name="del_rev_dialog_btn_revoke">Revocar y subir</string>
+ <string name="del_rev_dialog_btn_delete">Sólo borrar</string>
+ <!--Delete Or Revoke Dialog spinner-->
+ <string name="del_rev_dialog_choice_delete">Sólo borrar</string>
+ <string name="del_rev_dialog_choice_rev_upload">Revocar y subir</string>
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">1 clave seleccionada.</item>
@@ -509,14 +601,14 @@
<string name="key_trust_results_prefix">Keybase.io ofrece \"comprobantes\" que sostienen que el propietario de esta clave:</string>
<string name="key_trust_header_text">Nota: Los comprobantes de Keybase.io son una característica experimental de OpenKeychain. Le animamos a que escanee los Códigos QR o intercambie claves vía NFC además de confirmarlas.</string>
<!--keybase proof stuff-->
- <string name="keybase_narrative_twitter">Publica en Twitter como</string>
- <string name="keybase_narrative_github">Es conocido en GitHub como</string>
- <string name="keybase_narrative_dns">Controla el(los) nombre(s) de dominio</string>
- <string name="keybase_narrative_web_site">Puede publicar en el(los) sitio(s) web</string>
- <string name="keybase_narrative_reddit">Publica en Reddit como </string>
- <string name="keybase_narrative_coinbase">Es conocido en Coinbase como</string>
- <string name="keybase_narrative_hackernews">Publica en Hacker News como</string>
- <string name="keybase_narrative_unknown">Tipo de comprobante desconocido</string>
+ <string name="keybase_narrative_twitter">Publica en Twitter como %s</string>
+ <string name="keybase_narrative_github">Es conocido en GitHub como %s</string>
+ <string name="keybase_narrative_dns">Controla el/los nombre(s) de dominio %s</string>
+ <string name="keybase_narrative_web_site">Puede publicar en el/los sitio(s) web %s</string>
+ <string name="keybase_narrative_reddit">Publica en Reddit como %s</string>
+ <string name="keybase_narrative_coinbase">Es conocido en Coinbase como %s</string>
+ <string name="keybase_narrative_hackernews">Publica en Hacker News como %s</string>
+ <string name="keybase_narrative_unknown">Prueba de tipo desconocido %s</string>
<string name="keybase_proof_failure">Desafortunadamente este comprobante no se puede verificar.</string>
<string name="keybase_unknown_proof_failure">Problema no reconocido con el chequeador del comprobante</string>
<string name="keybase_problem_fetching_evidence">Problema con el comprobante</string>
@@ -538,7 +630,7 @@
<string name="keybase_reddit_attribution">atribuido por Reddit a</string>
<string name="keybase_verify">Verificar</string>
<!--Edit key-->
- <string name="edit_key_action_change_passphrase">Cambiar frase-contraseña</string>
+ <string name="edit_key_action_change_passphrase">Cambiar contraseña</string>
<string name="edit_key_action_add_identity">Añadir identificación</string>
<string name="edit_key_action_add_subkey">Añadir subclave</string>
<string name="edit_key_edit_user_id_title">¡Seleccione una acción!</string>
@@ -552,30 +644,42 @@
<string name="edit_key_edit_user_id_revoked">Esta identificación se ha revocado. Esto no puede deshacerse.</string>
<string name="edit_key_edit_subkey_title">¡Seleccione una acción!</string>
<string-array name="edit_key_edit_subkey">
- <item>Cambiar expiración</item>
+ <item>Cambiar caducidad</item>
<item>Revocar subclave</item>
<item>Desnudar subclave</item>
+ <item>Mover subclave a la Yubikey / Tarjeta inteligente</item>
</string-array>
<string name="edit_key_new_subkey">nueva subclave</string>
<string name="edit_key_select_flag">¡Por favor, seleccione al menos un indicador!</string>
<string name="edit_key_error_add_identity">¡Añadir al menos una identificación!</string>
<string name="edit_key_error_add_subkey">¡Añadir al menos una subclave!</string>
+ <string name="edit_key_error_bad_nfc_algo">¡Algoritmo no soportado por la tarjeta inteligente!</string>
+ <string name="edit_key_error_bad_nfc_size">¡El tamaño de la clave no está soportado por la tarjeta inteligente!</string>
+ <string name="edit_key_error_bad_nfc_stripped">¡No se puede mover la clave a la tarjeta inteligente (tanto desnuda como \'derivar-a-tarjeta\')!</string>
<!--Create key-->
<string name="create_key_upload">Sincronizar con la nube</string>
<string name="create_key_empty">Este campo es obligatorio</string>
- <string name="create_key_passphrases_not_equal">Las frases-contraseña no coinciden</string>
+ <string name="create_key_passphrases_not_equal">Las contraseñas no coinciden</string>
<string name="create_key_final_text">Ha introducido la siguiente identificación:</string>
<string name="create_key_final_robot_text">Crear una clave puede llevar un tiempo, tómese una taza de café entre tanto...</string>
<string name="create_key_rsa">(3 subclaves, RSA, 4096 bits)</string>
<string name="create_key_custom">(configuración de clave personalizada)</string>
<string name="create_key_name_text">Elija un nombre asociado con esta clave. Este puede ser un nombre completo, ej. \'Juan Nadie\', o un apodo, ej. \'Juanito\'.</string>
<string name="create_key_email_text">Introduzca su dirección principal de correo electrónico para comunicación segura.</string>
- <string name="create_key_passphrase_text">Elija una frase-contraseña robusta. Protege su clave cuando su dispositivo es robado.</string>
+ <string name="create_key_passphrase_text">Escoja una contraseña robusta. Protegerá su clave si su dispositivo es robado.</string>
<string name="create_key_hint_full_name">Nombre completo o apodo</string>
<string name="create_key_edit">Cambiar configuración de clave</string>
<string name="create_key_add_email">Añadir dirección de correo electrónico</string>
<string name="create_key_add_email_text">Las direcciones adicionales de correo electrónico también están asociadas a esta clave y pueden utilizarse para asegurar la comunicación.</string>
- <string name="create_key_email_already_exists_text">Ya se ha añadido el correo electrónico</string>
+ <string name="create_key_email_already_exists_text">Esta dirección de correo electrónico ya fue añadida</string>
+ <string name="create_key_email_invalid_email">El formato de la dirección de correo no es válido</string>
+ <string name="create_key_yubi_key_pin_text">Por favor, recuerde el PIN, se requiere para usar su YubiKey más tarde. Por favor, escriba el PIN de Admin y guárdelo en un lugar seguro.</string>
+ <string name="create_key_yubi_key_pin">PIN</string>
+ <string name="create_key_yubi_key_admin_pin">PIN de Admin</string>
+ <string name="create_key_yubi_key_pin_repeat_text">Por favor, introduzca el PIN y el PIN de Admin para proceder.</string>
+ <string name="create_key_yubi_key_pin_repeat">Repita el PIN</string>
+ <string name="create_key_yubi_key_admin_pin_repeat">Repita el PIN de Admin</string>
+ <string name="create_key_yubi_key_pin_not_correct">¡El PIN no es correcto!</string>
<!--View key-->
<string name="view_key_revoked">Revocada: ¡La clave no debe volver a ser usada!</string>
<string name="view_key_expired">Caducada: ¡El contacto necesita extender la vigencia de la clave!</string>
@@ -584,6 +688,15 @@
<string name="view_key_verified">Clave confirmada</string>
<string name="view_key_unverified">No confirmada: ¡Escanee el código QR para confirmar clave!</string>
<string name="view_key_fragment_no_system_contact">&lt;ninguno&gt;</string>
+ <!--Add/Edit keyserver-->
+ <string name="add_keyserver_dialog_title">Añadir servidor de claves</string>
+ <string name="edit_keyserver_dialog_title">Editar servidor de claves</string>
+ <string name="add_keyserver_verified">¡Servidor de claves verificado!</string>
+ <string name="add_keyserver_without_verification">Servidor de claves añadido sin verificación</string>
+ <string name="add_keyserver_invalid_url">¡URL no válida!</string>
+ <string name="add_keyserver_connection_failed">Fallo al conectar al servidor de claves. Por favor, compuebe la URL y su conexión a Internet.</string>
+ <string name="keyserver_preference_deleted">%s borrado</string>
+ <string name="keyserver_preference_cannot_delete_last">No se puede borrar el último servidor de claves. ¡Se requiere al menos uno!</string>
<!--Navigation Drawer-->
<string name="nav_keys">Claves</string>
<string name="nav_encrypt_decrypt">Cifrar/Descifrar</string>
@@ -591,6 +704,7 @@
<string name="drawer_open">Abrir el Navigation Drawer</string>
<string name="drawer_close">Cerrar el Navigation Drawer</string>
<string name="my_keys">Mis claves</string>
+ <string name="nav_backup">Copia de seguridad</string>
<!--hints-->
<string name="encrypt_content_edit_text_hint">Escriba el texto</string>
<!--certs-->
@@ -711,10 +825,10 @@
<string name="msg_is_pubring_generate">Generando un juego de claves públicas desde el juego de claves secretas (privadas)</string>
<string name="msg_is_subkey_nonexistent">La subclave %s no está disponible en la clave secreta (privada)</string>
<string name="msg_is_subkey_ok">Subclave secreta (privada) %s marcada como disponible</string>
- <string name="msg_is_subkey_empty">Subclave secreta (privada) %s marcada como disponible, con frase-contraseña vacía</string>
+ <string name="msg_is_subkey_empty">Subclave secreta %s marcada como disponible, con contraseña vacía</string>
<string name="msg_is_subkey_pin">Subclave secreta %s marcada como disponible, con PIN</string>
<string name="msg_is_subkey_stripped">Subclave secreta (privada) %s marcada como desnuda</string>
- <string name="msg_is_subkey_divert">Se marcó la subclave secreta (privada) %s como \'desviar a smartcard/NFC\'</string>
+ <string name="msg_is_subkey_divert">Se marcó la subclave sercreta (privada) %s como \'derivar-a-tarjeta\'</string>
<string name="msg_is_success_identical">El juego de claves no contiene nuevos datos, no hay nada que hacer</string>
<string name="msg_is_success">Juego de claves secretas (privadas) importado con éxito</string>
<!--Keyring Canonicalization log entries-->
@@ -741,6 +855,7 @@
<string name="msg_kc_sub_bad_local">Eliminando certificado de vinculación de subclave con distintivo \'local\'</string>
<string name="msg_kc_sub_bad_keyid">La identificación del publicante de la vinculación de subclave no coincide</string>
<string name="msg_kc_sub_bad_time">Eliminando certificado de vinculación de subclave, con marca de tiempo futura</string>
+ <string name="msg_kc_sub_bad_time_early">¡El certificado de acoplamiento de subclave tiene una marca de tiempo anterior a su clave!</string>
<string name="msg_kc_sub_bad_type">Tipo de certificado de subclave desconocido: %s</string>
<string name="msg_kc_sub_dup">Eliminando certificado redundante de vinculación de subclave</string>
<string name="msg_kc_sub_primary_bad">Eliminando certificado de vinculación de subclave debido a un certificado de vinculación principal no vigente</string>
@@ -793,7 +908,7 @@
<string name="msg_kc_uat_warn_encoding">¡La identificación de usuario no se confirma como UTF-8!</string>
<!--Keyring merging log entries-->
<string name="msg_mg_error_secret_dummy">Nueva subclave pública encontrada, ¡pero la generación de subclaves secretas (privadas) ficticias no está soportada!</string>
- <string name="msg_mg_error_heterogeneous">¡Se intentaron fusionar juegos de claves con diferentes huellas de validación!</string>
+ <string name="msg_mg_error_heterogeneous">¡Se intentaron fusionar juegos de claves con diferentes huellas de validación de claves!</string>
<string name="msg_mg_error_encode">¡Error fatal codificando firma!</string>
<string name="msg_mg_public">Incorporándolas en el juego de claves públicas %s</string>
<string name="msg_mg_secret">Incorporándolas en el juego de claves secretas (privadas) %s</string>
@@ -817,7 +932,9 @@
<string name="msg_cr_error_flags_ecdh">¡Seleccionados indicativos de clave defectuosa, ECDH no puede usarse para firmado!</string>
<!--modifySecretKeyRing-->
<string name="msg_mr">Modificando el juego de claves %s</string>
- <string name="msg_mf_error_divert_serial">¡El número de serie de una clave con indicativo desviar-a-tarjeta debe ser de 16 bytes! ¡Esto es un error de programación, por favor consigne un informe de error!</string>
+ <string name="msg_mf_divert">Se derivará a la tarjeta inteligente para operaciones de criptografía</string>
+ <string name="msg_mf_error_divert_newsub">¡La creación de nuevas claves no está soportada para claves primarias marcadas \'derivar-a-tarjeta\'!</string>
+ <string name="msg_mf_error_divert_serial">¡El número de serie de una clave \'derivar-a-tarjeta\' ha de tener 16 bytes! Esto es un error de programación, por favor, ¡consigne un infome de fallo!</string>
<string name="msg_mf_error_encode">¡Excepción en la codificación!</string>
<string name="msg_mf_error_fingerprint">¡La actual huella de validación de clave no coincide con la esperada!</string>
<string name="msg_mf_error_keyid">No hay identificación de clave. Esto es un error interno, por favor ¡consigne un informe de error!</string>
@@ -825,28 +942,40 @@
<string name="msg_mf_error_master_none">¡No se encontró certificado maestro sobre el que operar! (¿Todos revocados?)</string>
<string name="msg_mf_error_noexist_primary">¡Se especificó una identificación de usuario principal errónea!</string>
<string name="msg_mf_error_noexist_revoke">¡Se especificó una identificación de usuario errónea para revocación!</string>
- <string name="msg_mf_error_restricted">¡Se intentó efectuar una operación restringida sin frase-contraseña! ¡Este es un error de programación, por favor consigne un informe de error!</string>
+ <string name="msg_mf_error_restricted">¡Se intentó ejecutar una operación restringida sin contraseña! Este es un error de programación, por favor, ¡consigne un informe de fallo!</string>
<string name="msg_mf_error_revoked_primary">¡Las identificaciones de usuario revocadas no pueden ser las principales!</string>
<string name="msg_mf_error_null_expiry">El periodo hasta la expiración no puede ser \"el mismo que antes\" al crear subclave. Esto es un error de programación, por favor ¡rellene un informe de fallo!</string>
+ <string name="msg_mf_error_noop">¡No hay nada que hacer!</string>
<string name="msg_mf_error_passphrase_master">¡Error fatal descrifrando la clave maestra! Probablemente esto se daba a un error de programación, por favor ¡rellene un informe de fallo!</string>
<string name="msg_mf_error_pgp">¡Error OpenPGP interno!</string>
<string name="msg_mf_error_sig">¡Excepción con la firma!</string>
+ <string name="msg_mf_error_sub_stripped">¡No se pueden modificar subclaves desnudas %s!</string>
+ <string name="msg_mf_error_subkey_missing">¡Se intentó operar sobre una subclave desaparecida %s!</string>
+ <string name="msg_mf_error_conflicting_nfc_commands">No se puede mover la clave a la tarjeta inteligente en la misma operación que crea una firma en la tarjeta.</string>
+ <string name="msg_mf_error_duplicate_keytocard_for_slot">La tarjeta inteligente sólo soporta un lector por cada tipo de clave.</string>
+ <string name="msg_mf_error_invalid_flags_for_keytocard">Indicadores de clave inapropiados para la clave de la tarjeta inteligente.</string>
<string name="msg_mf_master">Modificando certificaciones maestras</string>
<string name="msg_mf_notation_empty">Añadiendo paquete de notación vacío</string>
<string name="msg_mf_notation_pin">Añadiendo paquete de notación de PIN</string>
- <string name="msg_mf_passphrase">Cambiando la frase-contraseña para el juego de claves</string>
- <string name="msg_mf_passphrase_key">Re-cifrando subclave %s con nueva frase-contraseña</string>
- <string name="msg_mf_passphrase_empty_retry">Fallo al establecer nueva frase-contraseña, intentándolo de nuevo con una antigua frase-contraseña vacía</string>
- <string name="msg_mf_passphrase_fail">¡La frase contraseña para la subclave no pudo cambiarse! (¿Tiene la subclave una diferente de la de las otras claves?)</string>
+ <string name="msg_mf_passphrase">Cambiando contraseña para el juego de claves</string>
+ <string name="msg_mf_pin">Cambiando el PIN en la tarjeta</string>
+ <string name="msg_mf_admin_pin">Cambiando el PIN de Admin en la tarjeta</string>
+ <string name="msg_mf_passphrase_key">Re-cifrando subclave %s con la nueva contraseña</string>
+ <string name="msg_mf_passphrase_empty_retry">El establecimiento de la nueva contraseña falló, volviéndolo a intentar con una contraseña antigua vacía.</string>
+ <string name="msg_mf_passphrase_fail">¡La contraseña para la subclave no se pudo cambiar! (¿Tiene una contraseña diferente a la del resto de las claves?)</string>
<string name="msg_mf_primary_replace_old">Reemplazando certificado de la identificación de usuario principal anterior</string>
<string name="msg_mf_primary_new">Generando nuevo certificado para nueva identificación de usuario primaria</string>
+ <string name="msg_mf_restricted_mode">Cambiando al modo de operación restringido</string>
<string name="msg_mf_subkey_change">Modificando subclave %s</string>
- <string name="msg_mf_error_subkey_missing">¡Se intentó operar sobre una subclave desaparecida %s!</string>
+ <string name="msg_mf_require_divert">Derivando a la tarjeta inteligente para operaciones de criptografía</string>
+ <string name="msg_mf_require_passphrase">Contraseña requerida para operaciones</string>
<string name="msg_mf_subkey_new">Añadiendo nueva subclave de tipo %s</string>
<string name="msg_mf_subkey_new_id">Nueva identificación de subclave: %s</string>
<string name="msg_mf_error_past_expiry">¡La fecha de expiración no puede ser del pasado!</string>
<string name="msg_mf_subkey_revoke">Revocando subclave %s</string>
<string name="msg_mf_subkey_strip">Desnudando subclave %s</string>
+ <string name="msg_mf_keytocard_start">Moviendo subclave %s a la tarjeta inteligente</string>
+ <string name="msg_mf_keytocard_finish">Se movió %1$s a la tarjeta inteligente %2$s</string>
<string name="msg_mf_success">Juego de claves modificado con éxito</string>
<string name="msg_mf_uid_add">Añadiendo identificación de usuario %s</string>
<string name="msg_mf_uid_primary">Cambiando identificación de usuario primaria a %s</string>
@@ -891,19 +1020,21 @@
<string name="msg_con_warn_delete_secret">Excepción al borrar fichero de caché de claves secretas (privadas)</string>
<!--Edit Key (higher level than modify)-->
<string name="msg_ed">Realizando operación en la clave</string>
- <string name="msg_ed_caching_new">Guardar en caché nueva frase-contraseña</string>
+ <string name="msg_ed_caching_new">Guardando en caché la nueva contraseña</string>
<string name="msg_ed_error_no_parcel">¡SaveKeyringParcel ausente! (esto es un fallo, por favor notifíquelo)</string>
<string name="msg_ed_error_key_not_found">¡Clave no encontrada!</string>
+ <string name="msg_ed_error_extract_public_upload">¡Error al extraer la clave pública para subirla!</string>
<string name="msg_ed_fetching">Descargando clave a modificar (%s)</string>
<string name="msg_ed_success">Operación en la clave completada</string>
<!--Promote key-->
- <string name="msg_pr">Promover clave pública a clave secreta (privada)</string>
- <string name="msg_pr_error_already_secret">¡La clave ya es una clave secreta (privada)!</string>
+ <string name="msg_pr">Promocionar clave pública a clave secreta (privada)</string>
+ <string name="msg_pr_all">Promocionando todas las subclaves</string>
<string name="msg_pr_error_key_not_found">¡Clave no encontrada!</string>
<string name="msg_pr_fetching">Descargar clave para modificar (%s)</string>
- <string name="msg_pr_success">La clave se promovió con éxito</string>
+ <string name="msg_pr_subkey_match">Promocionar la subclave: %s</string>
+ <string name="msg_pr_subkey_nomatch">La subclave no está en la Yubikey: %s</string>
+ <string name="msg_pr_success">La clave se promocionó con éxito</string>
<!--Other messages used in OperationLogs-->
- <string name="msg_ek_error_divert">¡La edición de claves NFC (aún) no está soportada!</string>
<string name="msg_ek_error_dummy">¡No se pudo editar el juego de claves (keyring) con la clave maestra desnuda!</string>
<string name="msg_ek_error_not_found">¡Clave no encontrada!</string>
<!--Messages for DecryptVerify operation-->
@@ -919,26 +1050,27 @@
<string name="msg_dc_clear_meta_size_unknown">Tamaño de fichero desconocido</string>
<string name="msg_dc_clear_meta_time">Hora de la modificación: %s</string>
<string name="msg_dc_clear_signature_bad">¡Comprobación de firma NO CORRECTA!</string>
- <string name="msg_dc_error_unsupported_hash_algo">¡Algoritmo de identificador criptográfico (hash) no soportado y potencialmente inseguro!</string>
<string name="msg_dc_clear_signature_check">Verificando datos de firma</string>
<string name="msg_dc_clear_signature_ok">Comprobación de firma CORRECTA</string>
<string name="msg_dc_clear_signature">Guardando datos de firma para más tarde</string>
<string name="msg_dc_clear">Procesando datos de texto no cifrado (`cleartext`)</string>
- <string name="msg_dc_error_bad_passphrase">Error al desbloquear clave, ¡frase-contraseña errónea!</string>
+ <string name="msg_dc_error_bad_passphrase">Error desbloqueando clave, ¡contraseña incorrecta!</string>
+ <string name="msg_dc_error_sym_passphrase">¡Error al descifrar datos! (¿frase-contraseña errónea?)</string>
+ <string name="msg_dc_error_corrupt_data">¡Los datos están corruptos!</string>
<string name="msg_dc_error_extract_key">¡Error desconocido al desbloquear clave!</string>
<string name="msg_dc_error_integrity_check">¡Error de comprobación de integridad!</string>
- <string name="msg_dc_error_integrity_missing">¡Verificación de integridad ausente! Esto puede ocurrir porque la aplicación de cifrado no está actualizada, o debido a un ataque desactualización.</string>
- <string name="msg_dc_error_invalid_siglist">¡No se encontraron datos de firma válidos!</string>
- <string name="msg_dc_error_io">¡Se encontró Excepción de E/S durante la operación!</string>
+ <string name="msg_dc_error_invalid_data">¡No se encontraron datos firmados o cifrados con OpenPGP válidos!</string>
+ <string name="msg_dc_error_io">¡Se encontró un error al leer los datos de entrada!</string>
+ <string name="msg_dc_error_input">¡Error al abrir el flujo de datos de entrada!</string>
<string name="msg_dc_error_no_data">¡No se encontraron datos cifrados en el flujo de datos (`stream`)!</string>
<string name="msg_dc_error_no_key">¡No se encontraron datos cifrados con clave secreta (privada) conocida en el flujo de datos (`stream`)!</string>
<string name="msg_dc_error_pgp_exception">¡Se encontró una excepción OpenPGP durante la operación!</string>
<string name="msg_dc_integrity_check_ok">¡Comprobación de integridad CORRECTA!</string>
<string name="msg_dc_ok_meta_only">Sólo fueron solicitados los metadatos, omitiendo descifrado</string>
<string name="msg_dc_ok">Descifrado/Verificación finalizado</string>
- <string name="msg_dc_pass_cached">Usando frase-contraseña desde caché</string>
+ <string name="msg_dc_pass_cached">Usando contraseña desde la caché</string>
<string name="msg_dc_pending_nfc">Se requiere credencial NFC, solicitando su introducción por el usuario...</string>
- <string name="msg_dc_pending_passphrase">Se requiere frase-contraseña, solicitando su introducción por el usuario...</string>
+ <string name="msg_dc_pending_passphrase">Contraseña requerida, solicitando su introducción al usuario...</string>
<string name="msg_dc_prep_streams">Preparando flujos de datos (`streams`) para descifrado</string>
<string name="msg_dc">Comenzando la operación de descifrado...</string>
<string name="msg_dc_sym_skip">Datos simétricos no permitidos, omitiendo...</string>
@@ -947,7 +1079,10 @@
<string name="msg_dc_trail_sym">Se encontró huella, datos cifrados simétricamente</string>
<string name="msg_dc_trail_unknown">Se encontró huella, datos de tipo desconocido</string>
<string name="msg_dc_unlocking">Desbloqueando clave secreta (privada)</string>
- <string name="msg_dc_old_symmetric_encryption_algo">¡Se ha usado un algoritmo de cifrado potencialmente inseguro!</string>
+ <string name="msg_dc_insecure_symmetric_encryption_algo">¡Se ha usado un algoritmo de cifrado no seguro! Esto puede ocurrir porque la aplicación no esté actualizada, o por un ataque.</string>
+ <string name="msg_dc_insecure_hash_algo">¡Se ha usado un algoritmo de identificador criptográfico (hash) no seguro! Esto puede ocurrir porque la aplicación no esté actualizada, o por un ataque de degradación de versión.</string>
+ <string name="msg_dc_insecure_mdc_missing">¡El paquete de Código de Detección de Modificación (MDC) está ausente! Esto puede ocurrir porque la aplicación de cifrado no esté actualizada, o por un ataque de degradación de versión.</string>
+ <string name="msg_dc_insecure_key">Clave no segura: O bien el número de bits de la clave RSA/DSA/ElGamal es demasiado pequeño o la curva/algoritmo ECC es considerado no seguro! Esto puede ocurrir porque la aplicación no esté actualizada, o por un ataque de degradación de versión.</string>
<!--Messages for VerifySignedLiteralData operation-->
<string name="msg_vl">Comenzando comprobación de firma</string>
<string name="msg_vl_error_no_siglist">No hay lista de firmas en los datos literales firmados</string>
@@ -968,20 +1103,18 @@
<string name="msg_se_error_input_uri_not_found">¡Error al abrir URI para lectura!</string>
<string name="msg_se_error_output_uri_not_found">¡Error al abrir URI para escritura!</string>
<string name="msg_se_error_too_many_inputs">¡Se especificaron más entradas que salidas! Probablemente esto es un error de programación, ¡por favor informe!</string>
- <string name="msg_se_warn_output_left">Se dejaron las salidas pero no las entradas. Probablemente esto es un error de programación, ¡por favor informe!</string>
<string name="msg_se_success">Operación de firmado/cifrado completada</string>
<!--Messages for PgpSignEncrypt operation-->
<string name="msg_pse_asymmetric">Preparando claves públicas para cifrado</string>
<string name="msg_pse_clearsign_only">¡El fimado de texto sin cifrar (cleartext) no está soportado!</string>
<string name="msg_pse_compressing">Preparando compresión</string>
<string name="msg_pse_encrypting">Cifrando datos</string>
- <string name="msg_pse_error_bad_passphrase">¡Frase-contraseña errónea!</string>
- <string name="msg_pse_error_hash_algo">¡El algoritmo de identificación criptográfica (hash) solicitado no está soportado por esta clave!</string>
+ <string name="msg_pse_error_bad_passphrase">¡Contraseña incorrecta!</string>
<string name="msg_pse_error_io">¡Se encontró una excepción de E/S durante la operación!</string>
<string name="msg_pse_error_key_sign">¡La clave de firmado seleccionada no puede firmar datos!</string>
<string name="msg_pse_error_sign_key">¡Error al descargar clave de firmado!</string>
<string name="msg_pse_error_nfc">¡Error de datos NFC!</string>
- <string name="msg_pse_error_no_passphrase">¡No se proporcionó frase-contraseña!</string>
+ <string name="msg_pse_error_no_passphrase">¡No se proporcionó contraseña!</string>
<string name="msg_pse_error_pgp">¡Error interno de OpenPGP!</string>
<string name="msg_pse_error_sig">¡Se encontró una excepción de firma OpenPGP!</string>
<string name="msg_pse_error_unlock">¡Error desconocido al desbloquear clave!</string>
@@ -990,7 +1123,7 @@
<string name="msg_pse_key_warn">Clave para cifrado errónea: %s</string>
<string name="msg_pse_ok">¡Operación de firmado/cifrado completada!</string>
<string name="msg_pse_pending_nfc">Se requiere credencial NFC, solicitando su introducción al usuario...</string>
- <string name="msg_pse_pending_passphrase">Se requiere frase-contraseña, solicitando su introducción al usuario.</string>
+ <string name="msg_pse_pending_passphrase">Contraseña requerida, solicitando su introducción al usuario...</string>
<string name="msg_pse_signing">Firmando datos (sin cifrado)</string>
<string name="msg_pse_signing_cleartext">Creando firma de texto sin cifrar (cleartext)</string>
<string name="msg_pse_signing_detached">Creando firma desacoplada</string>
@@ -1010,9 +1143,9 @@
<string name="msg_crt_error_master_not_found">¡Clave maestra no encontrada!</string>
<string name="msg_crt_error_nothing">¡No se certificaron claves!</string>
<string name="msg_crt_error_unlock">¡Error desbloqueando clave maestra!</string>
- <string name="msg_crt_error_divert">¡La certificación con NFC no está soportada (aún)!</string>
<string name="msg_crt">Certificando juegos de claves</string>
<string name="msg_crt_master_fetch">Descargando clave maestra certificante</string>
+ <string name="msg_crt_nfc_return">Volviendo a la pantalla NFC</string>
<string name="msg_crt_save">Guardando clave certificada %s</string>
<string name="msg_crt_saving">Guardando juegos de claves</string>
<string name="msg_crt_unlock">Desbloqueando clave maestra</string>
@@ -1020,6 +1153,7 @@
<string name="msg_crt_warn_not_found">¡Clave no encontrada!</string>
<string name="msg_crt_warn_cert_failed">¡La generación de certificado falló!</string>
<string name="msg_crt_warn_save_failed">¡La operación de guardado falló!</string>
+ <string name="msg_crt_warn_upload_failed">¡Fallo en la operación de subida!</string>
<string name="msg_crt_upload_success">Clave subida al servidor con éxito</string>
<plurals name="msg_import">
<item quantity="one">Importando clave</item>
@@ -1028,13 +1162,14 @@
<string name="msg_import_fetch_error_decode">¡Error al descifrar juego de claves descargado!</string>
<string name="msg_import_fetch_error">¡La clave no se pudo descargar! (¿problemas con la red?)</string>
<string name="msg_import_fetch_keybase">Descargando desde keybase.io: %s</string>
- <string name="msg_import_fetch_keyserver_error">¡No se pudo descargar la clave desde keybase!</string>
+ <string name="msg_import_fetch_keyserver_error">No se pudo obtener clave de los servidores de claves: %s</string>
<string name="msg_import_fetch_keyserver">Descargando desde el servidor de claves: %s</string>
<string name="msg_import_fetch_keyserver_ok">La clave se descargó con éxito</string>
<string name="msg_import_keyserver">Usando el servidor de claves %s</string>
<string name="msg_import_fingerprint_error">¡La huella de validación de la clave descargada no coincidió con la esperada!</string>
- <string name="msg_import_fingerprint_ok">Comprobación de la huella de validación correcta</string>
+ <string name="msg_import_fingerprint_ok">La comprobación de la huella de validación de clave es Correcta</string>
<string name="msg_import_merge">Incorporando datos descargados</string>
+ <string name="msg_import_merge_error">¡Error al fusionar datos descargados!</string>
<string name="msg_import_error">¡La operación de importación falló!</string>
<string name="msg_import_error_io">¡Fallo en la operación de importación debido a un error de e/s!</string>
<string name="msg_import_partial">¡Operación de importado completada, con errores!</string>
@@ -1043,8 +1178,10 @@
<item quantity="one">Exportando una clave</item>
<item quantity="other">Exportando %d claves</item>
</plurals>
+ <string name="msg_export_file_name">Nombre de fichero: %s</string>
<string name="msg_export_all">Exportando todas las claves</string>
<string name="msg_export_public">Exportando clave pública %s</string>
+ <string name="msg_export_upload_public">Subiendo clave pública %s</string>
<string name="msg_export_secret">Exportando clave secreta (privada) %s</string>
<string name="msg_export_error_no_file">¡No se especificó nombre de fichero!</string>
<string name="msg_export_error_fopen">¡Error al abrir el fichero!</string>
@@ -1054,7 +1191,9 @@
<string name="msg_export_error_db">¡Error de base de datos!</string>
<string name="msg_export_error_io">¡Error de entrada/salida!</string>
<string name="msg_export_error_key">¡Error al preprocesar los datos de la clave!</string>
+ <string name="msg_export_error_upload">¡Error al subir clave al servidor! Por favor, compruebe su conexión a Internet</string>
<string name="msg_export_success">Operación de exportado exitosa</string>
+ <string name="msg_export_upload_success">Subida al servidor de claves completada</string>
<string name="msg_del_error_empty">¡No hay nada que borrar!</string>
<string name="msg_del_error_multi_secret">¡Las claves secretas (privadas) sólo se pueden borrar individualmente!</string>
<plurals name="msg_del">
@@ -1072,6 +1211,11 @@
<item quantity="one">No se pudo borrar una clave</item>
<item quantity="other">No se pudieron borrar %d claves</item>
</plurals>
+ <string name="msg_revoke_error_empty">¡Nada que revocar!</string>
+ <string name="msg_revoke_error_not_found">¡No se pudo encontrar clave a revocar!</string>
+ <string name="msg_revoke_key">Revocando clave %s</string>
+ <string name="msg_revoke_key_fail">Fallo al revocar clave</string>
+ <string name="msg_revoke_ok">Clave revocada con éxito</string>
<string name="msg_acc_saved">Cuenta guardada</string>
<string name="msg_download_success">¡Descargado con éxito!</string>
<string name="msg_download_no_valid_keys">¡No se encontraron claves vigentes en el fichero/portapapeles!</string>
@@ -1084,6 +1228,14 @@
<string name="msg_download_too_many_responses">La solicitud de búsqueda de clave devolvió demasiados candidatos. ¡Por favor refine su petición!</string>
<string name="msg_download_query_too_short_or_too_many_responses">O bien no hay claves o se han encontrado demasiadas. ¡Por favor mejore su petición!</string>
<string name="msg_download_query_failed">Ocurrió un error al buscar claves.</string>
+ <!--Messages for Keybase Verification operation-->
+ <string name="msg_keybase_verification">Intentando la verificación con Keybase para %s</string>
+ <string name="msg_keybase_error_no_prover">No se encontró prueba verificadora para %s</string>
+ <string name="msg_keybase_error_fetching_evidence">Problema al descargar prueba</string>
+ <string name="msg_keybase_error_key_mismatch">La huella de validación de la clave no coincide con la prueba publicada</string>
+ <string name="msg_keybase_error_dns_fail">Fallo al obtener Registro DNS TXT</string>
+ <string name="msg_keybase_error_specific">%s</string>
+ <string name="msg_keybase_error_msg_payload_mismatch">La prueba descifrada publicada no coincide con el valor esperado</string>
<!--Messages for Export Log operation-->
<string name="msg_export_log_start">Exportando registro (log)</string>
<string name="msg_export_log_error_fopen">Error abriendo fichero</string>
@@ -1091,17 +1243,31 @@
<string name="msg_export_log_error_writing">¡Error de E/S al escribir al fichero!</string>
<string name="msg_export_log_success">¡Registro (log) exportado con éxito!</string>
<!--PassphraseCache-->
- <string name="passp_cache_notif_click_to_clear">Haga clic para eliminar las frases-contraseña almacenadas en caché</string>
- <string name="passp_cache_notif_n_keys">OpenKeychain ha almacenado en caché %d frases-contraseña</string>
- <string name="passp_cache_notif_keys">Frases-contraseña almacenadas en caché:</string>
- <string name="passp_cache_notif_clear">Limpiar caché</string>
- <string name="passp_cache_notif_pwd">Frase-contraseña</string>
+ <string name="passp_cache_notif_click_to_clear">Pulsar para limpiar contraseñas.</string>
+ <plurals name="passp_cache_notif_n_keys">
+ <item quantity="one">%d contraseña recordada</item>
+ <item quantity="other">%d contraseñas recordadas</item>
+ </plurals>
+ <string name="passp_cache_notif_keys">Contraseñas recordadas</string>
+ <string name="passp_cache_notif_clear">Limpiar contraseñas</string>
+ <string name="passp_cache_notif_pwd">Contraseña</string>
+ <!--Keyserver sync-->
+ <string name="keyserver_sync_orbot_notif_title">La sincronizacion desde la nube requiere Orbot</string>
+ <string name="keyserver_sync_orbot_notif_msg">Pulse para iniciar Orbot</string>
+ <string name="keyserver_sync_orbot_notif_start">Iniciar Orbot</string>
+ <string name="keyserver_sync_orbot_notif_ignore">Directamente</string>
<!--First Time-->
<string name="first_time_text1">¡Recupere su privacidad con OpenKeychain!</string>
<string name="first_time_create_key">Crear mi clave</string>
<string name="first_time_import_key">Importar clave desde fichero</string>
<string name="first_time_yubikey">Usar Yubikey NEO</string>
<string name="first_time_skip">Omitir configuración</string>
+ <string name="first_time_blank_yubikey">¿Desea usar esta YubiKey NEO en blanco con OpenKeychain?\n\n¡Por favor, retire ahora la YubiKey, se le solicitará cuando sea necesaria de nuevo!</string>
+ <string name="first_time_blank_yubikey_yes">Usar esta YubiKey</string>
+ <string name="backup_text">¡Las copias de seguridad que incluyen sus propias claves nunca se deben compartir con otras personas!</string>
+ <string name="backup_all">Todas las claves + sus propias claves</string>
+ <string name="backup_public_keys">Todas las claves</string>
+ <string name="backup_section">Copia de seguridad</string>
<!--unsorted-->
<string name="section_certifier_id">Certificador</string>
<string name="section_cert">Detalles del certificado</string>
@@ -1111,47 +1277,53 @@
<string name="certs_text">Aquí sólo se muestran auto-certificados validados y certificados validados creados con sus claves.</string>
<string name="section_uids_to_certify">Identificaciones para</string>
<string name="certify_text">Las claves que está importando contienen \"identidades\": nombres y direcciones de correo electrónico. Para confirmación seleccione exactamente aquellas que coincidan con lo que esperaba.</string>
- <string name="certify_fingerprint_text">Compare la huella de validación mostrada, caracter por caracter, con la que se muestra en el dispositivo de su colega.</string>
- <string name="certify_fingerprint_text2">¿Coinciden las huellas de validación mostradas?</string>
+ <string name="certify_fingerprint_text">Compare la huella de validación de clave mostrada, caracter por caracter, con la que se muestra en el dispositivo de su colega.</string>
+ <string name="certify_fingerprint_text2">¿Coinciden las huellas de validación de clave mostradas?</string>
<string name="label_revocation">Razón de la revocación</string>
<string name="label_cert_type">Tipo</string>
<string name="error_key_not_found">¡Clave no encontrada!</string>
<string name="error_key_processing">¡Error procesando clave!</string>
<string name="key_stripped">desnuda, sin clave</string>
- <string name="key_divert">desviar a tarjeta-inteligente/NFC</string>
- <string name="key_no_passphrase">sin frase-contraseña</string>
+ <string name="key_divert">derivar a tarjeta inteligente</string>
+ <string name="key_no_passphrase">sin contraseña</string>
<string name="key_unavailable">no disponible</string>
<string name="secret_cannot_multiple">¡Sus claves propias sólo pueden ser borradas individualmente!</string>
<string name="title_view_cert">Ver detalles del certificado</string>
<string name="unknown_algorithm">desconocido</string>
<string name="can_sign_not">no puede firmarse</string>
<string name="error_no_encrypt_subkey">¡No hay subclave de cifrado disponible!</string>
- <string name="info_no_manual_account_creation">No crear Cuentas-OpenKeychain manualmente.\nPara más información, vea la Ayuda.</string>
<string name="contact_show_key">Mostrar clave (%s)</string>
<string name="swipe_to_update">Gesto de barrido hacia abajo para actualizar desde el servidor de claves</string>
<string name="error_no_file_selected">¡Seleccione al menos un fichero a cifrar!</string>
- <string name="error_multi_not_supported">El guardado de múltiples ficheros no está soportado. Esto es una limitación de su Android actual.</string>
+ <string name="error_multi_files">El guardado de múltiples ficheros no está soportado. Esta es una limitación del actual Android.</string>
+ <string name="error_multi_clipboard">El cifrado de múltiples ficheros hacia el portapapeles no está soportado.</string>
+ <string name="error_detached_signature">La operación de sólo-firmado de ficheros binarios no está soportada, seleccione al menos una clave de cifrado.</string>
+ <string name="error_empty_text">¡Escriba algún texto a cifrar!</string>
<string name="key_colon">Clave:</string>
- <string name="exchange_description">Para iniciar un intercambio de claves, en el lado derecho elija el número de participantes, y luego pulse el botón \"Iniciar intercambio\".\n\nSe le formularán dos preguntas más para asegurar que sólo están en el intercambio los participantes debidos, y que sus huellas de validación son correctas.</string>
+ <string name="exchange_description">Para iniciar un intercambio de claves, en el lado derecho elija el número de participantes, y luego pulse el botón \"Iniciar intercambio\".\n\nSe le formularán dos preguntas más para asegurar que sólo están en el intercambio los participantes debidos, y que las huellas de validación de sus claves son correctas.</string>
<string name="btn_start_exchange">Comenzar intercambio</string>
<string name="user_id_none"><![CDATA[<ninguno>]]></string>
+ <!--Android Account-->
+ <string name="account_no_manual_account_creation">No puede crear cuentas de OpenKeychain manualmente.</string>
+ <string name="account_privacy_title">Privacidad</string>
+ <string name="account_privacy_text">OpenKeychain no sincroniza sus contactos con Internet. Sólo vincula contactos a claves basándose en nombres y direcciones de correo electrónico. Hace esto de forma desconectada en su dispositivo.</string>
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
<string name="title_unlock_method">Escoja un método de desbloqueo</string>
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
- <string name="enter_passphrase">Introduzca frase-contraseña</string>
- <string name="passphrase">Frase-contraseña</string>
- <string name="noPassphrase">Sin frase-contraseña</string>
- <string name="no_passphrase_set">No se estableció frase-contraseña</string>
- <string name="passphrases_match">Las frases-contraseña no coinciden</string>
- <string name="passphrase_saved">Frase-contraseña guardada</string>
- <string name="passphrase_invalid">Frase-contraseña no válida</string>
- <string name="missing_passphrase">Frase-contraseña ausente</string>
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
+ <string name="enter_passphrase">Introduzca contraseña</string>
+ <string name="passphrase">Contraseña</string>
+ <string name="noPassphrase">Sin contraseña</string>
+ <string name="no_passphrase_set">No se estableció contraseña</string>
+ <string name="passphrases_match">Las contraseñas coinciden</string>
+ <string name="passphrase_saved">Contraseña guardada</string>
+ <string name="passphrase_invalid">Contraseña no válida</string>
+ <string name="missing_passphrase">Contraseña desaparecida</string>
<string name="passphrase_again">De nuevo</string>
<string name="lockpattern">Patrón de bloqueo</string>
<string name="lockpatternNFC">NFC + patrón de bloqueo</string>
<string name="unlock_method">Método de desbloqueo</string>
- <string name="set_passphrase">Establecer frase-contraseña</string>
+ <string name="set_passphrase">Establecer contraseña</string>
<string name="draw_lockpattern">Dibujar patrón de bloqueo</string>
<string name="nfc_title">NFC</string>
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
@@ -1161,4 +1333,62 @@
<string name="nfc_write_succesful">Se escribió con éxito en la etiqueta NFC</string>
<string name="unlocked">Desbloqueado</string>
<string name="nfc_settings">Configuración</string>
+ <string name="snack_yubikey_view">Ver</string>
+ <string name="snack_yubikey_import">Importar</string>
+ <string name="button_bind_key">Ligar clave</string>
+ <string name="yubikey_serno">Nº de serie: %s</string>
+ <string name="yubikey_key_holder">Titular de la clave:</string>
+ <string name="yubikey_key_holder_not_set">Titular de la clave: &lt;no establecido&gt;</string>
+ <string name="yubikey_status_bound">La YubiKey coincide y está ligada a la clave</string>
+ <string name="yubikey_status_unbound">La YubiKey coincide, puede ligarse a la clave</string>
+ <string name="yubikey_status_partly">La YubiKey coincide, parcialmente ligada a la clave</string>
+ <string name="yubikey_create">Sostenga la YubiKey contra el reverso de su dispositivo.</string>
+ <string name="btn_import">Importar</string>
+ <string name="snack_yubi_other">¡Clave almacenada en YubiKey distinta!</string>
+ <string name="error_nfc">Error de NFC: %s</string>
+ <plurals name="error_pin">
+ <item quantity="one">¡PIN incorrecto!\nLe queda %d intento.</item>
+ <item quantity="other">¡PIN incorrecto!\nLe quedan %d intentos.</item>
+ </plurals>
+ <string name="error_nfc_terminated">YubiKey en estado de vencimiento.</string>
+ <string name="error_nfc_wrong_length">El PIN introducido es demasiado corto. Los PINs tienen al menos 6 dígitos de largo.</string>
+ <string name="error_nfc_conditions_not_satisfied">Condiciones de uso no satisfechas.</string>
+ <string name="error_nfc_security_not_satisfied">Estado de seguridad no satisfecho.</string>
+ <string name="error_nfc_authentication_blocked">PIN bloqueado después de demasiados intentos.</string>
+ <string name="error_nfc_data_not_found">Clave u objeto no encontrado.</string>
+ <string name="error_nfc_unknown">Error desconocido</string>
+ <string name="error_nfc_bad_data">La YubiKey informó de datos no válidos.</string>
+ <string name="error_nfc_chaining_error">La YubiKey esperaba el último comando en una cadena.</string>
+ <string name="error_nfc_header">La YubiKey informó de un byte %s no válido.</string>
+ <string name="error_nfc_tag_lost">La YubiKey ha sido retirada demasiado pronto. Mantenga la YubiKey en el reverso hasta que la operación finalice.</string>
+ <string name="error_nfc_try_again">Intentar de nuevo</string>
+ <string name="error_pin_nodefault">¡El PIN por defecto fue rechazado!</string>
+ <string name="error_temp_file">Error al crear fichero temporal.</string>
+ <string name="btn_delete_original">Borrar fichero original</string>
+ <string name="snack_encrypt_filenames_on">Los nombres de fichero <b>están</b> cifrados.</string>
+ <string name="snack_encrypt_filenames_off">Los nombres de fichero <b>no están</b> cifrados.</string>
+ <string name="snack_armor_on">Salida codificada como Texto.</string>
+ <string name="snack_armor_off">Salida codificada como Binario.</string>
+ <string name="snack_compression_on">Compresión <b>habilitada</b>.</string>
+ <string name="snack_compression_off">Compresión <b>deshabilitada</b>.</string>
+ <string name="error_loading_keys">¡Error al cargar claves!</string>
+ <string name="error_empty_log">(error, registro (log) vacío)</string>
+ <string name="error_reading_text">¡No se pudo leer entrada a descifrar!</string>
+ <string name="filename_unknown">&lt;sin nombre de fichero&gt;</string>
+ <string name="filename_unknown_text">&lt;datos en texto sin cifrar&gt;</string>
+ <string name="intent_show">Mostrar contenido firmado/cifrado</string>
+ <string name="view_internal">Ver en OpenKeychain</string>
+ <string name="error_preparing_data">¡Error al preparar datos!</string>
+ <string name="label_clip_title">Datos cifrados</string>
+ <string name="progress_processing">Procesando...</string>
+ <string name="error_saving_file">¡Error al guardar fichero!</string>
+ <string name="file_saved">¡Fichero guardado!</string>
+ <string name="file_delete_ok">Fichero original borrado.</string>
+ <string name="file_delete_none">¡No se borró ningún fichero! (¿ya se había borrado?)</string>
+ <string name="file_delete_exception">¡No se pudo borrar el fichero original!</string>
+ <string name="error_clipboard_empty">¡Portapapeles vacío!</string>
+ <string name="error_clipboard_copy">¡Error al copiar datos al portapapeles!</string>
+ <string name="error_scan_fp">¡Error al escanear huella de validación de clave!</string>
+ <string name="error_scan_match">¡Las huellas de validación de clave no coinciden!</string>
+ <string name="error_expiry_past">¡La fecha de caducidad es del pasado!</string>
</resources>
diff --git a/OpenKeychain/src/main/res/values-et/strings.xml b/OpenKeychain/src/main/res/values-et/strings.xml
index cc9061149..7952c6ab0 100644
--- a/OpenKeychain/src/main/res/values-et/strings.xml
+++ b/OpenKeychain/src/main/res/values-et/strings.xml
@@ -6,14 +6,11 @@
<string name="title_decrypt">Dekrüpteeri</string>
<string name="title_edit_key">Muuda võtit</string>
<string name="title_import_keys">Impordi võtmeid</string>
- <string name="title_export_key">Ekspordi võti</string>
- <string name="title_export_keys">Ekspordi võtmed</string>
<string name="title_key_not_found">Võtit ei leitud</string>
<string name="title_send_key">Lae võtmeserverisse</string>
<string name="title_help">Abi</string>
<!--section-->
<!--button-->
- <string name="btn_save">Salvesta</string>
<string name="btn_do_not_save">Katkesta</string>
<string name="btn_delete">Kustuta</string>
<string name="btn_export_to_server">Saada võtmeserverisse</string>
@@ -25,8 +22,6 @@
<string name="menu_search">Otsi</string>
<!--label-->
<string name="label_file">Fail</string>
- <string name="label_no_passphrase">Salasõnet pole</string>
- <string name="label_passphrase">Salasõne</string>
<string name="label_algorithm">Algoritm</string>
<string name="label_creation">Loodud</string>
<string name="label_expiry">Aegub</string>
@@ -35,6 +30,12 @@
<string name="label_name">Nimi</string>
<string name="label_comment">Kommentaar</string>
<string name="label_email">E-mail</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <!--proxy type choices and values-->
+ <!--OrbotHelper strings-->
+ <!--InstallDialogFragment strings-->
+ <!--StartOrbotDialogFragment strings-->
<string name="secret_key">Salajane võti:</string>
<!--choice-->
<string name="choice_15secs">15 sekundit</string>
@@ -56,8 +57,6 @@
<string name="error_message">Viga: %s</string>
<!--key flags-->
<!--sentences-->
- <string name="wrong_passphrase">Vale salasõne</string>
- <string name="passphrases_do_not_match">Salasõned ei ühti.</string>
<string name="passphrase_for_symmetric_encryption">Sümmeetriline krüpteering</string>
<!--errors
no punctuation, all lowercase,
@@ -76,13 +75,18 @@
<!--compression-->
<!--Help-->
<!--Import-->
+ <!--Import from URL-->
<!--Generic result toast-->
<!--Import result toast-->
<!--Delete result toast-->
+ <!--Revoke result toast (snackbar)-->
<!--Certify result toast-->
<!--Intent labels-->
<!--Remote API-->
<!--Share-->
+ <!--retry upload dialog-->
+ <!--Delete or revoke private key dialog-->
+ <!--Delete Or Revoke Dialog spinner-->
<!--Key list-->
<!--Key view-->
<!--Key trust-->
@@ -90,6 +94,7 @@
<!--Edit key-->
<!--Create key-->
<!--View key-->
+ <!--Add/Edit keyserver-->
<!--Navigation Drawer-->
<!--hints-->
<!--certs-->
@@ -108,12 +113,15 @@
<!--Messages for VerifySignedLiteralData operation-->
<!--Messages for SignEncrypt operation-->
<!--Messages for PgpSignEncrypt operation-->
+ <!--Messages for Keybase Verification operation-->
<!--Messages for Export Log operation-->
<!--PassphraseCache-->
+ <!--Keyserver sync-->
<!--First Time-->
<!--unsorted-->
+ <!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
</resources>
diff --git a/OpenKeychain/src/main/res/values-eu/strings.xml b/OpenKeychain/src/main/res/values-eu/strings.xml
index 784217b95..22b77242e 100644
--- a/OpenKeychain/src/main/res/values-eu/strings.xml
+++ b/OpenKeychain/src/main/res/values-eu/strings.xml
@@ -7,51 +7,60 @@
<string name="title_encrypt_text">Enkriptatu</string>
<string name="title_encrypt_files">Enkriptatu</string>
<string name="title_decrypt">Dekriptatu</string>
- <string name="title_unlock">Desblokeatu Giltza</string>
<string name="title_add_subkey">Gehitu azpigiltza</string>
<string name="title_edit_key">Editatu Giltza</string>
<string name="title_preferences">Ezarpenak</string>
<string name="title_api_registered_apps">Aplikazioak</string>
- <string name="title_key_server_preference">Giltza-zerbitzariak</string>
- <string name="title_change_passphrase">Aldatu Sar-esaldia</string>
+ <string name="title_key_server_preference">OpenPGP giltza-zerbitzariak</string>
+ <string name="title_change_passphrase">Aldatu Sarhitza</string>
+ <string name="title_share_fingerprint_with">Elkarbanatu hatz-aztarna honekin...</string>
<string name="title_share_key">Elkarbanatu giltza honekin...</string>
<string name="title_share_file">Elkarbanatu agiria honekin...</string>
<string name="title_share_message">Elkarbanatu idazkia honekin...</string>
<string name="title_encrypt_to_file">Enkriptatu Agirira</string>
<string name="title_decrypt_to_file">Dekriptatu Agirira</string>
<string name="title_import_keys">Inportatu Giltzak</string>
- <string name="title_export_key">Esportatu Giltza</string>
- <string name="title_export_keys">Esportatu Giltzak</string>
+ <string name="title_export_key">Babeskopiatu Giltza</string>
+ <string name="title_export_keys">Babeskopiatu Giltzak</string>
<string name="title_key_not_found">Giltza Ez da Aurkitu</string>
<string name="title_send_key">Igo Giltza-zerbitzarira</string>
- <string name="title_certify_key">Baieztatu Giltza</string>
+ <string name="title_certify_key">Baieztatutako Giltza</string>
<string name="title_key_details">Giltzaren Xehetasunak</string>
<string name="title_help">Laguntza</string>
<string name="title_log_display">Oharra</string>
<string name="title_exchange_keys">Trukatu Giltzak</string>
- <string name="title_advanced_key_info">Giltza Argibide Aurreratuak</string>
+ <string name="title_advanced_key_info">Argibide Hedatuak</string>
+ <string name="title_delete_secret_key">Ezabatu ZURE \'%s\' giltza?</string>
<string name="title_export_log">Esportatu Oharra</string>
<string name="title_manage_my_keys">Kudeatu nire giltzak</string>
<!--section-->
<string name="section_user_ids">Nortasunak</string>
+ <string name="section_yubikey">YubiKey</string>
<string name="section_linked_system_contact">Loturatutako Sistema Harremana</string>
- <string name="section_should_you_trust">Fildatu behar zara giltza honetaz?</string>
+ <string name="section_should_you_trust">Fidatu behar zara giltza honetaz?</string>
+ <string name="section_proof_details">Probatu egiaztapena</string>
+ <string name="section_cloud_evidence">Hodeiko probak</string>
<string name="section_keys">Azpigiltzak</string>
<string name="section_cloud_search">Hodei bilaketa</string>
- <string name="section_passphrase_cache">Sar-esaldi Katxea</string>
+ <string name="section_passphrase_cache">Sarhitz/PIN Kudeaketa</string>
+ <string name="section_proxy_settings">Proxy Ezarpenak</string>
+ <string name="section_gui">Interfazea</string>
<string name="section_certify">Baieztatu</string>
<string name="section_actions">Ekintzak</string>
<string name="section_share_key">Giltza</string>
<string name="section_key_server">Giltza-zerbitzaria</string>
+ <string name="section_fingerprint">Hatz-aztarna</string>
<string name="section_encrypt">Enkriptatu</string>
- <string name="section_decrypt">Dekriptatu</string>
+ <string name="section_decrypt">Dekriptatu / Egiaztatu</string>
<string name="section_current_expiry">Oraingo epemuga</string>
<string name="section_new_expiry">Epemuga berria</string>
<!--button-->
<string name="btn_decrypt_verify_file">Dekriptatu, egiaztatu eta gorde agiria</string>
<string name="btn_encrypt_share_file">Enkriptatu eta elkarbanatu agiria</string>
<string name="btn_encrypt_save_file">Enkriptatu eta gorde agiria</string>
+ <string name="btn_save_file">Gorde agiria</string>
<string name="btn_save">Gorde</string>
+ <string name="btn_view_log">Ikusi oharra</string>
<string name="btn_do_not_save">Ezeztatu</string>
<string name="btn_delete">Ezabatu</string>
<string name="btn_no_date">Epemuga gabe</string>
@@ -60,22 +69,27 @@
<string name="btn_next">Hurrengoa</string>
<string name="btn_back">Atzera</string>
<string name="btn_no">Ez</string>
+ <string name="btn_match">Hatz-aztarnak bat datoz</string>
<string name="btn_share_encrypted_signed">Enkriptatu eta elkarbanatu idazkia</string>
<string name="btn_copy_encrypted_signed">Enkriptatu eta kopiatu idazkia</string>
<string name="btn_view_cert_key">Ikusi egiaztagiri giltza</string>
<string name="btn_create_key">Sortu giltza</string>
<string name="btn_add_files">Gehitu agiria(k)</string>
- <string name="btn_add_share_decrypted_text">Elkarbanatu dekriptaturiko idazkia</string>
- <string name="btn_decrypt_clipboard">Dekriptatu idazkia gakotik</string>
- <string name="btn_decrypt_and_verify">eta egiaztatu sinadurak</string>
- <string name="btn_decrypt_files">Dekriptatu agiriak</string>
+ <string name="btn_share_decrypted_text">Elkarbanatu dekriptaturiko idazkia</string>
+ <string name="btn_copy_decrypted_text">Kopiatu dekriptaturiko idazkia</string>
+ <string name="btn_decrypt_clipboard">Irakurri gakotik</string>
+ <string name="btn_decrypt_files">Hautatu sarrera agiria</string>
<string name="btn_encrypt_files">Enkriptatu agiriak</string>
<string name="btn_encrypt_text">Enkriptatu idazkia</string>
<string name="btn_add_email">Gehitu post@ helbide gehigarriak</string>
+ <string name="btn_unlock">Desblokeatu</string>
+ <string name="btn_add_keyserver">Gehitu</string>
+ <string name="btn_save_default">Gorde berezkoa bezala</string>
+ <string name="btn_saved">Gordeta!</string>
<!--menu-->
<string name="menu_preferences">Ezarpenak</string>
<string name="menu_help">Laguntza</string>
- <string name="menu_export_key">Esportatu agirira</string>
+ <string name="menu_export_key">Babeskopiatu Agirira</string>
<string name="menu_delete_key">Ezabatu giltza</string>
<string name="menu_manage_keys">Kudeatu nire giltzak</string>
<string name="menu_search">Bilatu</string>
@@ -84,33 +98,43 @@
<string name="menu_encrypt_to">Enkriptatu hona...</string>
<string name="menu_select_all">Hautatu denak</string>
<string name="menu_export_all_keys">Esportatu giltza guztiak</string>
- <string name="menu_advanced">Erakutsi argibide aurreratuak</string>
+ <string name="menu_update_all_keys">Eguneratu giltza guztiak</string>
+ <string name="menu_advanced">Argibide Hedatuak</string>
+ <string name="menu_certify_fingerprint">Baieztatu hatz-aztarna alderaketa bidez</string>
<string name="menu_export_log">Esportatu Oharra</string>
+ <string name="menu_keyserver_add">Gehitu</string>
<!--label-->
<string name="label_message">Idazkia</string>
<string name="label_file">Agiria</string>
<string name="label_files">Agiria(k)</string>
<string name="label_file_colon">Agiria:</string>
- <string name="label_no_passphrase">Sar-esaldirik ez</string>
- <string name="label_passphrase">Sar-esaldia</string>
+ <string name="label_no_passphrase">Sarhitzik gabe</string>
+ <string name="label_passphrase">Sarhitza</string>
<string name="label_unlock">Desblokeatzen...</string>
- <string name="label_passphrase_again">Berregin Sar-esaldia</string>
- <string name="label_show_passphrase">Erakutsi Sar-esaldia</string>
+ <string name="label_passphrase_again">Berridatzi Sarhitza</string>
+ <string name="label_show_passphrase">Erakutsi Sarhitza</string>
<string name="label_algorithm">Algoritmoa</string>
<string name="label_ascii_armor">ASCII Armor agiria</string>
<string name="label_file_ascii_armor">Gaitu ASCII Armor</string>
+ <string name="label_write_version_header">Jakinarazi besteei OpenKeychain erabiltzen ari zarela</string>
+ <string name="label_write_version_header_summary">\'OpenKeychain v2.7\' idazten du OpenPGP sinadura, idazkia zifratzen du eta giltzak esportatzen ditu</string>
<string name="label_use_default_yubikey_pin">Erabili berezko YubiKey PIN-a</string>
- <string name="label_asymmetric_from">Sinatzailea:</string>
+ <string name="label_use_num_keypad_for_yubikey_pin">Erabili zenbaki teklatua YubiKey PIN-erako</string>
+ <string name="label_label_use_default_yubikey_pin_summary">Berezko PIN (123456) erabiltzen du NFC bidezko YubiKeys sarbiderako </string>
+ <string name="label_asymmetric_from">Hasi saioa honekin:</string>
<string name="label_to">Enkriptatu hona:</string>
+ <string name="label_delete_after_encryption">Ezabatu agiriak enkriptatu ondoren</string>
<string name="label_delete_after_decryption">Ezabatu dekriptatu ondoren</string>
<string name="label_encryption_algorithm">Enkriptaketa algoritmoa</string>
<string name="label_hash_algorithm">Hash algoritmoa</string>
- <string name="label_symmetric">Enkriptatu sar-esaldiarekin</string>
- <string name="label_passphrase_cache_ttl">Katxe denbora</string>
+ <string name="label_symmetric">Enkriptatu sarhitzarekin</string>
+ <string name="label_passphrase_cache_ttl">Gogoratu ordua</string>
+ <string name="label_passphrase_cache_subs">Gogoratu sarhitzak azpigiltzaz</string>
<string name="label_message_compression">Idazki konpresioa</string>
<string name="label_file_compression">Agiri konpresioa</string>
- <string name="label_keyservers">Giltza-zerbitzariak</string>
+ <string name="label_keyservers">Hautatu OpenPGP giltza-zerbitzariak</string>
<string name="label_key_id">Giltza ID-a</string>
+ <string name="label_key_created">Giltza sortuta %s</string>
<string name="label_creation">Sortzea</string>
<string name="label_expiry">Epemuga</string>
<string name="label_usage">Erabilpena</string>
@@ -121,11 +145,57 @@
<string name="label_comment">Aipamena</string>
<string name="label_email">Post@</string>
<string name="label_send_key">Aldiberetu hodeiarekin</string>
+ <string name="label_fingerprint">Hatz-aztarna</string>
<string name="expiry_date_dialog_title">Ezarri epemuga eguna</string>
+ <string name="label_keyservers_title">Giltza-zerbitzariak</string>
+ <string name="label_keyserver_settings_hint">Arrastatu hurrenkera aldatzeko, ikutu editatu/ezabatzeko</string>
+ <string name="label_selected_keyserver_title">Giltza-zerbitzaria hautatuta</string>
<string name="label_preferred">hobetsia</string>
<string name="label_enable_compression">Gaitu konpresioa</string>
<string name="label_encrypt_filenames">Enkriptatu agirizenak</string>
<string name="label_hidden_recipients">Ezkutatu jasotzaileak</string>
+ <string name="label_verify_keyserver">Egiaztatu giltza-zerbitzaria</string>
+ <string name="label_enter_keyserver_url">Sartu giltza-zerbitzariaren URL-a</string>
+ <string name="label_keyserver_dialog_delete">Ezabatu giltza-zerbitzaria</string>
+ <string name="label_theme">Azalgaia</string>
+ <string name="pref_keyserver">OpenPGP giltza-zerbitzariak</string>
+ <string name="pref_keyserver_summary">Bilatu giltzak hautaturiko OpenPGP giltza-zerbitzarietan (HKP protokoloa)</string>
+ <string name="pref_keybase">keybase.io</string>
+ <string name="pref_keybase_summary">Bilatu giltzak keybase.io-an</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <string name="pref_proxy_tor_title">Gaitu Tor</string>
+ <string name="pref_proxy_tor_summary">Orbot ezarrita egotea behar du</string>
+ <string name="pref_proxy_normal_title">Gaitu beste proxy bat</string>
+ <string name="pref_proxy_host_title">Proxy Hostalaria</string>
+ <string name="pref_proxy_host_err_invalid">Proxy hostalaria ezin da hutsik egon</string>
+ <string name="pref_proxy_port_title">Proxy Ataka</string>
+ <string name="pref_proxy_port_err_invalid">Ataka zenbaki baliogabea sartu da</string>
+ <string name="pref_proxy_type_title">Proxy Mota</string>
+ <!--proxy type choices and values-->
+ <string name="pref_proxy_type_choice_http">HTTP</string>
+ <string name="pref_proxy_type_choice_socks">SOCKS</string>
+ <!--OrbotHelper strings-->
+ <string name="orbot_ignore_tor">Ez erabili Tor</string>
+ <!--InstallDialogFragment strings-->
+ <string name="orbot_install_dialog_title">Ezarri Orbot edo erabili Tor?</string>
+ <string name="orbot_install_dialog_install">Ezarri</string>
+ <string name="orbot_install_dialog_content">Orbot ezarrita eta proxy trafiko igaropena gaituta eduki behar duzu. Nahi duzu ezartzea?</string>
+ <string name="orbot_install_dialog_cancel">Ezeztatu</string>
+ <string name="orbot_install_dialog_ignore_tor">Ez erabili Tor</string>
+ <!--StartOrbotDialogFragment strings-->
+ <string name="orbot_start_dialog_title">Abiarazi Orbot?</string>
+ <string name="orbot_start_dialog_content">Ez dirudi Orbot ekinean dagoenik. Nahi duzu abiaraztea eta Tor-era elkartzea?</string>
+ <string name="orbot_start_btn">Abiarazi Orbot</string>
+ <string name="orbot_start_dialog_start">Abiarazi Orbot</string>
+ <string name="orbot_start_dialog_cancel">Ezeztatu</string>
+ <string name="orbot_start_dialog_ignore_tor">Ez erabili Tor</string>
+ <string name="user_id_no_name">&lt;izen gabe&gt;</string>
+ <string name="none">&lt;ezer ez&gt;</string>
+ <plurals name="n_keys">
+ <item quantity="one">1 giltza</item>
+ <item quantity="other">%d giltza</item>
+ </plurals>
<plurals name="n_keyservers">
<item quantity="one">%d giltza-zerbitzari</item>
<item quantity="other">%d giltza-zerbitzari</item>
@@ -153,37 +223,59 @@
<string name="filemanager_title_open">Ireki...</string>
<string name="error">Akatsa</string>
<string name="error_message">Akatsa: %s</string>
+ <string name="theme_dark">Iluna</string>
+ <string name="theme_light">Argia</string>
<!--key flags-->
<string name="flag_certify">Egiaztatu</string>
<string name="flag_sign">Sinatu</string>
<string name="flag_encrypt">Enkriptatu</string>
<string name="flag_authenticate">Egiaztatu</string>
<!--sentences-->
- <string name="wrong_passphrase">Sar-esaldi okerra.</string>
+ <string name="wrong_passphrase">Sarhitz okerra.</string>
<string name="no_filemanager_installed">Ez dago agiri kudeatzaile bateragarririk ezarrita.</string>
- <string name="passphrases_do_not_match">Sar-esaldiak ez datoz bat.</string>
- <string name="passphrase_must_not_be_empty">Mesedez sartu sar-esaldi bat.</string>
+ <string name="passphrases_do_not_match">Sarhitzak ez datoz bat.</string>
+ <string name="passphrase_must_not_be_empty">Mesedez sartu sarhitz bat.</string>
<string name="passphrase_for_symmetric_encryption">Enkriptaketa simetrikoa.</string>
- <string name="passphrase_for">Sartu sar-esaldia \'%s\'-rako</string>
+ <string name="passphrase_for">Sartu \'%s\'-rako sarhitza</string>
<string name="pin_for">Sartu PIN-a \'%s\'-rako</string>
+ <string name="yubikey_pin_for">Sartu PIN-a YubKey-ra sartzeko \'%s\'-rentzat</string>
+ <string name="nfc_text">Heutsi YubiKey NFC markatzailearen kontra zure gailuaren atzealdean.</string>
+ <string name="nfc_wait">Eduki YubiKey atzealdean!</string>
+ <string name="nfc_finished">Irten YubiKey-tik orain</string>
+ <string name="nfc_try_again_text">Irten YubiKey-tik orain eta sakatu SAIATU BERRIRO.</string>
<string name="file_delete_confirmation_title">Ezabatu jatorrizko agiriak?</string>
<string name="file_delete_confirmation">Hurrengo agiriak ezabatu egingo dira: %s</string>
- <string name="no_file_selected">Hautatu agiri bat lehenik.</string>
+ <string name="file_delete_successful">%1$d -&gt; %2$d-tik agiri ezbatu dira.%3$s</string>
+ <string name="no_file_selected">Ez da agiririk hautatu</string>
<string name="encrypt_sign_successful">Ongi sinatu eta/edo enkriptatu da.</string>
<string name="encrypt_sign_clipboard_successful">Ongi sinatu eta/edo enkriptatu da gakora.</string>
<string name="select_encryption_key">Hautatu enkriptaketa giltza bat gutxienez.</string>
- <string name="select_encryption_or_signature_key">Hautatu enkriptaketa giltza bat edo sinadura giltza bat gutxienez.</string>
+ <string name="error_no_encryption_or_signature_key">Hautatu gutxienez enkriptaketa giltza bat edo sinadura giltza bat.</string>
+ <string name="specify_file_to_encrypt_to">Mesedez adierazi zein agirira enkriptatu.\nKONTUZ: Agiria gainidatzi egingo da egonez gero!</string>
+ <string name="specify_file_to_decrypt_to">Mesedez adierazi zein agirira dekriptatu.\nKONTUZ: Agiria gainidatzi egingo da egonez gero!</string>
+ <string name="specify_backup_dest">Babeskopia bat egingo da, zure giltzak salbu barneratuz, mesedez adierazi helmuga agiri bat.\nKONTUZ: Agiria gainidatzi egingo da egonez gero!</string>
+ <string name="specify_backup_dest_single">Giltza hau elkarbanatu egingo da, mesedez adierazi helmuga agiria.\nKONTUZ: Agiria gainidatzi egingo da egonez gero!</string>
+ <string name="specify_backup_dest_secret_single">Zure giltzaren babeskopia oso bat egingo da, mesedez adierazi helmuga agiria.\nKONTUZ: Agiria gainidatzi egingo da egonez gero!</string>
+ <string name="specify_backup_dest_secret">Giltza guztien, zeureak barne, babeskopia oso bat egingo da, mesedez adierazi helmuga agiriak.\nKONTUZ: Agiria gainidatzi egingo da egonez gero!</string>
<string name="key_deletion_confirmation_multi">Egitan nahi duzu hautaturiko giltzak ezabatzea?</string>
+ <string name="secret_key_deletion_confirmation">Ezabatu ondoren ezingo dituzu giltza honekin enkriptatutako mezuak irakurri eta berarekin egindako giltza baieztapen guztiak galduko dira!</string>
<string name="public_key_deletetion_confirmation">Ezabatu \'%s\' giltza?</string>
<string name="also_export_secret_keys">Esportatu giltza sekretuak ere</string>
+ <string name="reinstall_openkeychain">Akats ezagun bat aurkitu duzu Android-rekin. Mesedez ezarri berriro OpenKeychain zure harremanak giltzekin lotzea nahi badituzu.</string>
<string name="key_exported">Ongi esportatu da 1 giltza</string>
<string name="keys_exported">Ongi esportatu dira %d giltza</string>
<string name="no_keys_exported">Ez da giltzarik esportatu.</string>
<string name="key_creation_el_gamal_info">Oharra: azpigiltzek bakarrik sostengatzen dute EIGamal.</string>
<string name="key_not_found">Ezin da %08X giltza aurkitu.</string>
+ <string name="specify_file_to_export_log_to">Mesedez adierazi zein agirira esportatu. \nKONTUZ: Agiria gainidatzi egingo da egonez gero.</string>
+ <plurals name="bad_keys_encountered">
+ <item quantity="one">%d giltza sekretu gaitz ezikusita. Badaiteke esportatu izana\n --export-secret-subkeys aukerarekin.\nZihurtatu esportatzen duzula --export-secret-keys aukerarekin ordez.</item>
+ <item quantity="other">%d giltza sekretu gaitz ezikusita. Badaiteke esportatu izana\n --export-secret-subkeys aukerarekin.\nZihurtatu esportatzen duzula --export-secret-keys aukerarekin ordez.</item>
+ </plurals>
<string name="list_empty">Zerrenda hau hutsik dago!</string>
<string name="nfc_successful">Giltza ongi bidali da NFC Beam-rekin!</string>
- <string name="key_copied_to_clipboard">Giltz gakora kopiatu da!</string>
+ <string name="key_copied_to_clipboard">Giltza gakora kopiatu da!</string>
+ <string name="fingerprint_copied_to_clipboard">Hatz-aztarna gakora kopiatu da!</string>
<string name="select_key_to_certify">Mesedez hautatu baiztapenerako erabiltzeko giltza!</string>
<string name="key_too_big_for_sharing">Giltza handiegia da modu honetan elkarbanatzeko!</string>
<string name="text_copied_to_clipboard">Idazkia gakora kopiatu da!</string>
@@ -197,28 +289,38 @@
<string name="error_external_storage_not_ready">kanpoko biltegia ez dago gertu</string>
<string name="error_key_size_minimum512bit">giltzaren neurria gutxienez 512bitekoa izan behar da</string>
<string name="error_unknown_algorithm_choice">algoritmo ezezagun hautapena</string>
- <string name="error_user_id_no_email">ez da post@rik aurkitu</string>
+ <string name="error_user_id_no_email">ez da post@ helbiderik aurkitu</string>
<string name="error_key_needs_a_user_id">gutxienez nortasun bat behar da</string>
- <string name="error_no_signature_passphrase">ez da sar-esaldirik behar</string>
+ <string name="error_no_signature_passphrase">ez da sarhitzik eman</string>
<string name="error_no_signature_key">ez da sinadura giltzarik eman</string>
- <string name="error_integrity_check_failed">osotasun egiaztapen hutsigitea! Datuak aldatuak izan dira!</string>
- <string name="error_wrong_passphrase">sar-esaldi okerra</string>
+ <string name="error_invalid_data">OpenPGP eduki enkriptatu edo sinatu baliogabea!</string>
+ <string name="error_integrity_check_failed">osotasun egiaztapen hutsegitea! Datuak aldatuak izan dira!</string>
+ <string name="error_wrong_passphrase">sarhitz okerra</string>
<string name="error_could_not_extract_private_key">ezin da giltza pribatua atera</string>
<!--errors without preceeding Error:-->
<string name="error_jelly_bean_needed">Android 4.1 behar duzu Android-ren NFC Beam ezaugarria erabiltzeko!</string>
<string name="error_nfc_needed">NFC gaitua izan behar da!</string>
<string name="error_beam_needed">Beam gaitua izan behar da!</string>
<string name="error_nothing_import">Ez da giltzarik aurkitu!</string>
+ <string name="error_nothing_import_selected">Ez da giltzarik hautatu inportatzeko!</string>
+ <string name="error_contacts_key_id_missing">Hutsegitea giltza ID-a harremanetatik berreskuratzean!</string>
+ <string name="error_generic_report_bug">Akats generiko bat gertatu da, mesedez sortu akats jakinarazpen berri bat OpenKeychain-erako.</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">Sinatu Gabe</string>
<string name="decrypt_result_invalid_signature">Sinadura baliogabea!</string>
- <string name="decrypt_result_signature_uncertified">Sinatzailea (egiaztatu gabea!)</string>
- <string name="decrypt_result_signature_certified">Sinatzailea</string>
- <string name="decrypt_result_signature_expired_key">Giltzaren epemuga gaindituta!</string>
- <string name="decrypt_result_signature_missing_key">Giltza publiko ezezaguna</string>
+ <string name="decrypt_result_insecure_cryptography">Sinadura baliogabea (Kriptografia segurtasun gabea)!</string>
+ <string name="decrypt_result_signature_uncertified"><b>baieztatu gabeko</b> giltzak sinatua</string>
+ <string name="decrypt_result_signature_secret">Zure giltzak sinatuta</string>
+ <string name="decrypt_result_signature_certified">Baieztatutako giltzak sinatuta</string>
+ <string name="decrypt_result_signature_expired_key"><b>iraungitutako</b> giltzak sinatua!</string>
+ <string name="decrypt_result_signature_revoked_key"><b>ukatutako</b> giltzak sinatua!</string>
+ <string name="decrypt_result_signature_missing_key"><b>giltza publiko ezezagunak</b> sinatuta</string>
<string name="decrypt_result_encrypted">Enkriptatuta</string>
<string name="decrypt_result_not_encrypted">Enkriptatu Gabea</string>
+ <string name="decrypt_result_insecure">Enkriptaketa Segurtasun-gabea</string>
<string name="decrypt_result_action_show">Erakutsi</string>
+ <string name="decrypt_invalid_text">Bietako bat, sinadura baliogabea da edo giltza ukatua izan da. Ezin zara zihur egon nork idatzi duen idazkia. Oraindik ere erakutsi dadin nahi duzu?</string>
+ <string name="decrypt_invalid_button">Ulertzen ditut arriskuak, erakutsi!</string>
<!--Add keys-->
<string name="add_keys_my_key">Nire giltza:</string>
<!--progress dialogs, usually ending in '…'-->
@@ -227,24 +329,34 @@
<string name="progress_cancelling">ezeztatzen...</string>
<string name="progress_saving">gordetzen...</string>
<string name="progress_importing">inportatzen...</string>
+ <string name="progress_revoking_uploading">Giltza ukatzen eta igotzen...</string>
+ <string name="progress_updating">Giltzak eguneratzen...</string>
<string name="progress_exporting">esportatzen...</string>
<string name="progress_uploading">igotzen...</string>
<string name="progress_building_key">giltza eraikitzen...</string>
+ <string name="progress_building_master_key">Maisu eraztuna eraikitzen...</string>
<string name="progress_generating_rsa">RSA giltza berria sortzen...</string>
<string name="progress_generating_dsa">DSA giltza berria sortzen...</string>
<string name="progress_generating_elgamal">EIGamal giltza berria sortzen...</string>
<string name="progress_generating_ecdsa">ECDSA giltza berria sortzen...</string>
<string name="progress_generating_ecdh">ECDH giltza berria sortzen...</string>
+ <string name="progress_modify">giltza-uztaia aldatzen...</string>
+ <string name="progress_modify_unlock">giltza-uztaia desblokeatzen...</string>
<string name="progress_modify_adduid">erabiltzaile ID-ak gehitzen...</string>
<string name="progress_modify_adduat">erabiltzaile ezaugarriak gehitzen...</string>
+ <string name="progress_modify_revokeuid">erabiltzaile ID-ak ukatzen...</string>
<string name="progress_modify_primaryuid">lehen erabiltzaile ID-a aldatzen...</string>
<string name="progress_modify_subkeychange">azpigiltzak aldatzen...</string>
+ <string name="progress_modify_subkeyrevoke">azpigiltzak ukatzen...</string>
<string name="progress_modify_subkeyadd">azpigiltzak gehitzen...</string>
- <string name="progress_modify_passphrase">sar-esaldia aldatzen...</string>
+ <string name="progress_modify_passphrase">sarhitza aldatzen...</string>
+ <string name="progress_modify_pin">PIN-a aldatzen...</string>
+ <string name="progress_modify_admin_pin">Administrari PIN-a aldatzen...</string>
<plurals name="progress_exporting_key">
<item quantity="one">giltza esportatzen</item>
<item quantity="other">giltzak esportatzen</item>
</plurals>
+ <string name="progress_start">eragiketa gertatzen...</string>
<string name="progress_extracting_signature_key">sinadura giltza ateratzen...</string>
<string name="progress_extracting_key">giltza ateratzen...</string>
<string name="progress_preparing_streams">jarioak gertatzen...</string>
@@ -262,6 +374,10 @@
<string name="progress_verifying_integrity">osotasuna egiaztatzen...</string>
<string name="progress_deleting_securely">\'%s\' segurtasunez ezabatzen...</string>
<string name="progress_deleting">giltzak ezabatzen...</string>
+ <string name="progress_con_saving">sendotu: katxean gordetzen...</string>
+ <string name="progress_con_reimport">sendotu: berrinportatzen...</string>
+ <string name="progress_verifying_keyserver_url">giltza-zerbitzaria egiaztatzen...</string>
+ <string name="progress_starting_orbot">Orbot Abiarazten...</string>
<!--action strings-->
<string name="hint_cloud_search_hint">Bilatu Izena, Post@... bidez</string>
<!--key bit length selections-->
@@ -275,6 +391,8 @@
<string name="key_size_8192">8192</string>
<string name="key_size_custom">Norbere giltza neurria</string>
<string name="key_size_custom_info">Idatzi norbere giltza luzera (bitetan):</string>
+ <string name="key_size_custom_info_rsa">RSA giltzaren luzera 1024 baino handiagoa eta gehienez 16384-ko izan behar da. Baita ere 8-ren biderkaria izan behar da.</string>
+ <string name="key_size_custom_info_dsa">DSA giltzaren luzera gutxienez 512-koa eta gehienez 1024-koa izan behar da. Baita ere 64-ren biderkaria izan behar da.</string>
<!--elliptic curve names-->
<string name="key_curve_nist_p256">NIST P-256</string>
<string name="key_curve_nist_p384">NIST P-384</string>
@@ -287,7 +405,7 @@
<string name="compression_fast">azkarra</string>
<string name="compression_very_slow">oso astiroa</string>
<!--Help-->
- <string name="help_tab_start">Hasi</string>
+ <string name="help_tab_start">Hasiera</string>
<string name="help_tab_faq">SEG</string>
<string name="help_tab_wot">Giltza Baieztapena</string>
<string name="help_tab_nfc_beam">NFC Beam</string>
@@ -301,34 +419,93 @@
<string name="import_tab_qr_code">QR Kodea/NFC</string>
<string name="import_import">Inportatu hautaturiko giltzak</string>
<string name="import_qr_code_wrong">QR Kodea gaizki-eratua! Mesedez saiatu berriro!</string>
+ <string name="import_qr_code_fp">Hatz-aztarna gaizkiegina edo laburregia da!</string>
+ <string name="import_qr_code_too_short_fingerprint">Hatz-aztarna laburregia da!</string>
<string name="import_qr_code_button">Eskaneatu QR Kodea</string>
<string name="import_qr_code_text">Jarri zure kamera QR Kodearen gainean!</string>
+ <!--Import from URL-->
+ <string name="import_url_warn_no_search_parameter">Ez da bilaketa eskaerarik zehaztu. Oraindik eskuz bilatu dezakezu giltza-zerbitzari honetan.</string>
<!--Generic result toast-->
- <string name="view_log">Xehetasunak</string>
+ <string name="snackbar_details">Xehetasunak</string>
<string name="with_warnings">, kontuz oharrekin</string>
+ <string name="with_cancelled">, ezeztatu arte</string>
<!--Import result toast-->
+ <plurals name="import_keys_added_and_updated_1">
+ <item quantity="one">Ongi inportatuta 1 giltza</item>
+ <item quantity="other">Ongi inportatuta %1$d giltza</item>
+ </plurals>
+ <plurals name="import_keys_added_and_updated_2">
+ <item quantity="one">eta eguneratuta giltza%2$s.</item>
+ <item quantity="other">eta eguneratuta %1$d giltza%2$s.</item>
+ </plurals>
+ <plurals name="import_keys_added">
+ <item quantity="one">Ongi inportatuta giltza%2$s.</item>
+ <item quantity="other">Ongi inportatuta %1$d giltza%2$s.</item>
+ </plurals>
+ <plurals name="import_keys_updated">
+ <item quantity="one">Ongi eguneratuta giltza%2$s.</item>
+ <item quantity="other">Ongi eguneratuta %1$d giltza%2$s.</item>
+ </plurals>
+ <plurals name="import_keys_with_errors">
+ <item quantity="one">Inportatze hutsegitea 1 giltzarentzat!</item>
+ <item quantity="other">Inportatze hutsegitea %d giltzentzat!</item>
+ </plurals>
+ <plurals name="import_error">
+ <item quantity="one">Inportazio hutsegitea!</item>
+ <item quantity="other">Hutsegitea %d giltza inportatzerakoan!</item>
+ </plurals>
<string name="import_error_nothing">Ez dago ezer inportatzeko.</string>
<string name="import_error_nothing_cancelled">Inportazioa ezeztatuta.</string>
<!--Delete result toast-->
+ <plurals name="delete_ok_but_fail_1">
+ <item quantity="one">Ongi ezabatu da bat giltza</item>
+ <item quantity="other">Ongi ezabatu dira %1$d giltza</item>
+ </plurals>
+ <plurals name="delete_ok_but_fail_2">
+ <item quantity="one">, baina hutsegitea egon da ezabatzerakoan bat giltza%2$s.</item>
+ <item quantity="other">, baina hutsegitea egon da ezabatzerakoan %1$d giltza%2$s.</item>
+ </plurals>
+ <plurals name="delete_ok">
+ <item quantity="one">Ongi ezabatu da giltza%2$s.</item>
+ <item quantity="other">Ongi ezabatu dira %1$d giltza%2$s.</item>
+ </plurals>
<plurals name="delete_fail">
<item quantity="one">Akatsa %2$s giltza ezabatzerakoan.</item>
<item quantity="other">Akatsa %1$d giltza ezabatzerakoan.</item>
</plurals>
<string name="delete_nothing">Ez dago ezer ezabatzeko</string>
<string name="delete_cancelled">Ezabapen eragiketa ezeztaturik.</string>
+ <!--Revoke result toast (snackbar)-->
+ <string name="revoke_ok">Giltza ongi ukatu da.</string>
+ <string name="revoke_fail">Akatsa giltza ukatzerakoan!</string>
+ <string name="revoke_nothing">Ez dago ezer ukatzeko.</string>
+ <string name="revoke_cancelled">Ukatze eragiketa ezeztatuta.</string>
<!--Certify result toast-->
+ <plurals name="certify_keys_ok">
+ <item quantity="one">Ongi egiaztatuta giltza%2$s.</item>
+ <item quantity="other">Ongi egiaztatuta %1$d giltza%2$s.</item>
+ </plurals>
+ <plurals name="certify_keys_with_errors">
+ <item quantity="one">Egiaztagiritze hutsegitea!</item>
+ <item quantity="other">Egiaztagiritzeak huts egindu %d giltzekin!</item>
+ </plurals>
+ <plurals name="certify_error">
+ <item quantity="one">Egiaztagiritze hutsegitea!</item>
+ <item quantity="other">Hutsegitea %d giltzen egiaztagiritzean!</item>
+ </plurals>
<!--Intent labels-->
<string name="intent_decrypt_file">Dekriptatu Agiria OpenKeychain-rekin</string>
<string name="intent_import_key">Inportatu Giltza OpenKeychain-rekin</string>
<string name="intent_send_encrypt">Enkriptatu OpenKeychain-rekin</string>
<string name="intent_send_decrypt">Dekriptatu OpenKeychain-rekin</string>
<!--Remote API-->
- <string name="api_settings_show_info">Erakutsi argibide aurreratuak</string>
- <string name="api_settings_hide_info">Ezkutatu argibide aurreratuak</string>
- <string name="api_settings_show_advanced">Erakutsi ezarpen aurreratuak</string>
- <string name="api_settings_hide_advanced">Ezkutatu ezarpen aurreratuak</string>
+ <string name="api_settings_show_info">Erakutsi argibide hedatuak</string>
+ <string name="api_settings_hide_info">Ezkutatu argibide hedatuak</string>
+ <string name="api_settings_show_advanced">Erakutsi ezarpen hedatuak</string>
+ <string name="api_settings_hide_advanced">Ezkutatu ezarpen hedatuak</string>
<string name="api_settings_no_key">Ez da giltzarik hautatu</string>
<string name="api_settings_select_key">Hautatu giltza</string>
+ <string name="api_settings_create_key">Sortu giltza berria</string>
<string name="api_settings_save">Gorde</string>
<string name="api_settings_save_msg">Kontua gorde da</string>
<string name="api_settings_cancel">Ezeztatu</string>
@@ -336,20 +513,40 @@
<string name="api_settings_start">Abiarazi aplikazioa</string>
<string name="api_settings_delete_account">Ezabatu kontua</string>
<string name="api_settings_package_name">Pakete Izena</string>
- <string name="api_settings_advanced">Argibide Aurreratuak</string>
+ <string name="api_settings_package_certificate">Pakete Egiaztagiriaren SHA-256</string>
+ <string name="api_settings_accounts">Kontuak (API zaharra)</string>
+ <string name="api_settings_advanced">Argibide Hedatuak</string>
<string name="api_settings_allowed_keys">Ahalbidetutako Giltzak</string>
<string name="api_settings_settings">Ezarpenak</string>
<string name="api_settings_key">Kontuaren giltza:</string>
<string name="api_settings_accounts_empty">Ez dago konturik app honi erantsita.</string>
+ <string name="api_create_account_text">Ez dago giltzarik itxuratuta kontu honentzat. Mesedez hautatu dauden giltzetako bat edo sortu berri bat.\nAplikazioek hemen hautaturiko giltzekin bakarrik dekriptatu/sinatu dezakete.</string>
+ <string name="api_update_account_text">Kontu honetarako gordetako giltza ezabatua izan da. Mesedez hautatu beste bat!\nApliazioek hemen hautaturiko giltzekin bakarrik dekriptatu/sinatu dezakete!</string>
+ <string name="api_register_text">Erakusten den aplikazioak mezuak enkriptatu/dekriptatu eta sinatzea nahi ditu zure izenean.\nAhalbidetu sarbidea?\n\nKONTUZ: Ez badakizu zergaitik agertzen den ikusleiho hau, ez ahalbidetu sarbidea! Sarbidea ukatu dezakezu geroago \'Aplikazioak\' ikusleihoa erabiliz.</string>
<string name="api_register_allow">Ahalbidetu sarbidea</string>
<string name="api_register_disallow">Ez ahalbidetu sarbidea</string>
<string name="api_register_error_select_key">Mesedez hautatu giltza bat!</string>
- <string name="api_select_pub_keys_dublicates_text">Giltza bat baino gehiago dago nortasun hauentzat:</string>
- <string name="api_select_pub_keys_text">Mesedz berrikusi jasotzaile zerrenda!</string>
+ <string name="api_select_pub_keys_missing_text">Ez da giltzarik aurkitu posta helbide hauentzat:</string>
+ <string name="api_select_pub_keys_dublicates_text">Giltza bat baino gehiago daude posta helbide honekin:</string>
+ <string name="api_select_pub_keys_text">Mesedez berrikusi jasotzaile zerrenda!</string>
<string name="api_select_pub_keys_text_no_user_ids">Mesedez hautatu jasotzaileak!</string>
+ <string name="api_error_wrong_signature">Sinadura egiaztapen hutsegitea! Aplikazio hau beste iturburu batetik duzu ezarrita? Zihur bazaude hau ez dela eraso bat, ukatu aplikazio honen erregistrazioa OpenKeychain eta orduan erregistratu aplikazioa berriro.</string>
+ <string name="api_select_sign_key_text">Mesedez hautatu zure giltzetako bat edo sortu berri bat.</string>
+ <string name="api_select_keys_text">Ahalbidetutako giltza batek ere ezin du edukia dekriptatu. Mesedez hautatu ahalbidetutako giltzak.</string>
<!--Share-->
<string name="share_qr_code_dialog_title">Elkarbanatu QR Kodearekin</string>
<string name="share_nfc_dialog">Elkarbanatu NFC-rekin</string>
+ <!--retry upload dialog-->
+ <string name="retry_up_dialog_title">Igoera hutsegitea!</string>
+ <string name="retry_up_dialog_message">Igoera hutsegitea. Nahi duzu eragiketa berriro saiatzea?</string>
+ <string name="retry_up_dialog_btn_reupload">Bersaiatu Eragiketa</string>
+ <string name="retry_up_dialog_btn_cancel">Ezeztatu Eragiketa</string>
+ <!--Delete or revoke private key dialog-->
+ <string name="del_rev_dialog_btn_revoke">Ukatu eta igo</string>
+ <string name="del_rev_dialog_btn_delete">Ezabatu bakarrik</string>
+ <!--Delete Or Revoke Dialog spinner-->
+ <string name="del_rev_dialog_choice_delete">Ezabatu bakarrik</string>
+ <string name="del_rev_dialog_choice_rev_upload">Ukatu eta Igo</string>
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">1 giltza hautaturik.</item>
@@ -373,51 +570,117 @@
<string name="key_view_tab_certs">Egiaztagiriak</string>
<string name="key_view_tab_keybase">Keybase.io</string>
<string name="user_id_info_revoked_title">Ukatuta</string>
- <string name="user_id_info_certified_title">Egiaztuta</string>
+ <string name="user_id_info_revoked_text">Nortasun hau ukatua izan da giltzaren jabeagaitik. Aurrerantzean ez da baliozkoa.</string>
+ <string name="user_id_info_certified_title">Egiaztatuta</string>
<string name="user_id_info_certified_text">Nortasun hau zeuk egiaztatua da.</string>
<string name="user_id_info_uncertified_title">Egiaztatu gabea</string>
+ <string name="user_id_info_uncertified_text">Nortasun hau ez da egiaztagiritua izan oraindik. Ezin zara zihur egon nortasuna egitan dagokion adierazitako norbanakoari.</string>
<string name="user_id_info_invalid_title">Baliogabea</string>
<string name="user_id_info_invalid_text">Zerbait oker dago nortasun honekin!</string>
<!--Key trust-->
- <string name="key_trust_already_verified">Jadanik baduzu giltza hau baieztatuta!</string>
+ <string name="key_trust_already_verified">Giltza hau jadanik baieztatuta duzu!</string>
<string name="key_trust_it_is_yours">Hau zure giltzetako bat da!</string>
+ <string name="key_trust_maybe">Giltza hau ukatua edo iraungitua dago.\nEzin duzu baieztatuta, baina fidatzea hautatu dezakezu.</string>
+ <string name="key_trust_revoked">Nortasun hau ukatua izan da giltzaren jabeagaitik. Ez zara berataz fidatu behar.</string>
<string name="key_trust_expired">Giltza hau iraungituta dago. Ez zara berataz fidatu behar.</string>
+ <string name="key_trust_old_keys">Ongi egon daiteke hau erabiltzea giltza hau baliozkoa zen garaiko mezu zahar bat dekriptatzeko.</string>
+ <string name="key_trust_no_cloud_evidence">Ez dago hodeiko probarik giltza honen fidagarritasunerako.</string>
<string name="key_trust_start_cloud_search">Hasi bilaketa</string>
+ <string name="key_trust_results_prefix">Keybase.io \"probak\" eskaintzen ditu giltza honen jabea baieztatzeko:</string>
+ <string name="key_trust_header_text">Oharra: Keybase.io egiaztapenak OpenKeychain-en ezaugarri esperimental bat da. QR kodeak eskaneatzea edo giltzak NFC bidez aldatzea gomendatzen dizugu hauek baieztatu ahal izateko.</string>
<!--keybase proof stuff-->
- <string name="keybase_narrative_twitter">Argitaratu Twitter-en honela</string>
- <string name="keybase_narrative_github">GitHub-en honela ezagutzen da</string>
- <string name="keybase_narrative_dns">Domeinu izena(k) kontrolatzen ditu</string>
- <string name="keybase_narrative_reddit">Argitaratu Reddit-en honela</string>
- <string name="keybase_narrative_coinbase">Coinbasen honela ezagutzen da</string>
- <string name="keybase_narrative_hackernews">Argitaratu Hacker News-en honela</string>
+ <string name="keybase_narrative_twitter">Argitaratu Twitter-en honela %s</string>
+ <string name="keybase_narrative_github">GitHub-en %s bezala ezagutzen da</string>
+ <string name="keybase_narrative_dns">%s domeinu izena(k) agintzen ditu</string>
+ <string name="keybase_narrative_web_site">%s Web gune(et)an argitaratu dezake</string>
+ <string name="keybase_narrative_reddit">Argitaratu Reddit-en %s bezala</string>
+ <string name="keybase_narrative_coinbase">Coinbasen %s bezala ezagutzen da</string>
+ <string name="keybase_narrative_hackernews">Argitaratu Hacker News-en %s bezala</string>
+ <string name="keybase_proof_failure">Zorigaitzez proba hau ezin da egiaztatu.</string>
+ <string name="keybase_unknown_proof_failure">Arazo ezezaguna proba egiaztatzailearekin</string>
+ <string name="keybase_problem_fetching_evidence">Arazoa probarekin</string>
+ <string name="keybase_key_mismatch">Giltzaren hatz-aztarna ez dator bat proban aurkeztutakoarekin</string>
+ <string name="keybase_dns_query_failure">DNS TXT Grabaketa berreskurapen hutsegitea</string>
+ <string name="keybase_no_prover_found">Ez da proba egiaztatzailerik aurkitu honentzat:</string>
+ <string name="keybase_message_payload_mismatch">Dekriptatutako proba ez dator bat itxarondako balioarekin</string>
+ <string name="keybase_message_fetching_data">Proba lortzen</string>
+ <string name="keybase_proof_succeeded">Proba hau egiaztatua izan da!</string>
+ <string name="keybase_a_post">Posta bat</string>
+ <string name="keybase_fetched_from">hemendik lortuta</string>
<string name="keybase_for_the_domain">domeinurako</string>
+ <string name="keybase_contained_signature">giltza honen jabeak bakarrik sortu ahal izan duen mezu bat du.</string>
<string name="keybase_twitter_proof">Txio bat</string>
<string name="keybase_dns_proof">DNS TXT grabaketa bat</string>
<string name="keybase_web_site_proof">Idazki agiri bat</string>
<string name="keybase_reddit_proof">JSON agiri bat</string>
<string name="keybase_verify">Egiaztatu</string>
<!--Edit key-->
- <string name="edit_key_action_change_passphrase">Aldatu Sar-esaldia</string>
+ <string name="edit_key_action_change_passphrase">Aldatu Sarhitza</string>
<string name="edit_key_action_add_identity">Gehitu Nortasuna</string>
<string name="edit_key_action_add_subkey">Gehitu Azpigiltza</string>
<string name="edit_key_edit_user_id_title">Hautatu ekintza bat!</string>
+ <string-array name="edit_key_edit_user_id">
+ <item>Aldatu Lehen Nortasuna</item>
+ <item>Ukatu Nortasuna</item>
+ </string-array>
+ <string-array name="edit_key_edit_user_id_revert_revocation">
+ <item>Leheneratu ukapena</item>
+ </string-array>
+ <string name="edit_key_edit_user_id_revoked">Nortasun hau ukatua izan da. Hau ezin da desegin.</string>
<string name="edit_key_edit_subkey_title">Hautatu ekintza bat!</string>
+ <string-array name="edit_key_edit_subkey">
+ <item>Aldatu Iraungitzea</item>
+ <item>Ukatu Azpigiltza</item>
+ <item>Ezeztatu Azpigiltza</item>
+ <item>Mugitu Azpigiltza YubiKey / Txartel Adimentsura</item>
+ </string-array>
<string name="edit_key_new_subkey">azpigiltza berria</string>
+ <string name="edit_key_select_flag">Mesedez hautatu gutxienez ikur bat!</string>
<string name="edit_key_error_add_identity">Gehitu nortasun bat gutxienez!</string>
<string name="edit_key_error_add_subkey">Gehitu azpigiltza bat gutxienez!</string>
+ <string name="edit_key_error_bad_nfc_algo">Txartel adimentsuak ez du algoritmoa sostengatzen!</string>
+ <string name="edit_key_error_bad_nfc_size">Txartel adimentsuak ez du giltzaren neurria sostengatzen!</string>
<!--Create key-->
<string name="create_key_upload">Aldiberetu hodeiarekin</string>
<string name="create_key_empty">Eremu hau beharrezkoa da</string>
- <string name="create_key_passphrases_not_equal">Sar-esaldiak ez datoz bat</string>
+ <string name="create_key_passphrases_not_equal">Sarhitzak ez datoz bat</string>
<string name="create_key_final_text">Hurrengo nortasuna sartu duzu:</string>
+ <string name="create_key_final_robot_text">Giltza sortzeak denbora apur bat hartu dezake, hartu kafe bat bitartean...</string>
+ <string name="create_key_rsa">(3 azpigiltza, RSA, 4096 bit)</string>
<string name="create_key_custom">(norbere giltza itxurapena)</string>
+ <string name="create_key_name_text">Hautatu giltza honekin lotutako izen bat. Izan daiteke izen oso bat, adib., \'Mikel Etxeberria\', edo goitizen bat, adib., \'Xabi\'.</string>
+ <string name="create_key_email_text">Sartu harreman segururako erabiltzeko zure post@ helbide nagusia.</string>
+ <string name="create_key_passphrase_text">Hautatu sarhitz gogor bat. Honek zure giltza babesten du zure gailua lapurtzen dutenean.</string>
<string name="create_key_hint_full_name">Izen Osoa edo Ezizena</string>
<string name="create_key_edit">Aldatu giltza itxurapena</string>
<string name="create_key_add_email">Gehitu post@ helbidea</string>
+ <string name="create_key_add_email_text">Post@ helbide gehigarriak ere giltza honekin elkartzen dira eta komunikazio segururako erabili daitezke.</string>
+ <string name="create_key_email_already_exists_text">Post@ helbidea jadanik gehituta dago</string>
+ <string name="create_key_email_invalid_email">Post@ heuskarria baliogabea da</string>
+ <string name="create_key_yubi_key_pin_text">Mesedez gogoratu PIN-a, beharrezkoa da gero zure YubiKey erabiltzeko. Mesedez idatzi behean Administrari PIN-a eta biltegiratu toki seguru batean.</string>
+ <string name="create_key_yubi_key_pin">PIN-a</string>
+ <string name="create_key_yubi_key_admin_pin">Administrari PIN-a</string>
+ <string name="create_key_yubi_key_pin_repeat_text">Mesedez sartu PIN-a eta Administrari PIN-a jarraitzeko.</string>
+ <string name="create_key_yubi_key_pin_repeat">Berridatzi PIN-a</string>
+ <string name="create_key_yubi_key_admin_pin_repeat">Berridatzi Administrari PIN-a</string>
+ <string name="create_key_yubi_key_pin_not_correct">PIN-a ez da zuzena!</string>
<!--View key-->
+ <string name="view_key_revoked">Ukatuta: Giltza ezin da gehiago erabili!</string>
+ <string name="view_key_expired">Iraungitua: Harremanak giltzaren baliotasuna luzatu behar du!</string>
+ <string name="view_key_expired_secret">Iraungitua: Giltzen baliotasuna luzatu dezakezu hauek editatuz!</string>
<string name="view_key_my_key">Nire Giltza</string>
- <string name="view_key_verified">Baieztatu Giltza</string>
+ <string name="view_key_verified">Baieztatutako Giltza</string>
<string name="view_key_unverified">Baieztatugabe: Eskaneatu QR Kodea giltza baieztatzeko!</string>
+ <string name="view_key_fragment_no_system_contact">&lt;ezer ez&gt;</string>
+ <!--Add/Edit keyserver-->
+ <string name="add_keyserver_dialog_title">Gehitu giltza-zerbitzaria</string>
+ <string name="edit_keyserver_dialog_title">Editatu giltza-zerbitzaria</string>
+ <string name="add_keyserver_verified">Giltza-zerbitzaria egiaztatuta!</string>
+ <string name="add_keyserver_without_verification">Giltza-zerbitzaria gehituta egiaztapen gabe.</string>
+ <string name="add_keyserver_invalid_url">URL baliogabea!</string>
+ <string name="add_keyserver_connection_failed">Hutsegitea giltza-zerbitzariarekin elkartzerakoan. Mesedez egiaztatu URL-a eta zure internet elkarketa.</string>
+ <string name="keyserver_preference_deleted">%s ezabatuta</string>
+ <string name="keyserver_preference_cannot_delete_last">Ezin da azken giltza-zerbitzaria ezabatu. Gutxienez bat behar da!</string>
<!--Navigation Drawer-->
<string name="nav_keys">Giltzak</string>
<string name="nav_encrypt_decrypt">Enkriptatu/Dekriptatu</string>
@@ -425,11 +688,14 @@
<string name="drawer_open">Ireki nabigazio marraztzailea</string>
<string name="drawer_close">Itxi nabigazio marraztzailea</string>
<string name="my_keys">Nire Giltzak</string>
+ <string name="nav_backup">Babeskopia</string>
<!--hints-->
<string name="encrypt_content_edit_text_hint">Idatzi idazkia</string>
<!--certs-->
<string name="cert_default">berezkoa</string>
<string name="cert_none">ezer ez</string>
+ <string name="cert_positive">positiboa</string>
+ <string name="cert_revoke">ukatuta</string>
<string name="cert_verify_ok">Ongi</string>
<string name="cert_verify_failed">hutsegitea!</string>
<string name="cert_verify_error">akatsa!</string>
@@ -438,66 +704,298 @@
<string name="msg_internal_error">Barneko akatsa!</string>
<string name="msg_cancelled">Eragiketa ezeztaturik.</string>
<!--Import Public log entries-->
+ <string name="msg_ip_bad_type_secret">Giltza-uztai sekretua publiko bezala inportatzeko saiakera. Hau akats bat da, mesedez agiritu jakinarazpen bat!</string>
+ <string name="msg_ip_delete_old_fail">Ez da giltza zaharrik ezabatu (berri bat sortzen?)</string>
<string name="msg_ip_delete_old_ok">Giltza zaharra datubasetik ezabatu da</string>
<string name="msg_ip_encode_fail">Eragiketa hutsegitea kodeaketa akats bategaitik</string>
<string name="msg_ip_error_io_exc">Eragiketa hutsegitea s/i akats bategaitik</string>
<string name="msg_ip_error_op_exc">Eragiketa hutsegitea datubase akats bategaitik</string>
<string name="msg_ip_error_remote_ex">Eragiketa hutsegitea barneko akats bategaitik</string>
+ <string name="msg_ip">%s giltza-uztai publikoa inportatzen</string>
+ <string name="msg_ip_insert_keyring">Giltza-uztai datuak kodeatzen</string>
<string name="msg_ip_insert_keys">Giltzak aztertzen</string>
<string name="msg_ip_prepare">Datubase eragiketak gertatzen</string>
+ <string name="msg_ip_master">%s maisu giltza prozesatzen</string>
+ <string name="msg_ip_master_expired">Giltza-uztaia iraungituta: %s</string>
+ <string name="msg_ip_master_expires">Giltza-uztaia iraungitzen da: %s</string>
+ <string name="msg_ip_master_flags_unspecified">Maisu ikurra: adierazi gabe (guztiak onartzen)</string>
+ <string name="msg_ip_master_flags_cesa">Maisu ikurrak: egiaztagiritu, enkriptatu, sinatu, egiaztatu</string>
+ <string name="msg_ip_master_flags_cesx">Maisu ikurrak: egiaztagiritu, enkriptatu, sinatu</string>
+ <string name="msg_ip_master_flags_cexa">Maisu ikurrak: egiaztagiritu, enkriptatu, egiaztatu</string>
+ <string name="msg_ip_master_flags_cexx">Maisu ikurrak: egiaztagiritu, enkriptatu</string>
+ <string name="msg_ip_master_flags_cxsa">Maisu ikurrak: egiaztagiritu, sinatu, egiaztatu</string>
+ <string name="msg_ip_master_flags_cxsx">Maisu ikurrak: egiaztagiritu, sinatu</string>
+ <string name="msg_ip_master_flags_cxxa">Maisu ikurrak: egiaztagiritu, egiaztatu</string>
+ <string name="msg_ip_master_flags_cxxx">Maisu ikurrak: egiaztagiritu</string>
+ <string name="msg_ip_master_flags_xesa">Maisu ikurrak: enkriptatu, sinatu, egiaztatu</string>
+ <string name="msg_ip_master_flags_xesx">Maisu ikurrak: enkriptatu, sinatu</string>
+ <string name="msg_ip_master_flags_xexa">Maisu ikurrak: enkriptatu, egiaztatu</string>
+ <string name="msg_ip_master_flags_xexx">Maisu ikurrak: enkriptatu</string>
+ <string name="msg_ip_master_flags_xxsa">Maisu ikurrak: sinatu, egiaztatu</string>
+ <string name="msg_ip_master_flags_xxsx">Maisu ikurrak: sinatu</string>
+ <string name="msg_ip_master_flags_xxxa">Maisu ikurrak: egiaztatu</string>
+ <string name="msg_ip_master_flags_xxxx">Maisu ikurrak: ezer ez</string>
+ <string name="msg_ip_merge_public">Inportatutako datuak dagoen giltza-uztai publikoan batzen</string>
+ <string name="msg_ip_merge_secret">Inportatutako datuak dagoen giltza-uztai publikoan batzen</string>
+ <string name="msg_ip_subkey">%s azpigiltza prozesatzen</string>
+ <string name="msg_ip_subkey_expired">Azpigiltza iraungipena: %s</string>
+ <string name="msg_ip_subkey_expires">Azpigiltza iraungipena: %s</string>
+ <string name="msg_ip_subkey_flags_unspecified">Azpigiltza ikurra: adierazi gabe (guztiak onartzen)</string>
+ <string name="msg_ip_subkey_flags_cesa">Azpigiltza ikurrak: egiaztagiritu, enkriptatu, sinatu, egiaztatu</string>
+ <string name="msg_ip_subkey_flags_cesx">Azpigiltza ikurrak: egiaztagiritu, enkriptatu, sinatu</string>
+ <string name="msg_ip_subkey_flags_cexa">Azpigiltza ikurrak: egiaztagiritu, enkriptatu, egiaztatu</string>
+ <string name="msg_ip_subkey_flags_cexx">Azpigiltza ikurrak: egiaztagiritu, enkriptatu</string>
+ <string name="msg_ip_subkey_flags_cxsa">Azpigiltza ikurrak: egiaztagiritu, sinatu, egiaztatu</string>
+ <string name="msg_ip_subkey_flags_cxsx">Azpigiltza ikurrak: egiaztagiritu, sinatu</string>
+ <string name="msg_ip_subkey_flags_cxxa">Azpigiltza ikurrak: egiaztagiritu, egiaztatu</string>
+ <string name="msg_ip_subkey_flags_cxxx">Azpigiltza ikurrak: egiaztagiritu</string>
+ <string name="msg_ip_subkey_flags_xesa">Azpigiltza ikurrak: enkriptatu, sinatu, egiaztatu</string>
+ <string name="msg_ip_subkey_flags_xesx">Azpigiltza ikurrak: enkriptatu, sinatu</string>
+ <string name="msg_ip_subkey_flags_xexa">Azpigiltza ikurrak: enkriptatu, egiaztatu</string>
+ <string name="msg_ip_subkey_flags_xexx">Azpigiltza ikurrak: enkriptatu</string>
+ <string name="msg_ip_subkey_flags_xxsa">Azpigiltza ikurrak: sinatu, egiaztatu</string>
+ <string name="msg_ip_subkey_flags_xxsx">Azpigiltza ikurrak: sinatu</string>
+ <string name="msg_ip_subkey_flags_xxxa">Azpigiltza ikurrak: egiaztatu</string>
+ <string name="msg_ip_subkey_flags_xxxx">Azpigiltza ikurrak: ezer ez</string>
+ <string name="msg_ip_success">Giltza-uztai publikoa ongi inportatu da</string>
+ <string name="msg_ip_success_identical">Giltza-uztaiak ez du datu berririk, ezer ez egiteko</string>
+ <string name="msg_ip_reinsert_secret">Giltza sekretua bir-txertatzen</string>
+ <string name="msg_ip_uid_cert_bad">Egiaztagiri gaitza aurkitu da!</string>
+ <string name="msg_ip_uid_cert_error">Akatsa egiaztagiria prozesatzerakoan!</string>
+ <string name="msg_ip_uid_cert_nonrevoke">Jadanik badu ez-ukatu egiaztagiri bat, jauzi egiten.</string>
+ <string name="msg_ip_uid_cert_old">Egiaztagiria aurrekoa baino zaharragoa da, jauzi egiten.</string>
+ <string name="msg_ip_uid_cert_new">Egiaztagiria berriagoa da, aurrekoa ordezten.</string>
+ <string name="msg_ip_uid_cert_good">Egiaztagiri ona aurkitu da %1$s-rako</string>
+ <string name="msg_ip_uid_cert_good_revoke">%1$s-ren egiaztagiri ukatze ona aurkitu da </string>
+ <plurals name="msg_ip_uid_certs_unknown">
+ <item quantity="one">Ezikusten giltza publiko ezezagunak jaulkitako bat egiaztagiri</item>
+ <item quantity="other">Ezikusten giltza publiko ezezagunek jaulkitako %s egiaztagiri</item>
+ </plurals>
+ <string name="msg_ip_uid_classifying_zero">Erabiltzaile ID-ak sailkatzen (giltza ez fidagarriak erabiltzen)</string>
+ <plurals name="msg_ip_uid_classifying">
+ <item quantity="one">Erabiltzaile ID-ak sailkatzen (giltza fidagarri bat erabiltzen)</item>
+ <item quantity="other">Erabiltzaile ID-ak sailkatzen (%s giltza fidagarri erabiltzen)</item>
+ </plurals>
+ <string name="msg_ip_uid_reorder">Erabiltzaile ID-ak ber-antolatzen</string>
+ <string name="msg_ip_uid_processing">Erabiltzaile ID-a %s prozesatzen</string>
+ <string name="msg_ip_uid_revoked">Erabiltzaile ID-a ukatua dago</string>
+ <string name="msg_ip_uat_processing_image">Irudi motako erabiltzaile ezaugarria prozesatzen</string>
+ <string name="msg_ip_uat_processing_unknown">Mota ezezaguneko erabiltzaile ezaugarri prozesatzen</string>
<string name="msg_ip_uat_cert_bad">Egiaztagiri gaitza aurkitu da!</string>
<string name="msg_ip_uat_cert_error">Akatsa egiaztagiria prozesatzerakoan!</string>
+ <string name="msg_ip_uat_cert_nonrevoke">Jadanik badu ez-ukatu egiaztagiri bat, jauzi egiten.</string>
+ <string name="msg_ip_uat_cert_old">Egiaztagiria aurrekoa baino zaharragoa da, jauzi egiten.</string>
<string name="msg_ip_uat_cert_new">Egiaztagiria berriagoa da, aurrekoa ordezten.</string>
<string name="msg_ip_uat_cert_good">Egiaztagiri ona aurkitu da %1$s-rako</string>
+ <string name="msg_ip_uat_cert_good_revoke">Egiaztagiri ukatze ona aurkitu da %1$s-rentzat</string>
+ <plurals name="msg_ip_uat_certs_unknown">
+ <item quantity="one">Giltza publiko ezezagunak jaulkitako egiaztagiri bat ezikusten</item>
+ <item quantity="other">Giltza publiko ezezagunek jaulkitako %s egiaztagiri ezikusten</item>
+ </plurals>
+ <string name="msg_ip_uat_classifying">Erabiltzaile ezaugarriak sailkatzen</string>
+ <string name="msg_ip_uat_revoked">Erabiltzaile ezaugarria ukatua dago</string>
+ <string name="msg_is_bad_type_public">Giltza-uztai publiko sekretu bezala inportatzeko saiakera. Hau akats bat da, mesedez agiritu jakinarazpen bat!</string>
+ <string name="msg_is_bad_type_uncanon">Giltza-uztaia kanonikalizaziorik gabe inportatzeko saiakera. Hau akats bat da, mesedez agiritu jakinarazpen bat!</string>
<!--Import Secret log entries-->
+ <string name="msg_is">%s giltza sekretua inportatzen</string>
<string name="msg_is_db_exception">Datubase akatsa!</string>
+ <string name="msg_is_importing_subkeys">Azpigiltza sekretuak prozesatzen</string>
+ <string name="msg_is_error_io_exc">Akatsa giltza-uztaia kodeatzerakoan</string>
+ <string name="msg_is_merge_public">Inportatutako datuak dagoen giltza-uztai publikoan batzen</string>
+ <string name="msg_is_merge_secret">Inportatutako datuak dagoen giltza-uztai publikoan batzen</string>
+ <string name="msg_is_pubring_generate">Giltza-uztai publikoa sortzen giltza-uztai sekreturako</string>
+ <string name="msg_is_subkey_nonexistent">%s azpigiltza eskuraezina da giltza sekretuan</string>
+ <string name="msg_is_subkey_ok">%s azpigiltza eskuragarria bezala markatuta</string>
+ <string name="msg_is_subkey_pin">%s azpigiltza sekretua eskuragarri bezala markatuta, PIN-arekin</string>
+ <string name="msg_is_success_identical">Giltza-uztaiak ez du datu berririk, ezer ez egiteko</string>
+ <string name="msg_is_success">Giltza-uztai sekretua ongi inportatu da</string>
<!--Keyring Canonicalization log entries-->
+ <string name="msg_kc_error_v3">Hau OpenPGP 3 bertsioko giltza bat da, zeina zaharkituta dago eta ez da gehiago sostengatzen!</string>
+ <string name="msg_kc_error_no_uid">Giltza-uztaiak ez du baliozko erabiltzaile ID-rik!</string>
+ <string name="msg_kc_error_master_algo">Giltza maisuak algoritmo ezezagun bat erabiltzen du (%s)!</string>
+ <string name="msg_kc_master">Maisu giltza prozesatzen</string>
+ <string name="msg_kc_master_bad_type">Mota ezezaguneko (%s) maisu giltza egiaztagiria kentzen</string>
+ <string name="msg_kc_master_bad_err">Maisu giltza egiaztagiri gaitza kentzen</string>
+ <string name="msg_kc_master_bad_type_uid">Erabiltzaile ID egiaztagiria kentzen kokapen okerretik</string>
+ <string name="msg_kc_master_bad">Maisu giltza egiaztagiri gaitza kentzen</string>
+ <string name="msg_kc_sub">%s azpigiltza prozesatzen</string>
+ <string name="msg_kc_sub_bad_type">Azpigiltza egiaztagiri mota ezezaguna: %s</string>
+ <string name="msg_kc_sub_no_cert">Ez da baliozko egiaztagiririk aurkitu %s-rentzat, eraztunetik kentzen</string>
+ <string name="msg_kc_sub_revoke_bad_err">Azpigiltza ukatze egiaztagiri gaitza kentzen</string>
+ <string name="msg_kc_sub_revoke_bad">Azpigiltza ukatze egiaztagiri gaitza kentzen</string>
+ <string name="msg_kc_sub_unknown_algo">Azpigiltzak algoritmo ezezagun bat erabiltzen du, ez inportatzen...</string>
+ <string name="msg_kc_sub_algo_bad_encrpyt">Azpigiltzak enkriptaketa erabilpen ikurra du, baina ezin da enkriptaketarako algoritmoa aurkitu.</string>
+ <string name="msg_kc_sub_algo_bad_sign">Azpigiltzak sinadura erabilpen ikurra du, baina ezin da sinadurarako algoritmoa aurkitu.</string>
+ <string name="msg_kc_uid_bad_local">\'local\' ikurra duen erabiltzaile ID egiaztagiria kentzen</string>
+ <string name="msg_kc_uid_remove">Erabiltzaile ID \'%s\' baliogabea kentzen</string>
+ <string name="msg_kc_uid_warn_encoding">Erabiltzaile ID-a ez da UTF-8 bezala egiaztatu!</string>
+ <string name="msg_kc_uat_jpeg">JPEG motako erabiltzaile ezaugarria prozesatzen</string>
+ <string name="msg_kc_uat_unknown">Mota ezezaguneko erabiltzaile ezaugarria prozesatzen</string>
+ <string name="msg_kc_uat_dup">Erabiltzaile ezaugarri bikoiztua kentzen. Giltza-uztaiak beraietako bi ditu. Honek egiaztagiriak galtzea eragin dezake!</string>
+ <string name="msg_kc_uat_revoke_old">Erabiltzaile ezaugarrirako ukatze egiaztagiri zaharkitua kentzen </string>
<string name="msg_kc_uat_remove">Erabiltzaile ezaugarri baliogabea kentzen</string>
+ <string name="msg_kc_uat_warn_encoding">Erabiltzaile ID-a ez da UTF-8 bezala egiaztatu!</string>
<!--Keyring merging log entries-->
+ <string name="msg_mg_error_secret_dummy">Azpigiltza publiko berria aurkitu da, baina irudizko azpigiltza sekretua sortzea ez dago sostengatuta!</string>
+ <string name="msg_mg_error_heterogeneous">Hatz-aztarna ezberdina duten giltza-uztaiak batzen saiatzen!</string>
+ <string name="msg_mg_error_encode">Akats larria sinadura kodeatzerakoan!</string>
+ <string name="msg_mg_public">%s giltza-uztai publikoan batzen</string>
+ <string name="msg_mg_secret">%s giltza-uztai sekretuan batzen</string>
+ <string name="msg_mg_new_subkey">%s azpigiltza berria gehitzen</string>
+ <string name="msg_mg_found_new">%s egiaztagiri berri aurkitu dira giltza-uztaian</string>
<string name="msg_mg_unchanged">Ez dago ezer batzeko</string>
<!--createSecretKeyRing-->
<string name="msg_cr">Giltza maisu berria sortzen</string>
<string name="msg_cr_error_no_master">Ez da maisu giltza aukerarik adierazi!</string>
+ <string name="msg_cr_error_no_user_id">Giltza-uztaiak gutxienez erabiltzaile ID batekin sortu behar dira!</string>
+ <string name="msg_cr_error_no_certify">Maisu giltzak egiaztagiri ikurra izan behar du!</string>
+ <string name="msg_cr_error_null_expiry">Epemuga ezin daiteke giltza sortzea baino \'lehenago\' izan. Hau programazio akats bat da, mesedez agiritu akats jakinarazpen bat!</string>
+ <string name="msg_cr_error_keysize_512">Giltza neurria 512 edo handiagoa izan behar da!</string>
+ <string name="msg_cr_error_no_curve">Ez da giltzaren neurria adierazi! Hau programazio akats bat da, mesedez agiritu akats jakinarazpen bat!</string>
<string name="msg_cr_error_internal_pgp">Barneko OpenPGP akatsa!</string>
+ <string name="msg_cr_error_unknown_algo">Algoritmo ezezaguna hautatu da! Hau programazio akats bat da, mesedez agiritu akats jakinarazpen bat!</string>
+ <string name="msg_cr_error_flags_dsa">Giltza ikur okerrak hautatuta, DSA ezin da erabili enkriptatzeko!</string>
+ <string name="msg_cr_error_flags_elgamal">Giltza ikur okerrak hautatuta, EIGamal ezin da erabili sinatzeko!</string>
+ <string name="msg_cr_error_flags_ecdsa">Giltza ikur okerrak hautatuta, ECDSA ezin da erabili enkriptatzeko!</string>
+ <string name="msg_cr_error_flags_ecdh">Giltza ikur okerrak hautatuta, ECDH ezin da erabili sinatzeko!</string>
<!--modifySecretKeyRing-->
+ <string name="msg_mr">%s giltza-uztaia aldatzen</string>
+ <string name="msg_mf_divert">Txartel adimentsura desbideratuko da kripto eragiketarako</string>
<string name="msg_mf_error_encode">Kodeaketa salbuespena!</string>
+ <string name="msg_mf_error_fingerprint">Oraingo giltzaren hatz-aztarna ez dator bat itxarondakoarekin!</string>
+ <string name="msg_mf_error_keyid">Ez dago giltza ID-rik. Hau barneko akats bat da, mesedez agiritu akats jakinarazpen bat!</string>
+ <string name="msg_mf_error_integrity">Barneko akatsa, osotasun egiaztapen hutsegitea!</string>
+ <string name="msg_mf_error_master_none">Ez da egiaztagiri maisuri aurkitu eragiketarako! (Guztiak ukatuta?)</string>
+ <string name="msg_mf_error_noexist_primary">Lehen erabiltzaile ID gaitza adierazi da!</string>
+ <string name="msg_mf_error_noexist_revoke">Erabiltzaile ID okerra adierazi da ukatzeko!</string>
+ <string name="msg_mf_error_restricted">Eragiketa murriztua sarhitzik gabe exekutatzeko saiakera! Hau programazio akats bat da, mesedez agiritu akats jakinarazpen bat!</string>
+ <string name="msg_mf_error_revoked_primary">Ukatutako erabiltzaile ID-ak ezin dira lehena izan!</string>
+ <string name="msg_mf_error_null_expiry">Epemuga ezin daiteke azpigiltza sortzea baino \'lehenago\' izan. Hau programazio akats bat da, mesedez agiritu akats jakinarazpen bat!</string>
+ <string name="msg_mf_error_noop">Ez dago ezer egiteko!</string>
+ <string name="msg_mf_error_passphrase_master">Akats larria giltza maisua dekriptatzerakoan! Hau programazio akats bat da, mesedez agiritu akats jakinarazpen bat!</string>
<string name="msg_mf_error_pgp">Barneko OpenPGP akatsa!</string>
<string name="msg_mf_error_sig">Sinadura salbuespena!</string>
+ <string name="msg_mf_error_conflicting_nfc_commands">Ezin da giltza txartel adimentsura mugitu txartelean sinadura sortzen duen eragiketa berean.</string>
+ <string name="msg_mf_error_invalid_flags_for_keytocard">Giltza ikur ezegokiak txartel adimentsu giltzarako.</string>
<string name="msg_mf_master">Maisu egiaztagiriak aldatzen</string>
+ <string name="msg_mf_notation_pin">PIN jakinarazpen paketea gehitzen</string>
+ <string name="msg_mf_passphrase">Giltza-uztaiaren sarhitza aldatzen</string>
+ <string name="msg_mf_pin">PIN-a aldatzen txartelean</string>
+ <string name="msg_mf_admin_pin">Administrari PIN-a aldatzen txartelean</string>
+ <string name="msg_mf_passphrase_key">%s azpigiltza ber-enkriptatzen sarhitz berriarekin</string>
+ <string name="msg_mf_passphrase_empty_retry">Hutsegitea sarhitz berria ezartzerakoan, berriro saiatzen sarhitz zahar huts batekin</string>
+ <string name="msg_mf_passphrase_fail">Azpigiltzarentzako sarhitza ezin da aldatu! (Beste giltzen ezberdina du?)</string>
+ <string name="msg_mf_primary_replace_old">Aurreko lehen erabiltzaile ID-aren egiaztagiria ordezten</string>
+ <string name="msg_mf_primary_new">Egiaztagiri berria sortzen lehen erabiltzaile ID berriarentzat</string>
+ <string name="msg_mf_restricted_mode">Eragiketa modu murriztura aldatzen</string>
+ <string name="msg_mf_subkey_change">%s azpigiltza aldatzen</string>
+ <string name="msg_mf_require_passphrase">Sarhitza behar da eragiketetarako</string>
+ <string name="msg_mf_subkey_new">%s motako azpigiltza berria gehitzen</string>
+ <string name="msg_mf_subkey_new_id">Azpigiltza berriaren ID-a: %s</string>
+ <string name="msg_mf_error_past_expiry">Iraungitze eguna ezin da iraganean izan!</string>
+ <string name="msg_mf_subkey_revoke">%s azpigiltza ukatzen</string>
+ <string name="msg_mf_keytocard_start">%s azpigiltza txartel adimentsura mugitzen</string>
+ <string name="msg_mf_keytocard_finish">%1$s --&gt; %2$s txartel adimentsura mugitu da</string>
+ <string name="msg_mf_success">Giltza-uztaia ongi aldatu da</string>
+ <string name="msg_mf_uid_add">Erabiltzaile ID-a %s gehitzen</string>
+ <string name="msg_mf_uid_primary">Lehen erabiltzaile ID-a %s-ra aldatzen</string>
+ <string name="msg_mf_uid_revoke">Erabiltzaile ID-a %s ukatzen</string>
+ <string name="msg_mf_uid_error_empty">Erabiltzaile ID-a ezin da hutsik egon!</string>
+ <string name="msg_mf_uat_error_empty">Erabiltzaile ezaugarria ezin da hutsik egon!</string>
+ <string name="msg_mf_uat_add_image">Irudi motaren erabiltzaile ezaugarria gehitzen</string>
+ <string name="msg_mf_uat_add_unknown">Mota ezezaguneko erabiltzaile ezaugarria gehitzen</string>
+ <string name="msg_mf_unlock_error">Akatsa giltza-uztaia desblokeatzerakoan!</string>
+ <string name="msg_mf_unlock">Giltza-uztaia desblokeatzerakoan</string>
<!--Consolidate-->
+ <string name="msg_con">Datubasea sendotzen</string>
+ <string name="msg_con_error_bad_state">Sendotzea abiatu da datubasea katxeatu gabe! Hau zihurrenik programazio akats bat da, mesedez agiritu akats jakinarazpen bat.</string>
+ <string name="msg_con_error_concurrent">Sendotzea utzita, jadanik beste hari batean ekinean!</string>
+ <string name="msg_con_save_secret">Giltza-uztai sekretuak gordetzen</string>
+ <string name="msg_con_save_public">Giltza-uztai publikoak gordetzen</string>
<string name="msg_con_db_clear">Datubasea garbitzen</string>
<string name="msg_con_success">Datubasea ongi trinkotu da</string>
+ <string name="msg_con_delete_public">Giltza-uztai publikoaren katxe agiria ezabatzen</string>
+ <string name="msg_con_delete_secret">Giltza-uztai sekretuaren katxe agiria ezabatzen</string>
<string name="msg_con_error_db">Akatsa datubasea irekitzerakoan!</string>
<string name="msg_con_error_io_public">S/I akatsa giltza publikoak katxera idazterakoan!</string>
<string name="msg_con_error_io_secret">S/I akatsa giltza sekretuak katxera idazterakoan!</string>
<string name="msg_con_error_public">Akatsa giltza publikoak ber-inportatzerakoan!</string>
<string name="msg_con_error_secret">Akatsa giltza sekretuak ber-inportatzerakoan!</string>
<string name="msg_con_recover">Trinkotze aurrerabidea berrekiten</string>
+ <plurals name="msg_con_reimport_public">
+ <item quantity="one">Giltza publiko bat berrinportatzen</item>
+ <item quantity="other">%d giltza publiko berrinportatzen</item>
+ </plurals>
+ <string name="msg_con_reimport_public_skip">Ez dago giltza publikorik berrinportatzeko, jauzi egiten...</string>
+ <plurals name="msg_con_reimport_secret">
+ <item quantity="one">Giltza sekretu bat berrinportatzen</item>
+ <item quantity="other">%d giltza sekretu berrinportatzen</item>
+ </plurals>
+ <string name="msg_con_reimport_secret_skip">Ez dago giltza sekreturik berrinportatzeko, jauzi egiten...</string>
+ <string name="msg_con_warn_delete_public">Salbuespena katxe publikoaren agiria ezabatzerakoan</string>
+ <string name="msg_con_warn_delete_secret">Salbuespena katxe agiri sekretua ezabatzerakoan</string>
<!--Edit Key (higher level than modify)-->
- <string name="msg_ed_caching_new">Sar-esaldi berria katxeatzen</string>
+ <string name="msg_ed">Giltza eragiketa burutzen</string>
+ <string name="msg_ed_caching_new">Sarhitz berria katxeatzen</string>
+ <string name="msg_ed_error_no_parcel">Ez dago Gorde_Giltza-uztai_Eremua (hau akats bat da, mesedez jakinarazi)</string>
<string name="msg_ed_error_key_not_found">Giltza ez da aurkitu!</string>
+ <string name="msg_ed_error_extract_public_upload">Akatsa giltza publikoa igotzeko ateratzerakoan!</string>
+ <string name="msg_ed_fetching">Giltza lortzen aldatzeko (%s)</string>
+ <string name="msg_ed_success">Giltza eragiketa ongi burutu da</string>
<!--Promote key-->
+ <string name="msg_pr">Giltza publikoa giltza sekretura bultzatzen</string>
+ <string name="msg_pr_all">Azpigiltza guztiak sustatzen</string>
<string name="msg_pr_error_key_not_found">Giltza ez da aurkitu!</string>
+ <string name="msg_pr_fetching">Giltza lortzen aldatzeko (%s)</string>
+ <string name="msg_pr_subkey_match">Azpigiltza sustatzen: %s</string>
+ <string name="msg_pr_subkey_nomatch">Azpigiltza ez dago YubiKey-n: %s</string>
+ <string name="msg_pr_success">Giltza ongi sustatu da</string>
<!--Other messages used in OperationLogs-->
<string name="msg_ek_error_not_found">Giltza ez da aurkitu!</string>
<!--Messages for DecryptVerify operation-->
+ <string name="msg_dc_askip_no_key">Datuak ez daude giltza ezagun batekin enkriptatuta, jauzi egiten...</string>
+ <string name="msg_dc_askip_not_allowed">Datuak ez daude giltza ahalbidetu batekin enkriptatuta, jauzi egiten...</string>
+ <string name="msg_dc_charset">Idazburu hizkikodea aurkitu da: \'%s\'</string>
+ <string name="msg_dc_clear_data">Datu literalak prozesatzen</string>
+ <string name="msg_dc_clear_decompress">Konprimitutako datuak despaketatzen</string>
<string name="msg_dc_clear_meta_file">Agirizena: %s</string>
<string name="msg_dc_clear_meta_mime">MIME mota: %s</string>
<string name="msg_dc_clear_meta_size">Agiri neurria: %s</string>
<string name="msg_dc_clear_meta_size_unknown">Agiri neurria ezezaguna da</string>
<string name="msg_dc_clear_meta_time">Aldaketa ordua: %s</string>
<string name="msg_dc_clear_signature_bad">Sinadura egiaztapena EZ ONGI!</string>
- <string name="msg_dc_error_unsupported_hash_algo">Hash algoritmo sostengatu gabea eta potentzialki segurtasun gabea!</string>
<string name="msg_dc_clear_signature_check">Sinadura datuak egiaztatzen</string>
<string name="msg_dc_clear_signature_ok">Sinadura egiaztapena ONGI</string>
<string name="msg_dc_clear_signature">Sinadura datuak gerorako gordetzen</string>
+ <string name="msg_dc_clear">Cleartext datuak prozesatzen</string>
+ <string name="msg_dc_error_bad_passphrase">Akatsa giltza desblokeatzerakoan, sarhitz gaitza!</string>
+ <string name="msg_dc_error_sym_passphrase">Akatsa datuak dekriptatzerakoan! (Sar-esaldi gaitza?)</string>
+ <string name="msg_dc_error_corrupt_data">Datuak hondatuta daude!</string>
+ <string name="msg_dc_error_extract_key">Akats ezezaguna giltza desblokeatzerakoan!</string>
<string name="msg_dc_error_integrity_check">Osotasun egiaztapen akatsa!</string>
+ <string name="msg_dc_error_invalid_data">OpenPGP eduki enkriptatu edo sinatu baliogabea!</string>
+ <string name="msg_dc_error_io">Akats bat aurkitu da sarrera datuak irakurtzerakoan!</string>
+ <string name="msg_dc_error_input">Akatsa sarrerako datu jarioa irekitzerakoan!</string>
+ <string name="msg_dc_error_no_data">Ez da datu enkriptaturik aurkitu jarioan!</string>
+ <string name="msg_dc_error_no_key">Ez da enkriptatutako daturik giltza sekretu ezagunarekin aurkitu jarioan!</string>
+ <string name="msg_dc_error_pgp_exception">OpenPGP Salbuespen bat aurkitu da eragiketan zehar!</string>
<string name="msg_dc_integrity_check_ok">Osotasun egiaztapena ONGI!</string>
- <string name="msg_dc_pass_cached">Sar-esaldia erabiltzen katxetik</string>
+ <string name="msg_dc_ok_meta_only">Metadatuak bakarrik eskatu dira, dekriptaketa jauzi egiten</string>
+ <string name="msg_dc_ok">Dekriptaketa/Egiaztapena amaituta</string>
+ <string name="msg_dc_pass_cached">Sarhitza katxetik erabiltzen</string>
+ <string name="msg_dc_pending_nfc">NFC ezagutarazlea beharrezkoa, erabiltzaile sarrera eskatzen...</string>
+ <string name="msg_dc_pending_passphrase">Sarhitza beharrezkoa, erabiltzaile sarrera eskatzen...</string>
+ <string name="msg_dc_prep_streams">Jarioak dekriptatzeko gertatzen</string>
<string name="msg_dc">Dekriptaketa eragiketa abiatzen...</string>
<string name="msg_dc_sym_skip">Datu simetrikoak ez daude ahalbidetuta, jauzi egiten...</string>
<string name="msg_dc_unlocking">Giltza sekretua desblokeatzen</string>
+ <string name="msg_dc_insecure_symmetric_encryption_algo">Enkriptaketa algoritmo segurtasun gabea erabili da! Hau aplikazioa eguneratu gabe dagoelako, edo eraso bategaitik gertatu daiteke.</string>
+ <string name="msg_dc_insecure_hash_algo">Hash algoritmo segurtasun gabea erabili da! Hau aplikazioa eguneratu gabe dagoelako, edo eraso bategaitik gertatu daiteke.</string>
+ <string name="msg_dc_insecure_mdc_missing">Ez dago Aldaketa Atzemate Kodea (MDC) paketea! Hau aplikazioa eguneratu gabe dagoelako edo beheratze eraso bategaitik gertatu daiteke.</string>
+ <string name="msg_dc_insecure_key">Segurtasun gabeko giltza: Bietako bat, edo RSA/DSA/ElGamal bit luzera laburregia da edo ECC bihurgune/algoritmoa segurtasun gabekotzat hartzen da! Hau aplikazioa eguneratu gabe dagoelako, edo eraso bategaitik gertatu daiteke.</string>
<!--Messages for VerifySignedLiteralData operation-->
+ <string name="msg_vl">Sinadura egiaztapena abiatzen</string>
+ <string name="msg_vl_error_wrong_key">Mezua ez dago giltza zuzenarekin sinatuta</string>
<string name="msg_vl_clear_meta_file">Agirizena: %s</string>
<string name="msg_vl_clear_meta_mime">MIME mota: %s</string>
<string name="msg_vl_clear_meta_time">Aldaketa ordua: %s</string>
@@ -507,20 +1005,35 @@
<string name="msg_vl_ok">Ongi</string>
<!--Messages for SignEncrypt operation-->
<string name="msg_se">Sinadura/enkriptaketa eragiketa abiatzen</string>
+ <string name="msg_se_input_uri">Sarrera URI-tik prozesatzen</string>
<string name="msg_se_error_no_input">Ez da sarrerarik eman!</string>
<string name="msg_se_error_input_uri_not_found">Akatsa URI-a irakurtzeko irekitzerakoan!</string>
<string name="msg_se_error_output_uri_not_found">Akatsa URI-a idazteko irekitzerakoan!</string>
+ <string name="msg_se_error_too_many_inputs">Irteera baino sarrera gehiago adierazi dira! Hau zihurrenik programazio akats bat da, mesedez jakinarazi!</string>
<string name="msg_se_success">Sinadura/enkriptaketa eragiketa ongi</string>
<!--Messages for PgpSignEncrypt operation-->
<string name="msg_pse_asymmetric">Giltza publikoak gertatzen enkriptaketarako</string>
+ <string name="msg_pse_clearsign_only">Cleartext sarrera sinadura ez dago sostengatuta!</string>
<string name="msg_pse_compressing">Konpresioa gertatzen</string>
<string name="msg_pse_encrypting">Datuak enkriptatzen</string>
- <string name="msg_pse_error_bad_passphrase">Sar-esaldi gaitza!</string>
+ <string name="msg_pse_error_bad_passphrase">Sarhitz gaitza!</string>
+ <string name="msg_pse_error_io">SI Salbuespen bat aurkitu da eragiketan zehar!</string>
+ <string name="msg_pse_error_key_sign">Hautaturiko sinatze giltzak ezin du daturik sinatu!</string>
+ <string name="msg_pse_error_sign_key">Akatsa giltza sinatua lortzerakoan!</string>
<string name="msg_pse_error_nfc">NFC datu akatsa!</string>
- <string name="msg_pse_error_no_passphrase">Ez da sar-esaldirik eman!</string>
+ <string name="msg_pse_error_no_passphrase">Ez da sarhitzik eman!</string>
<string name="msg_pse_error_pgp">Barneko OpenPGP akatsa!</string>
<string name="msg_pse_error_sig">OpenPGP sinadura salbuespena aurkitu da!</string>
<string name="msg_pse_error_unlock">Akats ezezaguna giltza desblokeatzerakoan!</string>
+ <string name="msg_pse_key_ok">Enkriptatzen giltzarako: %s</string>
+ <string name="msg_pse_key_unknown">Ez dago enkriptaketarako giltza: %s</string>
+ <string name="msg_pse_key_warn">Giltza gaitza enkriptaketarako: %s</string>
+ <string name="msg_pse_ok">Sinadura/enkriptaketa eragiketa ongi</string>
+ <string name="msg_pse_pending_nfc">NFC ezagutarazlea beharrezkoa, erabiltzaile sarrera eskatzen...</string>
+ <string name="msg_pse_pending_passphrase">Sarhitza beharrezkoa, erabiltzaile sarrera eskatzen...</string>
+ <string name="msg_pse_signing">Datuak sinatzen (enkriptaketa gabe)</string>
+ <string name="msg_pse_signing_cleartext">Cleartext sinadura sortzen</string>
+ <string name="msg_pse_signing_detached">Sinadura deserantsia sortzen</string>
<string name="msg_pse_sigcrypting">Datuak sinadurarekin enkriptatzen</string>
<string name="msg_pse">Sinadura eta/edo enkriptaketa eragiketa abiatzen</string>
<string name="msg_pse_symmetric">Enkriptaketa simetrikoa gertatzen</string>
@@ -528,13 +1041,45 @@
<string name="msg_crt_error_master_not_found">Maisu giltza ez da aurkitu!</string>
<string name="msg_crt_error_nothing">Ez da giltzarik egiaztagiritu!</string>
<string name="msg_crt_error_unlock">Akatsa maisu giltza desblokeatzerakoan!</string>
+ <string name="msg_crt">Giltza-uztaiak egiaztagiritzen</string>
+ <string name="msg_crt_master_fetch">Maisu giltza egiaztagiritua lortzen</string>
+ <string name="msg_crt_nfc_return">NFC ikusleihora itzultzen</string>
+ <string name="msg_crt_save">%s giltza egiaztagiritua gordetzen</string>
+ <string name="msg_crt_saving">Giltza-uztaiak gordetzen</string>
+ <string name="msg_crt_unlock">Maisu giltza desblokeatzen</string>
<string name="msg_crt_success">Nortasunak ongi egiaztagiritu dira</string>
<string name="msg_crt_warn_not_found">Giltza ez da aurkitu!</string>
<string name="msg_crt_warn_cert_failed">Egiaztagiri sortze hutsegitea!</string>
<string name="msg_crt_warn_save_failed">Gordetze eragiketa hutsegitea!</string>
+ <string name="msg_crt_warn_upload_failed">Igoera eragiketa hutsegitea!</string>
<string name="msg_crt_upload_success">Giltza ongi igo da zerbitzarira</string>
+ <plurals name="msg_import">
+ <item quantity="one">Giltza inportatzen</item>
+ <item quantity="other">%d giltza inportatzen</item>
+ </plurals>
+ <string name="msg_import_fetch_error_decode">Akatsa berreskuratutako giltza-uztai dekodeatzerakoan!</string>
+ <string name="msg_import_fetch_error">Giltza ezin da berreskuratu! (Sare arazoak?)</string>
+ <string name="msg_import_fetch_keybase">keybase.io-tik berreskuratzen: %s</string>
+ <string name="msg_import_fetch_keyserver_error">Ezin da giltza giltza-zerbitzarietatik berreskuratu: %s</string>
+ <string name="msg_import_fetch_keyserver">Giltza-zerbitzaritik berreskuratzen: %s</string>
+ <string name="msg_import_fetch_keyserver_ok">Giltza ongi berreskuratu da</string>
+ <string name="msg_import_keyserver">%s giltza-zerbitzaria erabiltzen</string>
+ <string name="msg_import_fingerprint_error">Lortutako giltzaren hatz-aztarna ez dator bat itxarondakoarekin!</string>
+ <string name="msg_import_fingerprint_ok">Hatz-aztarna egiaztapena ONGI</string>
+ <string name="msg_import_merge">Berreskuratutako datuak batzen</string>
+ <string name="msg_import_merge_error">Akatsa berreskuratutako datuak batzerakoan!</string>
+ <string name="msg_import_error">Inportatze eragiketa ongi hutsegitea!</string>
+ <string name="msg_import_error_io">Eragiketa hutsegitea s/i akats bategaitik!</string>
+ <string name="msg_import_partial">Inportatze eragiketa ongi burutu da, akatsekin!</string>
<string name="msg_import_success">Inportatze eragiketa ongi burutu da!</string>
+ <plurals name="msg_export">
+ <item quantity="one">Giltza bat esportatzen</item>
+ <item quantity="other">%d giltza esportatzen</item>
+ </plurals>
<string name="msg_export_all">Giltza guztiak esportatzen</string>
+ <string name="msg_export_public">Giltza publikoa esportatzen %s</string>
+ <string name="msg_export_upload_public">%s giltza publikoa igotzen</string>
+ <string name="msg_export_secret">%s giltza sekretua esportatzen</string>
<string name="msg_export_error_no_file">Ez da agirizenik adierazi!</string>
<string name="msg_export_error_fopen">Akatsa agiria irekitzen!</string>
<string name="msg_export_error_no_uri">Ez da URI-rik adierazi!</string>
@@ -543,12 +1088,47 @@
<string name="msg_export_error_db">Datubase akatsa!</string>
<string name="msg_export_error_io">Sarrera/irteera akatsa!</string>
<string name="msg_export_error_key">Akatsa giltza datuak aurre-prozesatzerakoan!</string>
+ <string name="msg_export_error_upload">Hutsegitea giltza zerbitzarira igotzean! Mesedez egiaztatu zure internet elkarketa.</string>
<string name="msg_export_success">Esportatze eragiketa ongi burutu da!</string>
+ <string name="msg_export_upload_success">Giltza-zerbitzarira igotzea ongi!</string>
<string name="msg_del_error_empty">Ez dago ezer ezabatzeko!</string>
<string name="msg_del_error_multi_secret">Giltza sekretuak banaka bakarrik ezabatu daitezke!</string>
+ <plurals name="msg_del">
+ <item quantity="one">Giltza bat ezabatzen</item>
+ <item quantity="other">%d giltza ezabatzen</item>
+ </plurals>
+ <string name="msg_del_key">%s giltza ezabatzen</string>
+ <string name="msg_del_key_fail">Hutsegitea %s giltza ezabatzerakoan</string>
+ <string name="msg_del_consolidate">Datubasea sendotzen giltza sekretua ezabatu ondoren</string>
+ <plurals name="msg_del_ok">
+ <item quantity="one">Ongi ezabatuta giltza</item>
+ <item quantity="other">Ongi ezabatuta %d giltza</item>
+ </plurals>
+ <plurals name="msg_del_fail">
+ <item quantity="one">Hutsegitea giltza bat ezabatzerakoan</item>
+ <item quantity="other">Hutsegitea %d giltza ezabatzerakoan</item>
+ </plurals>
+ <string name="msg_revoke_error_empty">Ez dago ezer ukatzeko!</string>
+ <string name="msg_revoke_error_not_found">Ezin da giltza aurkitu ukatzeko!</string>
+ <string name="msg_revoke_key">%s giltza ukatzen</string>
+ <string name="msg_revoke_key_fail">Hutsegitea giltza ukatzerakoan</string>
+ <string name="msg_revoke_ok">Giltza ongi ukatu da</string>
<string name="msg_acc_saved">Kontua gordeta</string>
<string name="msg_download_success">Ongi jeitsi da!</string>
<string name="msg_download_no_valid_keys">Ez da baliozko giltzarik aurkitu agiri/gakoan!</string>
+ <string name="msg_download_no_pgp_parts">TODO: anitzak!</string>
+ <plurals name="error_import_non_pgp_part">
+ <item quantity="one">gertatutako agiriaren zati bat baliozko OpenPGP objetua da baina ez OpenPGP giltza</item>
+ <item quantity="other">gertatutako agiriaren zati batzuk baliozko OpenPGP objetuak dira baina ez OpenPGP giltzak</item>
+ </plurals>
+ <string name="msg_download_query_too_short">Bilaketa eskaera laburregia. Mesedez zehaztu zure eskaera!</string>
+ <string name="msg_download_too_many_responses">Giltza bilaketa eskaerak hautagai gehiegi itzuli ditu. Mesedez zehaztu zure eskaera!</string>
+ <string name="msg_download_query_too_short_or_too_many_responses">Ez dago giltzarik edo gehiegi aurkitu dira. Mesedez hobetu zure eskaera!</string>
+ <string name="msg_download_query_failed">Akats bat gertatu da giltzak bilatzerakoan.</string>
+ <!--Messages for Keybase Verification operation-->
+ <string name="msg_keybase_verification">Giltzabase egiaztapen saiakera %s-rentzat</string>
+ <string name="msg_keybase_error_dns_fail">DNS TXT Grabaketa berreskurapen hutsegitea</string>
+ <string name="msg_keybase_error_specific">%s</string>
<!--Messages for Export Log operation-->
<string name="msg_export_log_start">Esportatze oharra</string>
<string name="msg_export_log_error_fopen">Akatsa agiria irekitzerakoan</string>
@@ -556,44 +1136,140 @@
<string name="msg_export_log_error_writing">S/I akatsa agirira idazterakoan!</string>
<string name="msg_export_log_success">Oharra ongi esportatu da!</string>
<!--PassphraseCache-->
- <string name="passp_cache_notif_click_to_clear">Klikatu katxeatutako sar-esaldiak garbitzeko</string>
- <string name="passp_cache_notif_keys">Katxeatutako Sar-esaldiak:</string>
- <string name="passp_cache_notif_clear">Garibut Katxea</string>
- <string name="passp_cache_notif_pwd">Sar-esaldia</string>
+ <string name="passp_cache_notif_click_to_clear">Ikutu sarhitzak garbitzeko</string>
+ <plurals name="passp_cache_notif_n_keys">
+ <item quantity="one">%d sarhitz gogoratuta</item>
+ <item quantity="other">%d sarhitz gogoratuta</item>
+ </plurals>
+ <string name="passp_cache_notif_keys">Gogoratutako sarhitzak</string>
+ <string name="passp_cache_notif_clear">Garbitu Sarhitzak</string>
+ <string name="passp_cache_notif_pwd">Sarhitza</string>
+ <!--Keyserver sync-->
<!--First Time-->
+ <string name="first_time_text1">Berreskuratu zure pribatutasuna OpenKeychain-ekin!</string>
+ <string name="first_time_create_key">Sortu nire giltza</string>
<string name="first_time_import_key">Inportatu giltza agiritik</string>
<string name="first_time_yubikey">Erabili YubiKey NEO</string>
<string name="first_time_skip">Jauzi Ezarpena</string>
+ <string name="first_time_blank_yubikey">YubiKey huts hau erabiltzea nahi duzu OpenKeychain-ekin?\n\nMesedez irten Yubikey-tik orain, berriro galdetuko zaizu beharrezkoa denean!</string>
+ <string name="first_time_blank_yubikey_yes">Erabili YubiKey hau</string>
+ <string name="backup_text">Zure jabetzako giltzak dituzten babeskopiak inoiz ez dira besteekin elkarbanatu behar!</string>
+ <string name="backup_all">Giltza guztiak + zeure jabetzako giltzak</string>
+ <string name="backup_public_keys">Giltza guztiak</string>
+ <string name="backup_section">Babeskopia</string>
<!--unsorted-->
<string name="section_certifier_id">Egiaztatzailea</string>
<string name="section_cert">Egiaztagiriaren Xehetasunak</string>
<string name="label_user_id">Nortasuna</string>
+ <string name="unknown_uid">&lt;ezezaguna&gt;</string>
<string name="empty_certs">Ez dago egiaztagiririk giltza honentzat</string>
+ <string name="certs_text">Hemen balioztatutako berez-egiaztagiriak eta zeure giltzekin sortutako egiaztagiri balioztatuak bakarrik erakusten dira.</string>
+ <string name="section_uids_to_certify">Nortasunak honako</string>
+ <string name="certify_text">Inportatzen ari zaren giltzek \"nortasunak\":izenak eta posta helbideak dituzte. Hautatu zehatz-mehatz hauek itxaroten duzunarekin bat datozela baieztatzeko.</string>
+ <string name="certify_fingerprint_text">Alderatu erakutsitako hatz-aztarnak, hizkiz-hizki, zure gailuko ereduek erakusten duten batekin.</string>
+ <string name="certify_fingerprint_text2">Erakutsitako hatz-aztarnak bat datoz?</string>
+ <string name="label_revocation">Ukatze Zergaitia</string>
<string name="label_cert_type">Mota</string>
<string name="error_key_not_found">Giltza ez da aurkitu!</string>
<string name="error_key_processing">Akatsa giltza prozesatzerakoan!</string>
+ <string name="key_divert">desbideratu txartel adimentsura</string>
+ <string name="key_no_passphrase">sarhitzik gabe</string>
+ <string name="key_unavailable">eskuraezina</string>
+ <string name="secret_cannot_multiple">Zure jabetzako giltzak banaka bakarrik ezabatu daitezke!</string>
+ <string name="title_view_cert">Ikusi Egiaztagiriaren Xehetasunak</string>
<string name="unknown_algorithm">ezezaguna</string>
<string name="can_sign_not">ezin da sinatu</string>
<string name="error_no_encrypt_subkey">Ez dago enkriptaketa azpigiltzarik eskuragarri!</string>
<string name="contact_show_key">Erakutsi (%s) giltza</string>
+ <string name="swipe_to_update">Irristatu behera giltza-zerbitzaritik eguneratzeko</string>
+ <string name="error_no_file_selected">Hautatu agiri bat gutxienez enkriptatzeko!</string>
+ <string name="error_multi_files">Agiri ugari gordetzea ez dago sostengatuta. Hau oraingo Androiden muga bat da.</string>
+ <string name="error_multi_clipboard">Agiri anitz gakora enkriptatzea ez dago sostengatuta.</string>
+ <string name="error_detached_signature">Agiri binarioen sinatzea-bakarrik eragiketa ez dago sostengatuta, hautatu gutxienez enkriptaketa giltza bat.</string>
+ <string name="error_empty_text">Idatzi idazkiren bat enkriptatzeko!</string>
<string name="key_colon">Giltza:</string>
+ <string name="exchange_description">Giltza trukatzea bat hasteko, hautatu eskuinaldean kide zenbatekoa, orduan sakatu \"Hasi trukatzea\" botoia.\n\nBeste bi galdera egingo zaizkizu kide zuzenek besterik ez dutela eskuhartzen eta beren hatz-aztarnak zuzenak direla zihurtatzeko.</string>
+ <string name="btn_start_exchange">Hasi trukatzea</string>
+ <string name="user_id_none"><![CDATA[<none>]]></string>
+ <!--Android Account-->
+ <string name="account_no_manual_account_creation">Ezin duzu OpenKeychain konturik sortu eskuz.</string>
+ <string name="account_privacy_title">Pribatutasuna</string>
+ <string name="account_privacy_text">OpenKeychain-ek ez ditu zure harremanak Internet-ekin aldiberetzen. Harremanak giltzekin loturatzen ditu besterik gabe izen eta post@ helbideetan ohinarrituta. Hau lineaz-kanpo egiten du zure gailuan.</string>
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
<string name="title_unlock_method">Hautatu desblokeatze metodo bat</string>
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
- <string name="enter_passphrase">Sartu sar-esaldia</string>
- <string name="passphrase">Sar-esaldia</string>
- <string name="noPassphrase">Ez dago sar-esaldirik</string>
- <string name="no_passphrase_set">Ez da sar-esaldirik ezarri</string>
- <string name="passphrases_match">Sar-esaldia ez dator bat</string>
- <string name="passphrase_saved">Sar-esaldia gordeta</string>
- <string name="passphrase_invalid">Sar-esaldi baliogabea</string>
- <string name="missing_passphrase">Sar-esaldia ez dago</string>
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
+ <string name="enter_passphrase">Idatzi sarhitza</string>
+ <string name="passphrase">Sarhitza</string>
+ <string name="noPassphrase">Sarhitzik gabe</string>
+ <string name="no_passphrase_set">Ez da sarhitzik ezarri</string>
+ <string name="passphrases_match">Sarhitzak ez datoz bat</string>
+ <string name="passphrase_saved">Sarhitza gordeta</string>
+ <string name="passphrase_invalid">Sarhitz baliogabea</string>
+ <string name="missing_passphrase">Sarhitz ez dago</string>
<string name="passphrase_again">Berriro</string>
+ <string name="lockpattern">Blokeatze-eredua</string>
+ <string name="lockpatternNFC">NFC + Blokeo-eredua</string>
<string name="unlock_method">Desblokeatze metodoa</string>
- <string name="set_passphrase">Ezarri sar-esaldia</string>
+ <string name="set_passphrase">Ezarri sarhitza</string>
<string name="nfc_title">NFC</string>
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
+ <string name="nfc_wrong_tag">Etiketa Okerra. Mesedez saiatu berriro.</string>
+ <string name="enable_nfc">Mesedez gaitu NFC zure ezarpenetan</string>
+ <string name="no_nfc_support">Gailu honek ez du NFC sostengatzen</string>
+ <string name="nfc_write_succesful">Ongi idatzi da NFC etiketan</string>
<string name="unlocked">Desblokeatuta</string>
<string name="nfc_settings">Ezarpenak</string>
+ <string name="snack_yubikey_view">Ikusi</string>
+ <string name="snack_yubikey_import">Inportatu</string>
+ <string name="button_bind_key">Blindatu Giltza</string>
+ <string name="yubikey_serno">Serie Zbk: %s</string>
+ <string name="yubikey_create">Jarri YubiKey zure gailuaren atzealdean.</string>
+ <string name="btn_import">Inportatu</string>
+ <string name="snack_yubi_other">Giltza ezberdina biltegiratuta YubiKey-n!</string>
+ <string name="error_nfc">NFC Akatsa: %s</string>
+ <plurals name="error_pin">
+ <item quantity="one">PIN okerra!\n%d saiakera gelditzen da.</item>
+ <item quantity="other">PIN okerra!\n%d saiakera gelditzen dira.</item>
+ </plurals>
+ <string name="error_nfc_terminated">YubiKey amaiera egoeran.</string>
+ <string name="error_nfc_wrong_length">Sartutako PIN-a laburregia da. PIN-ak gutxienez 6 digituko luzera du.</string>
+ <string name="error_nfc_conditions_not_satisfied">Ez dira erabilpen baldintzak asetu.</string>
+ <string name="error_nfc_security_not_satisfied">Ez da segurtasun egoera asetu.</string>
+ <string name="error_nfc_authentication_blocked">PIN-a blokeatuta saiakera gehiegiren ondoren.</string>
+ <string name="error_nfc_data_not_found">Giltza edo objetua ez da aurkitu.</string>
+ <string name="error_nfc_unknown">Akats Ezezaguna</string>
+ <string name="error_nfc_bad_data">YubiKeyk datu baliogabeak jakinarazi ditu.</string>
+ <string name="error_nfc_chaining_error">YubiKeyk itxaroten zuen azken agindua kate batean.</string>
+ <string name="error_nfc_header">YubiKeyk %s byte baliogabe jakinarazi ditu.</string>
+ <string name="error_nfc_try_again">Saitu berriro</string>
+ <string name="error_pin_nodefault">Berezko PIN-a baztertua izan da!</string>
+ <string name="error_temp_file">Akatsa aldibaterako agiria sortzerakoan.</string>
+ <string name="btn_delete_original">Ezabatu jatorrizko agiria</string>
+ <string name="snack_encrypt_filenames_on">Agirizenak enkriptatuta <b>daude</b>.</string>
+ <string name="snack_encrypt_filenames_off">Agirizenak <b>ez daude</b> enkriptatuta.</string>
+ <string name="snack_armor_on">Irteera kodeaketa Idazki bezala.</string>
+ <string name="snack_armor_off">Irteera kodeaketa Bitar bezala.</string>
+ <string name="snack_compression_on">Konpresioa <b>gaituta</b>.</string>
+ <string name="snack_compression_off">Konpresioa <b>ezgaituta</b>.</string>
+ <string name="error_loading_keys">Akatsa giltzak gertatzerakoan!</string>
+ <string name="error_empty_log">(akatsa, oharra hutsik)</string>
+ <string name="error_reading_text">Ezin da irakurri sarrera dekriptatzeko!</string>
+ <string name="filename_unknown">&lt;agirizenik ez&gt;</string>
+ <string name="filename_unknown_text">&lt;idazki lau datuak&gt;</string>
+ <string name="intent_show">Erakutsi Sinatutako/Enkriptatutako Edukia</string>
+ <string name="view_internal">Ikusi OpenKeychain-en</string>
+ <string name="error_preparing_data">Akatsa datuak gertatzerakoan!</string>
+ <string name="label_clip_title">Enkriptatutako Datuak</string>
+ <string name="progress_processing">Prozesatzen...</string>
+ <string name="error_saving_file">Akatsa agiria gordetzerakoan!</string>
+ <string name="file_saved">Agiria gordeta!</string>
+ <string name="file_delete_ok">Jatorrizko agiria ezabatuta.</string>
+ <string name="file_delete_none">Ez da agiria ezabatu! (Jadanik ezabatuta?)</string>
+ <string name="file_delete_exception">Jatorrizko agiria ezin da ezabatu!</string>
+ <string name="error_clipboard_empty">Gakoa hutsik dago!</string>
+ <string name="error_clipboard_copy">Akatsa datuak gakora kopiatzerakoan!</string>
+ <string name="error_scan_fp">Akatsa hatz-aztarna eskaneatzerakoan!</string>
+ <string name="error_scan_match">Hatz-aztarnak ez datoz bat!</string>
+ <string name="error_expiry_past">Iraungitze eguna iraganda dago!</string>
</resources>
diff --git a/OpenKeychain/src/main/res/values-fa/strings.xml b/OpenKeychain/src/main/res/values-fa/strings.xml
new file mode 100644
index 000000000..86d8ab5cd
--- /dev/null
+++ b/OpenKeychain/src/main/res/values-fa/strings.xml
@@ -0,0 +1,333 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <!--GENERAL: Please put all strings inside quotes as described in example 1 on
+ http://developer.android.com/guide/topics/resources/string-resource.html (scroll down to "Escaping apostrophes and quotes").-->
+ <string name="app_name">دسته کلید باز</string>
+ <!--title-->
+ <string name="title_encrypt_text">رمزگذاری</string>
+ <string name="title_encrypt_files">رمزگذاری</string>
+ <string name="title_decrypt">رمزگشایی</string>
+ <string name="title_add_subkey">اضافه کردن زیرکلید</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">سرورهای کلید OpenPGP</string>
+ <string name="title_change_passphrase">تغییر رمزعبور</string>
+ <string name="title_share_fingerprint_with">به اشتراک گذاری اثر انگشت با...</string>
+ <string name="title_share_key">به اشتراک گذاری کلید با...</string>
+ <string name="title_share_file">به اشتراک گذاری فایل با...</string>
+ <string name="title_share_message">به اشتراک گذاری متن با...</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_send_key">آپلود در سرور کلید</string>
+ <string name="title_certify_key">تأیید کلید</string>
+ <string name="title_key_details">جزئیات کلید</string>
+ <string name="title_help">کمک</string>
+ <string name="title_log_display">لاگ</string>
+ <string name="title_exchange_keys">مبادلهٔ کلیدها</string>
+ <string name="title_advanced_key_info">اطلاعات بیشتر</string>
+ <string name="title_delete_secret_key">آیا کلید \'%s\' خود را پاک می‌کنید؟</string>
+ <string name="title_export_log">خروج لاگ</string>
+ <string name="title_manage_my_keys">مدیریت کلیدهام</string>
+ <!--section-->
+ <string name="section_user_ids">هویت‌ها</string>
+ <string name="section_yubikey">کلید Yubi</string>
+ <string name="section_should_you_trust">آیا به این کلید اعتماد دارید؟</string>
+ <string name="section_keys">زیرکلیدها</string>
+ <string name="section_cloud_search">جستجوی اینترنت</string>
+ <string name="section_proxy_settings">تنظیمات پراکسی</string>
+ <string name="section_gui">رابط</string>
+ <string name="section_certify">تأیید</string>
+ <string name="section_share_key">کلید</string>
+ <string name="section_key_server">سرور کلید</string>
+ <string name="section_fingerprint">اثر انگشت</string>
+ <string name="section_encrypt">رمزگذاری</string>
+ <string name="section_decrypt">رمزگشایی/بررسی</string>
+ <string name="section_current_expiry">انقضأ فعلی</string>
+ <string name="section_new_expiry">انقضأ جدید</string>
+ <!--button-->
+ <string name="btn_decrypt_verify_file">رمزگشایی، بررسی و ذخیره فایل</string>
+ <string name="btn_encrypt_share_file">رمزگذاری و به اشتراک گذاری فایل</string>
+ <string name="btn_encrypt_save_file">رمزگذاری و ذخیرهٔ فایل</string>
+ <string name="btn_save_file">ذخیرهٔ فایل</string>
+ <string name="btn_save">ذخیره</string>
+ <string name="btn_view_log">مشاهدهٔ لاگ</string>
+ <string name="btn_do_not_save">انصراف</string>
+ <string name="btn_delete">حذف</string>
+ <string name="btn_no_date">بدون انقضأ</string>
+ <string name="btn_okay">تأیید</string>
+ <string name="btn_export_to_server">آپلود در سرور کلید</string>
+ <string name="btn_next">بعدی</string>
+ <string name="btn_back">قبلی</string>
+ <string name="btn_no">نه</string>
+ <string name="btn_match">اثر انگشت‌ها مطابقت دارد</string>
+ <string name="btn_share_encrypted_signed">رمزگذاری و به اشتراک گذاری متن</string>
+ <string name="btn_copy_encrypted_signed">رمزگذاری و کپی متن</string>
+ <string name="btn_create_key">ساخت کلید</string>
+ <string name="btn_add_files">اضافه کردن فایل(ها)</string>
+ <string name="btn_share_decrypted_text">اشتراک گذاری متن رمزگشایی شده</string>
+ <string name="btn_copy_decrypted_text">کپی متن رمزگشایی شده</string>
+ <string name="btn_decrypt_clipboard">خواندن از متن کپی‌شده</string>
+ <string name="btn_decrypt_files">انتخاب فایل ورودی</string>
+ <string name="btn_encrypt_files">رمزگذاری فایلها</string>
+ <string name="btn_encrypt_text">رمزگذاری فایل</string>
+ <string name="btn_add_email">اضافه کردن ایمیلی دیگر</string>
+ <string name="btn_unlock">بازکردن قفل</string>
+ <string name="btn_add_keyserver">اضافه‌کردن</string>
+ <string name="btn_save_default">ذخیره به عنوان پیش‌فرض</string>
+ <string name="btn_saved">ذخیره شد!</string>
+ <!--menu-->
+ <string name="menu_preferences">تنظیمات</string>
+ <string name="menu_help">کمک</string>
+ <string name="menu_export_key">پشتیبان‌گیری به فایل</string>
+ <string name="menu_delete_key">حذف کلید</string>
+ <string name="menu_manage_keys">مدیریت کلیدهای من</string>
+ <string name="menu_search">جستجو</string>
+ <string name="menu_nfc_preferences">تنظیمات NFC</string>
+ <string name="menu_beam_preferences">تنظیمات Beam</string>
+ <string name="menu_encrypt_to">رمزگذاری به...</string>
+ <string name="menu_select_all">انتخاب همه</string>
+ <string name="menu_export_all_keys">خروج همهٔ کلیدها</string>
+ <string name="menu_update_all_keys">آپدیت همهٔ کلیدها</string>
+ <string name="menu_advanced">اطلاعات بیشتر</string>
+ <string name="menu_certify_fingerprint">تأیید با مقایسهٔ اثر انگشت</string>
+ <string name="menu_export_log">خروج لاگ</string>
+ <string name="menu_keyserver_add">اضافه‌کردن</string>
+ <!--label-->
+ <string name="label_message">متن</string>
+ <string name="label_file">فایل</string>
+ <string name="label_files">فایل(ها)</string>
+ <string name="label_file_colon">فایل</string>
+ <string name="label_no_passphrase">بدون رمزعبور</string>
+ <string name="label_passphrase">رمزعبور</string>
+ <string name="label_unlock">در حال بازکردن...</string>
+ <string name="label_passphrase_again">تکرار رمزعبور</string>
+ <string name="label_show_passphrase">نمایش رمزعبور</string>
+ <string name="label_algorithm">الگوریتم</string>
+ <string name="label_ascii_armor">فایل ASCII Armor</string>
+ <string name="label_file_ascii_armor">فعال‌کردن ASCII Armor</string>
+ <string name="label_write_version_header">به دیگران اطلاع دهید که شما از OpenKeyChain استفاده می‌کنید</string>
+ <string name="label_write_version_header_summary">عبارت \'OpenKeychain v2.7\' را در امضاها، متن رمزگذاری‌شده و کلیدها می‌نویسید</string>
+ <string name="label_use_default_yubikey_pin">از رمزِ PIN پیش‌فرضِ کلید Yubi استفاده کن</string>
+ <string name="label_use_num_keypad_for_yubikey_pin">از صفحه‌کلیدِ شماره‌ای برای وارد کردن رمز کلیدِ Yubi استفاده کن</string>
+ <string name="label_asymmetric_from">امضاء با:</string>
+ <string name="label_to">رمزگذاری به:</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_symmetric">رمزگذاری با رمز عبور</string>
+ <string name="label_passphrase_cache_ttl">زمان را به یاد داشته باش</string>
+ <string name="label_passphrase_cache_subs">رمز‌عبورها را با زیرکلیدها به یاد داشته باش</string>
+ <string name="label_message_compression">فشرده‌کردن متن</string>
+ <string name="label_file_compression">فشرده‌کردن فایل</string>
+ <string name="label_keyservers">سرور کلید OpenPGP را انتخاب کنید</string>
+ <string name="label_key_id">هویت کلید</string>
+ <string name="label_key_created">کلید ساخته شد %s</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">هویت اصلی</string>
+ <string name="label_name">نام</string>
+ <string name="label_comment">توضیحات</string>
+ <string name="label_email">ایمیل</string>
+ <string name="label_send_key">همگام‌سازی با اینترنت</string>
+ <string name="label_fingerprint">اثر انگشت</string>
+ <string name="expiry_date_dialog_title">تعیین تاریخ انقضأ</string>
+ <string name="label_keyservers_title">سرورِ کلیدها</string>
+ <string name="label_keyserver_settings_hint">برای ویرایش/حذف تَپ کنید و برای تغییر ترتیب بکشید</string>
+ <string name="label_selected_keyserver_title">سرور کلیدِ انتخاب شده</string>
+ <string name="label_preferred">ترجیح داده‌شده</string>
+ <string name="label_enable_compression">فشرده‌کردن</string>
+ <string name="label_encrypt_filenames">رمزگذاری اسمِ فایل‌ها</string>
+ <string name="label_hidden_recipients">مخفی‌کردن گیرنده‌ها</string>
+ <string name="label_verify_keyserver">بررسی سرورِ کلیدها</string>
+ <string name="label_enter_keyserver_url">آدرس URL سرورِ کلید را وارد کنید</string>
+ <string name="label_keyserver_dialog_delete">حذف سرورهای کلید</string>
+ <string name="label_theme">قالب</string>
+ <string name="pref_keyserver">سرورهای کلید OpenPGP</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <string name="pref_proxy_tor_title">فعال‌کردن Tor</string>
+ <string name="pref_proxy_tor_summary">برنامهٔ Orbot باید نصب شده باشد</string>
+ <string name="pref_proxy_normal_title">فعال کردن پراکسی دیگر</string>
+ <!--proxy type choices and values-->
+ <!--OrbotHelper strings-->
+ <!--InstallDialogFragment strings-->
+ <!--StartOrbotDialogFragment strings-->
+ <!--choice-->
+ <!--key flags-->
+ <string name="flag_sign">امضاء کردن</string>
+ <string name="flag_encrypt">رمزگذاری</string>
+ <string name="flag_authenticate">تصدیق کردن</string>
+ <!--sentences-->
+ <string name="wrong_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="pin_for">وارد کردن رمزِ پین برای \'%s\'</string>
+ <string name="keys_exported">%d کلید با موفقیت خارج شد.</string>
+ <string name="list_empty">این لیست خالی است!</string>
+ <string name="key_copied_to_clipboard">کلید در حافظه کپی شده است!</string>
+ <string name="fingerprint_copied_to_clipboard">اثر انگشت در حافظه کپی شده است!</string>
+ <string name="select_key_to_certify">لطفاً یک کلید برای تأیید کردن انتخاب کنید!</string>
+ <string name="key_too_big_for_sharing">کلید خیلی بزرگ است و نمی‌توان آن را از این طریق به اشتراک گذاشت!</string>
+ <string name="text_copied_to_clipboard">متن در حافظه کپی شده است!</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">حذف نشد. به صورت دستی آن را حذف کنید!</string>
+ <string name="error_file_added_already">%s قبلاً اضافه شده است.</string>
+ <string name="error_file_not_found">فایل یافت نشد</string>
+ <string name="error_no_secret_key_found">کلید شخصیِ متناسبی یافت نشد</string>
+ <string name="error_external_storage_not_ready">حافظهٔ خارجی آماده نیست</string>
+ <string name="error_key_size_minimum512bit">اندازهٔ کلید باید حداقل ۵۱۲بیت باشد</string>
+ <string name="error_unknown_algorithm_choice">الگوریتم انتخابی ناشناخته است</string>
+ <string name="error_user_id_no_email">آدرس ایمیلی یافت نشد</string>
+ <string name="error_key_needs_a_user_id">حداقل به یک هویت نیاز است</string>
+ <string name="error_no_signature_passphrase">رمزعبوری داده نشد</string>
+ <string name="error_no_signature_key">کلیدی برای امضاء کردن داده نشد</string>
+ <string name="error_wrong_passphrase">رمزعبور اشتباه است</string>
+ <string name="error_could_not_extract_private_key">نمی‌توان کلید شخصی را خارج کرد</string>
+ <!--errors without preceeding Error:-->
+ <!--results shown after decryption/verification-->
+ <string name="decrypt_result_no_signature">امضاء نشده</string>
+ <string name="decrypt_result_invalid_signature">امضاء معتبر نیست!</string>
+ <string name="decrypt_result_insecure_cryptography">امضاء معتبر نیست(رمزنگاری امن نیست)!</string>
+ <string name="decrypt_result_signature_uncertified">توسط کلیدی <b>تأیید نشده</b> امضاء شده است</string>
+ <string name="decrypt_result_signature_secret">با کلیدتان امضاء شده است</string>
+ <string name="decrypt_result_signature_certified">با کلیدی تأیید شده امضاء شده است</string>
+ <string name="decrypt_result_signature_expired_key">با کلیدی <b>منقضی شده</b> امضاء شده است!</string>
+ <string name="decrypt_result_encrypted">رمزگذاری شده</string>
+ <string name="decrypt_result_not_encrypted">رمزگذاری نشده</string>
+ <string name="decrypt_result_insecure">رمزگذاریِ نا امن</string>
+ <string name="decrypt_result_action_show">نمایش</string>
+ <string name="decrypt_result_action_Lookup">جستجو</string>
+ <string name="decrypt_invalid_text">امضاء نامعتبر است یا کلید فسخ شده‌است. شما نمی‌توانید مطمئن باشید که چه کسی متن را نوشته است. آیا متن نمایش داده شود؟</string>
+ <string name="decrypt_invalid_button">من متوجهٔ ریسک‌اش هستم. نمایش‌ بده!</string>
+ <!--Add keys-->
+ <string name="add_keys_my_key">کلید من:</string>
+ <!--progress dialogs, usually ending in '…'-->
+ <string name="progress_done">انجام شد.</string>
+ <string name="progress_cancel">انصراف</string>
+ <string name="progress_cancelling">در حال انصراف...</string>
+ <string name="progress_saving">در حال ذخیره‌سازی...</string>
+ <string name="progress_importing">در حال وارد کردن...</string>
+ <string name="progress_revoking_uploading">در حال فسخ‌ و آپلود‌کردن کلید...</string>
+ <string name="progress_updating">درحال آپدیت‌کردن کلیدها...</string>
+ <string name="progress_exporting">در حال خارج‌کردن...</string>
+ <string name="progress_uploading">در حال آپلود کردن...</string>
+ <string name="progress_building_key">در حال ساخت کلید...</string>
+ <string name="progress_building_master_key">در حال ساخت حلقهٔ اصلی...</string>
+ <string name="progress_generating_rsa">در حال تولید کلید RSA جدید...</string>
+ <string name="progress_generating_dsa">در حال تولید کلید DSA جدید...</string>
+ <string name="progress_generating_elgamal">در حال تولید کلید EIGamal جدید...</string>
+ <string name="progress_generating_ecdsa">در حال تولید کلید ECDSA جدید...</string>
+ <string name="progress_generating_ecdh">در حال تولید کلید ECDH جدید...</string>
+ <string name="progress_modify">در حال ویرایش حلقه کلید...</string>
+ <string name="progress_modify_unlock">در حال بازکردن حلقه کلید...</string>
+ <!--action strings-->
+ <!--key bit length selections-->
+ <!--elliptic curve names-->
+ <!--not in for now, see SaveKeyringParcel
+ <string name="key_curve_bp_p256">"Brainpool P-256"</string>
+ <string name="key_curve_bp_p384">"Brainpool P-384"</string>
+ <string name="key_curve_bp_p512">"Brainpool P-512"</string>-->
+ <!--compression-->
+ <!--Help-->
+ <!--Import-->
+ <!--Import from URL-->
+ <!--Generic result toast-->
+ <!--Import result toast-->
+ <!--Delete result toast-->
+ <!--Revoke result toast (snackbar)-->
+ <!--Certify result toast-->
+ <!--Intent labels-->
+ <!--Remote API-->
+ <!--Share-->
+ <!--retry upload dialog-->
+ <!--Delete or revoke private key dialog-->
+ <!--Delete Or Revoke Dialog spinner-->
+ <!--Key list-->
+ <!--Key view-->
+ <string name="user_id_info_invalid_title">نامعتبر</string>
+ <string name="user_id_info_invalid_text">هویت مشکل دارد!</string>
+ <!--Key trust-->
+ <string name="key_trust_already_verified">شما قبلاً این کلید را تأیید کرده‌اید!</string>
+ <string name="key_trust_it_is_yours">این یکی از کلیدهای‌تان است!</string>
+ <string name="key_trust_maybe">این کلید نه لغو شده و نه منقضی شده است.\nشما آن را تأیید نکرده‌اید، ولی می‌توانید اعتماد به آن کلید را انتخاب کنید.</string>
+ <string name="key_trust_revoked">این کلید توسط صاحب‌اش لغو شده است. نباید به آن اعتماد کنید.</string>
+ <string name="key_trust_expired">این کلید منقضی شده است. نباید به آن اعتماد کنید.</string>
+ <string name="key_trust_old_keys">اگر از این برای رمزگشاییِ یک پیغام قدیمی مربوط به قبل از منقضی شدن‌اش استفاده می‌کنید، مشکلی نیست.</string>
+ <string name="key_trust_no_cloud_evidence">هیچ دلیلی برای اعتماد به این کلید بر روی اینترنت نیست.</string>
+ <string name="key_trust_start_cloud_search">آغاز جستجو</string>
+ <!--keybase proof stuff-->
+ <string name="keybase_narrative_twitter">روی Twitter پست می‌کند به عنوان %s</string>
+ <string name="keybase_narrative_github">بر روی GitHub شناخته شده است به عنوان %s</string>
+ <string name="keybase_narrative_dns">کنترل می‌کند نام دامنه‌(های) %s</string>
+ <!--Edit key-->
+ <string name="edit_key_action_change_passphrase">تغییر رمزعبور</string>
+ <string name="edit_key_action_add_identity">اضافه‌کردن هویت</string>
+ <string name="edit_key_action_add_subkey">اضافه کردن زیرکلید</string>
+ <string name="edit_key_edit_user_id_title">یک عمل را انتخاب کنید!</string>
+ <string name="edit_key_edit_user_id_revoked">این هویت لغو شده‌است. انصراف از این عمل شدنی نیست.</string>
+ <string name="edit_key_edit_subkey_title">یک عمل را انتخاب کنید!</string>
+ <string name="edit_key_new_subkey">زیرکلیدِ جدید</string>
+ <string name="edit_key_select_flag">لطفاً حداقل یک پرچم انتخاب کنید!</string>
+ <string name="edit_key_error_add_identity">حداقل یک هویت اصافه کنید!</string>
+ <string name="edit_key_error_add_subkey">حداقل یک زیر‌کلید اضافه کنید</string>
+ <!--Create key-->
+ <string name="create_key_empty">این فیلد لازم است</string>
+ <string name="create_key_passphrases_not_equal">رمز عبور‌ها مطابقت ندارند</string>
+ <string name="create_key_final_text">شما هویتِ زیر را وارد کردید:</string>
+ <string name="create_key_rsa">(3 زیرکلید, RSA, 4096 بیت)</string>
+ <string name="create_key_custom">(تنظیمات شخصیِ کلید)</string>
+ <!--View key-->
+ <!--Add/Edit keyserver-->
+ <!--Navigation Drawer-->
+ <!--hints-->
+ <!--certs-->
+ <!--LogType log messages. Errors should have _ERROR_ in their name and end with a !-->
+ <!--Import Public log entries-->
+ <!--Import Secret log entries-->
+ <!--Keyring Canonicalization log entries-->
+ <!--Keyring merging log entries-->
+ <!--createSecretKeyRing-->
+ <!--modifySecretKeyRing-->
+ <!--Consolidate-->
+ <string name="msg_con_reimport_secret_skip">هیچ کلید خصوصی‌ای برای ورود یافت نشد، نادیده گرفته شد...</string>
+ <string name="msg_con_warn_delete_public">استثنا در حذف کردن فایل عمومیِ cache</string>
+ <string name="msg_con_warn_delete_secret">استثنا در حذف کردن فایل خصوصیِ cache</string>
+ <!--Edit Key (higher level than modify)-->
+ <string name="msg_ed">در حال انجام عملیات کلید</string>
+ <string name="msg_ed_caching_new">در حال کَش کردنِ رمزعبور جدید</string>
+ <string name="msg_ed_error_no_parcel">SaveKeyringParcel یافت نشد! (این یک باگ است، لطفاً گزارش کنید)</string>
+ <string name="msg_ed_error_key_not_found">کلید یافت نشد!</string>
+ <string name="msg_ed_error_extract_public_upload">خطا در خارج کردنِ کلید عمومی برای آپلود!</string>
+ <string name="msg_ed_fetching">در حال واکشیِ کلید برای ویرایش (%s)</string>
+ <string name="msg_ed_success">عملیات کلید با موفقیت انجام شد</string>
+ <!--Promote key-->
+ <!--Other messages used in OperationLogs-->
+ <!--Messages for DecryptVerify operation-->
+ <!--Messages for VerifySignedLiteralData operation-->
+ <!--Messages for SignEncrypt operation-->
+ <!--Messages for PgpSignEncrypt operation-->
+ <!--Messages for Keybase Verification operation-->
+ <!--Messages for Export Log operation-->
+ <!--PassphraseCache-->
+ <!--Keyserver sync-->
+ <!--First Time-->
+ <!--unsorted-->
+ <!--Android Account-->
+ <!--Passphrase wizard-->
+ <!--TODO: rename all the things!-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
+ <!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
+</resources>
diff --git a/OpenKeychain/src/main/res/values-fi/strings.xml b/OpenKeychain/src/main/res/values-fi/strings.xml
index 6cbc2b1fc..a8bbd7f79 100644
--- a/OpenKeychain/src/main/res/values-fi/strings.xml
+++ b/OpenKeychain/src/main/res/values-fi/strings.xml
@@ -4,32 +4,26 @@
http://developer.android.com/guide/topics/resources/string-resource.html (scroll down to "Escaping apostrophes and quotes").-->
<!--title-->
<string name="title_decrypt">Pura Salaus</string>
- <string name="title_unlock">Avaa Avain</string>
<string name="title_add_subkey">Lisää aliavain</string>
<string name="title_edit_key">Muokkaa avainta</string>
<string name="title_preferences">Asetukset</string>
<string name="title_api_registered_apps">Sovellukset</string>
- <string name="title_change_passphrase">Vaihda Salasana</string>
<string name="title_share_fingerprint_with">Jaa sormenjälki...</string>
<string name="title_share_key">Jaa avain...</string>
<string name="title_share_file">Jaa tiedosto...</string>
<string name="title_encrypt_to_file">Salaa Tiedostoon</string>
<string name="title_decrypt_to_file">Pura Tiedostoon</string>
<string name="title_import_keys">Tuo Avaimia</string>
- <string name="title_export_key">Vie Avain</string>
- <string name="title_export_keys">Vie Avaimia</string>
<string name="title_key_not_found">Avainta Ei Löydy</string>
<string name="title_send_key">Lähetä Avainpalvelimelle</string>
<string name="title_key_details">Avaimen Tiedot</string>
<string name="title_help">Apua</string>
<string name="title_log_display">Loki</string>
<string name="title_exchange_keys">Vaihda Avaimia</string>
- <string name="title_advanced_key_info">Lisätietoa Avaimesta</string>
<!--section-->
<string name="section_user_ids">Identiteetit</string>
<string name="section_keys">Aliavaimet</string>
<string name="section_cloud_search">Pilvihaku</string>
- <string name="section_passphrase_cache">Salasanavälimuisti</string>
<string name="section_actions">Toiminteet</string>
<string name="section_share_key">Avain</string>
<string name="section_key_server">Avainpalvelin</string>
@@ -37,7 +31,6 @@
<!--button-->
<string name="btn_decrypt_verify_file">Pura, todenna ja tallenna tiedosto</string>
<string name="btn_encrypt_share_file">Salaa ja jaa tiedosto</string>
- <string name="btn_save">Tallenna</string>
<string name="btn_do_not_save">Peruuta</string>
<string name="btn_delete">Poista</string>
<string name="btn_no_date">Ei umpeutumisaikaa</string>
@@ -48,28 +41,20 @@
<string name="btn_view_cert_key">Näytä varmennusavain</string>
<string name="btn_create_key">Luo avain</string>
<string name="btn_add_files">Lisää tiedosto(ja)</string>
- <string name="btn_add_share_decrypted_text">Jaa purettu teksti</string>
- <string name="btn_decrypt_and_verify">ja varmenna allekirjoitukset</string>
- <string name="btn_decrypt_files">Pura tiedostoja</string>
<!--menu-->
<string name="menu_preferences">Asetukset</string>
<string name="menu_help">Apua</string>
- <string name="menu_export_key">Vie tiedostoon</string>
<string name="menu_delete_key">Poista avain</string>
<string name="menu_search">Etsi</string>
<string name="menu_beam_preferences">Beam asetukset</string>
<string name="menu_encrypt_to">Salaa...</string>
<string name="menu_select_all">Valitse kaikki</string>
<string name="menu_export_all_keys">Vie kaikki avaimet</string>
- <string name="menu_advanced">Näytä lisäinformaatio</string>
<!--label-->
<string name="label_file">Tiedosto</string>
<string name="label_files">Tiedosto(t)</string>
<string name="label_file_colon">Tiedosto:</string>
- <string name="label_no_passphrase">Ei salasanaa</string>
- <string name="label_passphrase">Salasana</string>
<string name="label_unlock">Avataan...</string>
- <string name="label_passphrase_again">Toista salasana</string>
<string name="label_algorithm">Algoritmi</string>
<string name="label_ascii_armor">Tiedosto ASCII Armor</string>
<string name="label_file_ascii_armor">Käytä ASCII Armoria</string>
@@ -78,16 +63,11 @@
<string name="label_use_default_yubikey_pin">Käytä vakiota YubiKey PIN:iä</string>
<string name="label_use_num_keypad_for_yubikey_pin">Käytä numeerista näppäimistöä YuniKey PIN:iin</string>
<string name="label_label_use_default_yubikey_pin_summary">Käyttää vakio-PIN:iä (123456) käyttääkseen YubiKeyssejä NFC kautta</string>
- <string name="label_asymmetric_from">Allekirjoittaja:</string>
<string name="label_to">Salaa:</string>
<string name="label_delete_after_decryption">Poista salauksen purkamisen jälkeen</string>
<string name="label_encryption_algorithm">Salausalgoritmi</string>
<string name="label_hash_algorithm">Tiivistealgoritmi</string>
- <string name="label_symmetric">Salaa salasanalla</string>
- <string name="label_passphrase_cache_ttl">Välimuistiaika</string>
- <string name="label_passphrase_cache_subs">Väliaikaistalleta salasanat aliavaimen mukaan</string>
<string name="label_file_compression">Tiedoston pakkaus</string>
- <string name="label_keyservers">Avainpalvelimet</string>
<string name="label_key_id">Avaimen ID</string>
<string name="label_creation">Luontiaika</string>
<string name="label_expiry">Umpeutumisaika</string>
@@ -101,8 +81,13 @@
<string name="label_send_key">Synkronoi pilveen</string>
<string name="label_fingerprint">Sormenjälki</string>
<string name="expiry_date_dialog_title">Aseta umpeutumispäivämäärä</string>
- <string name="label_first_keyserver_is_used">(Ensimmäinen avainpalvelin listalla on ensisijainen)</string>
<string name="label_preferred">ensisijainen</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <!--proxy type choices and values-->
+ <!--OrbotHelper strings-->
+ <!--InstallDialogFragment strings-->
+ <!--StartOrbotDialogFragment strings-->
<string name="user_id_no_name">&lt;ei nimeä&gt;</string>
<string name="none">&lt;ei mitään&gt;</string>
<plurals name="n_keys">
@@ -142,12 +127,8 @@
<string name="flag_encrypt">Salaa</string>
<string name="flag_authenticate">Autentikoi</string>
<!--sentences-->
- <string name="wrong_passphrase">Väärä salasana.</string>
<string name="no_filemanager_installed">Yhteensopivaa tiedostonhallintaa ei ole asennettu.</string>
- <string name="passphrases_do_not_match">Salasanat eivät vastaa toisiaan.</string>
- <string name="passphrase_must_not_be_empty">Syötä salasana.</string>
<string name="passphrase_for_symmetric_encryption">Symmetrinen salaus.</string>
- <string name="passphrase_for">Syötä salasana \'%s\':lle</string>
<string name="pin_for">Syötä PIN \'%s\':lle</string>
<!--errors
no punctuation, all lowercase,
@@ -166,13 +147,18 @@
<!--compression-->
<!--Help-->
<!--Import-->
+ <!--Import from URL-->
<!--Generic result toast-->
<!--Import result toast-->
<!--Delete result toast-->
+ <!--Revoke result toast (snackbar)-->
<!--Certify result toast-->
<!--Intent labels-->
<!--Remote API-->
<!--Share-->
+ <!--retry upload dialog-->
+ <!--Delete or revoke private key dialog-->
+ <!--Delete Or Revoke Dialog spinner-->
<!--Key list-->
<!--Key view-->
<!--Key trust-->
@@ -180,6 +166,7 @@
<!--Edit key-->
<!--Create key-->
<!--View key-->
+ <!--Add/Edit keyserver-->
<!--Navigation Drawer-->
<!--hints-->
<!--certs-->
@@ -198,12 +185,15 @@
<!--Messages for VerifySignedLiteralData operation-->
<!--Messages for SignEncrypt operation-->
<!--Messages for PgpSignEncrypt operation-->
+ <!--Messages for Keybase Verification operation-->
<!--Messages for Export Log operation-->
<!--PassphraseCache-->
+ <!--Keyserver sync-->
<!--First Time-->
<!--unsorted-->
+ <!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
</resources>
diff --git a/OpenKeychain/src/main/res/values-fr/strings.xml b/OpenKeychain/src/main/res/values-fr/strings.xml
index f34dd8e46..ff50c15ce 100644
--- a/OpenKeychain/src/main/res/values-fr/strings.xml
+++ b/OpenKeychain/src/main/res/values-fr/strings.xml
@@ -7,13 +7,12 @@
<string name="title_encrypt_text">Chiffrer</string>
<string name="title_encrypt_files">Chiffrer</string>
<string name="title_decrypt">Déchiffrer</string>
- <string name="title_unlock">Déverrouiller la clef</string>
<string name="title_add_subkey">Ajouter une sous-clef</string>
<string name="title_edit_key">Modifier une clef</string>
<string name="title_preferences">Paramètres</string>
<string name="title_api_registered_apps">Applis</string>
- <string name="title_key_server_preference">Serveurs de clefs</string>
- <string name="title_change_passphrase">Modifier la phrase de passe</string>
+ <string name="title_key_server_preference">Serveurs de clefs OpenPGP</string>
+ <string name="title_change_passphrase">Changer le mot de passe</string>
<string name="title_share_fingerprint_with">Partager l\'empreinte avec...</string>
<string name="title_share_key">Partager la clef avec...</string>
<string name="title_share_file">Partager le fichier avec...</string>
@@ -21,8 +20,8 @@
<string name="title_encrypt_to_file">Chiffrer vers un fichier</string>
<string name="title_decrypt_to_file">Déchiffrer vers un fichier</string>
<string name="title_import_keys">importer des clefs</string>
- <string name="title_export_key">Exporter la clef</string>
- <string name="title_export_keys">Exporter les clefs</string>
+ <string name="title_export_key">Sauvegarder la clef</string>
+ <string name="title_export_keys">Sauvegarder les clefs</string>
<string name="title_key_not_found">Clef introuvable</string>
<string name="title_send_key">Téléverser vers le serveur de clefs</string>
<string name="title_certify_key">Confirmer la clef</string>
@@ -30,33 +29,39 @@
<string name="title_help">Aide</string>
<string name="title_log_display">Journal</string>
<string name="title_exchange_keys">Échanger des clefs</string>
- <string name="title_advanced_key_info">Infos avancées sur les clefs</string>
+ <string name="title_advanced_key_info">Informations détaillées</string>
<string name="title_delete_secret_key">Supprimer VOTRE clef « %s » ?</string>
<string name="title_export_log">Exporter le journal</string>
<string name="title_manage_my_keys">Gérer mes clefs</string>
<!--section-->
<string name="section_user_ids">identités</string>
+ <string name="section_yubikey">ClefYubi</string>
<string name="section_linked_system_contact">Contact système relié</string>
<string name="section_should_you_trust">Devriez-vous faire confiance à cette clef ?</string>
<string name="section_proof_details">Vérification de preuve</string>
<string name="section_cloud_evidence">Preuves provenant du nuage</string>
<string name="section_keys">Sous-clefs</string>
<string name="section_cloud_search">Recherche nuagique</string>
- <string name="section_passphrase_cache">Cache de la phrase de passe</string>
+ <string name="section_passphrase_cache">Gestion des mots de passe/NIP</string>
+ <string name="section_proxy_settings">Paramètres du mandataire</string>
+ <string name="section_gui">Interface</string>
+ <string name="section_sync_settings">Paramètres de synchro</string>
<string name="section_certify">Confirmer</string>
<string name="section_actions">Actions</string>
<string name="section_share_key">Clef</string>
<string name="section_key_server">Serveur de clefs</string>
<string name="section_fingerprint">Empreinte</string>
<string name="section_encrypt">Chiffrer</string>
- <string name="section_decrypt">Déchiffrer</string>
+ <string name="section_decrypt">Déchiffrer / Vérifier</string>
<string name="section_current_expiry">Expiration actuelle</string>
<string name="section_new_expiry">Nouvelle expiration</string>
<!--button-->
<string name="btn_decrypt_verify_file">Déchiffrer, vérifier et enregistrer le fichier</string>
<string name="btn_encrypt_share_file">Chiffrer et partager le fichier</string>
<string name="btn_encrypt_save_file">Chiffrer et enregistrer un fichier</string>
+ <string name="btn_save_file">Enregistrer le fichier</string>
<string name="btn_save">Enregistrer</string>
+ <string name="btn_view_log">Visualiser le journal</string>
<string name="btn_do_not_save">Annuler</string>
<string name="btn_delete">Supprimer</string>
<string name="btn_no_date">Pas d\'expiration</string>
@@ -71,17 +76,21 @@
<string name="btn_view_cert_key">Voir la clef de certification</string>
<string name="btn_create_key">Créer la clef</string>
<string name="btn_add_files">Ajouter un/des fichier(s)</string>
- <string name="btn_add_share_decrypted_text">Partager le texte déchiffrer</string>
- <string name="btn_decrypt_clipboard">Déchiffrer du texte à partir du presse-papiers</string>
- <string name="btn_decrypt_and_verify">et vérifier les signatures</string>
- <string name="btn_decrypt_files">Déchiffrer les fichiers</string>
+ <string name="btn_share_decrypted_text">Partager le texte déchiffré</string>
+ <string name="btn_copy_decrypted_text">Copier le texte déchiffré</string>
+ <string name="btn_decrypt_clipboard">Lire du presse-papiers</string>
+ <string name="btn_decrypt_files">Choisir le fichier d\'entrée</string>
<string name="btn_encrypt_files">Chiffrer des fichiers</string>
<string name="btn_encrypt_text">Chiffrer du texte</string>
<string name="btn_add_email">Ajouter une adresse courriel supplémentaire</string>
+ <string name="btn_unlock">Déverrouiller</string>
+ <string name="btn_add_keyserver">Ajouter</string>
+ <string name="btn_save_default">Enregistrer comme valeur par défaut</string>
+ <string name="btn_saved">Enregistré !</string>
<!--menu-->
<string name="menu_preferences">Paramètres</string>
<string name="menu_help">Aide</string>
- <string name="menu_export_key">Exporter vers un fichier</string>
+ <string name="menu_export_key">Sauvegarder vers un fichier</string>
<string name="menu_delete_key">Supprimer la clef</string>
<string name="menu_manage_keys">Gérer mes clefs</string>
<string name="menu_search">Rechercher</string>
@@ -91,40 +100,42 @@
<string name="menu_select_all">Tout sélectionner</string>
<string name="menu_export_all_keys">Exporter toutes les clefs</string>
<string name="menu_update_all_keys">Mettre toutes les clefs à jour</string>
- <string name="menu_advanced">Afficher les infos avancées</string>
+ <string name="menu_advanced">Informations détaillées</string>
<string name="menu_certify_fingerprint">Confirmer par une comparaison d\'empreinte</string>
<string name="menu_export_log">Exporter le journal</string>
+ <string name="menu_keyserver_add">Ajouter</string>
<!--label-->
<string name="label_message">Texte</string>
<string name="label_file">Fichier</string>
<string name="label_files">Fichier(s)</string>
<string name="label_file_colon">Fichier :</string>
- <string name="label_no_passphrase">Aucune phrase de passe</string>
- <string name="label_passphrase">Phrase de passe</string>
+ <string name="label_no_passphrase">Aucun mot de passe</string>
+ <string name="label_passphrase">Mot de passe</string>
<string name="label_unlock">Déverrouillage...</string>
- <string name="label_passphrase_again">Répéter la phrase de passe</string>
- <string name="label_show_passphrase">Montrer la phrase de passe</string>
+ <string name="label_passphrase_again">Répéter le mot de passe</string>
+ <string name="label_show_passphrase">Montrer le mot de passe</string>
<string name="label_algorithm">Algorithme</string>
<string name="label_ascii_armor">Fichier ASCII Armor</string>
- <string name="label_file_ascii_armor">Activer l\'Armure ASCII</string>
+ <string name="label_file_ascii_armor">Activer l\'armure ASCII</string>
<string name="label_write_version_header">Faire savoir aux autres que vous utilisez OpenKeychain</string>
<string name="label_write_version_header_summary">Ajoute « OpenKeychain v2.7 » aux signatures OpenPGP, aux cryptogrammes et aux clefs exportées</string>
- <string name="label_use_default_yubikey_pin">Utiliser le NIP par défaut de la Yubikey</string>
- <string name="label_use_num_keypad_for_yubikey_pin">Utiliser le pavé numérique pour le NIP Yubikey</string>
- <string name="label_label_use_default_yubikey_pin_summary">Utilise le NIP par défaut (123456) pour accéder aux Yubikeys par la NFC</string>
- <string name="label_asymmetric_from">Signé par :</string>
+ <string name="label_use_default_yubikey_pin">Utiliser le NIP par défaut de la ClefYubi</string>
+ <string name="label_use_num_keypad_for_yubikey_pin">Utiliser le pavé numérique pour le NIP de la ClefYubi</string>
+ <string name="label_label_use_default_yubikey_pin_summary">Utilise le NIP par défaut (123456) pour accéder aux ClefsYubi par la NFC</string>
+ <string name="label_asymmetric_from">Signer avec :</string>
<string name="label_to">Chiffrer pour :</string>
<string name="label_delete_after_encryption">Supprimer les fichiers après chiffrement</string>
<string name="label_delete_after_decryption">Supprimer le fichier après le déchiffrement</string>
<string name="label_encryption_algorithm">Algorithme de chiffrement</string>
<string name="label_hash_algorithm">Algorithme de hachage</string>
- <string name="label_symmetric">Chiffrer avec une phrase de passe</string>
- <string name="label_passphrase_cache_ttl">Durée du cache</string>
- <string name="label_passphrase_cache_subs">Mettre en cache une phrase de passe par sous-clef</string>
+ <string name="label_symmetric">Chiffrer avec un mot de passe</string>
+ <string name="label_passphrase_cache_ttl">Délai de mémorisation</string>
+ <string name="label_passphrase_cache_subs">Mémoriser les mots de passe par sous-clefs</string>
<string name="label_message_compression">Compression de texte</string>
<string name="label_file_compression">Compression des fichiers</string>
- <string name="label_keyservers">Serveurs de clefs</string>
+ <string name="label_keyservers">Choisir les serveurs de clefs OpenPGP</string>
<string name="label_key_id">ID de clef</string>
+ <string name="label_key_created">Clef créée %s</string>
<string name="label_creation">Création</string>
<string name="label_expiry">Expiration</string>
<string name="label_usage">Utilisation</string>
@@ -137,15 +148,56 @@
<string name="label_send_key">Synchroniser avec le nuage</string>
<string name="label_fingerprint">Empreinte</string>
<string name="expiry_date_dialog_title">Définir une date d\'expiration</string>
- <string name="label_first_keyserver_is_used">(Le premier serveur de clefs listé est préféré)</string>
+ <string name="label_keyservers_title">Serveurs de clefs</string>
+ <string name="label_keyserver_settings_hint">Glisser pour changer l\'ordre, toquer pour éditer/supprimer</string>
+ <string name="label_selected_keyserver_title">Serveurs de clefs sélectionnés</string>
<string name="label_preferred">préféré</string>
<string name="label_enable_compression">Activer la compression</string>
<string name="label_encrypt_filenames">Chiffrer les nom de fichier</string>
<string name="label_hidden_recipients">Cacher les destinataires</string>
- <string name="pref_keyserver">Rechercher dans le serveur de clef</string>
- <string name="pref_keyserver_summary">Rechercher dans le serveur de clef HKP</string>
- <string name="pref_keybase">Rechercher dans Keybase.io</string>
- <string name="pref_keybase_summary">Rechercher dans l\'index de Keybase.io</string>
+ <string name="label_verify_keyserver">Vérifier le serveur de clefs</string>
+ <string name="label_enter_keyserver_url">Saisir l\'URL du serveur de clefs</string>
+ <string name="label_keyserver_dialog_delete">Supprimer le serveur de clefs</string>
+ <string name="label_theme">Thème</string>
+ <string name="pref_keyserver">Serveurs de clefs OpenPGP</string>
+ <string name="pref_keyserver_summary">Rechercher les clefs dans les serveurs de clefs OpenPGP choisis (protocole HKP)</string>
+ <string name="pref_keybase">keybase.io</string>
+ <string name="pref_keybase_summary">Rechercher les clefs sur keybase.io</string>
+ <string name="label_sync_settings_keyserver_title">Mettre les clefs à jour automatiquement</string>
+ <string name="label_sync_settings_keyserver_summary_on">Les clefs de plus d\'une semaine sont misent à jour à partir du serveur de clefs</string>
+ <string name="label_sync_settings_keyserver_summary_off">Les clefs ne sont pas mises à jour automatiquement</string>
+ <string name="label_sync_settings_contacts_title">Synchroniser les contacts avec les clefs</string>
+ <string name="label_sync_settings_contacts_summary_on">Les clefs sont reliées aux contacts dont les adresses courriels correspondantes, ce qui se passe complètement hors ligne</string>
+ <string name="label_sync_settings_contacts_summary_off">Les nouvelles clefs ne seront pas reliées aux contacts</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <string name="keyserver_sync_settings_title">Mettre les clefs à jour automatiquement</string>
+ <!--Proxy Preferences-->
+ <string name="pref_proxy_tor_title">Activer Tor</string>
+ <string name="pref_proxy_tor_summary">Orbot doit être installé</string>
+ <string name="pref_proxy_normal_title">Activer un autre mandataire</string>
+ <string name="pref_proxy_host_title">Hôte du mandataire</string>
+ <string name="pref_proxy_host_err_invalid">L\'hôte du mandataire ne peut pas être vide</string>
+ <string name="pref_proxy_port_title">Port du mandataire</string>
+ <string name="pref_proxy_port_err_invalid">Le numéro de port saisi est invalide</string>
+ <string name="pref_proxy_type_title">Type de mandataire</string>
+ <!--proxy type choices and values-->
+ <string name="pref_proxy_type_choice_http">HTTP</string>
+ <string name="pref_proxy_type_choice_socks">SOCKS</string>
+ <!--OrbotHelper strings-->
+ <string name="orbot_ignore_tor">Ne pas utiliser Tor</string>
+ <!--InstallDialogFragment strings-->
+ <string name="orbot_install_dialog_title">Installer Orbot pour utiliser Tor ?</string>
+ <string name="orbot_install_dialog_install">Installer</string>
+ <string name="orbot_install_dialog_content">Orbot doit être installé et doit relayer le trafic. Voulez-vous l\'installer maintenant ?</string>
+ <string name="orbot_install_dialog_cancel">Annuler</string>
+ <string name="orbot_install_dialog_ignore_tor">Ne pas utiliser Tor</string>
+ <!--StartOrbotDialogFragment strings-->
+ <string name="orbot_start_dialog_title">Démarrer Orbot ?</string>
+ <string name="orbot_start_dialog_content">Orbot ne semble pas tourner. Voulez-vous le démarrer et vous connecter à Tor ?</string>
+ <string name="orbot_start_btn">Démarrer Orbot</string>
+ <string name="orbot_start_dialog_start">Démarrer Orbot</string>
+ <string name="orbot_start_dialog_cancel">Annuler</string>
+ <string name="orbot_start_dialog_ignore_tor">Ne pas utiliser Tor</string>
<string name="user_id_no_name">&lt;aucun nom&gt;</string>
<string name="none">&lt;aucune&gt;</string>
<plurals name="n_keys">
@@ -179,32 +231,40 @@
<string name="filemanager_title_open">Ouvrir...</string>
<string name="error">Erreur</string>
<string name="error_message">Erreur : %s</string>
+ <string name="theme_dark">Sombre</string>
+ <string name="theme_light">Clair</string>
<!--key flags-->
<string name="flag_certify">Certifier</string>
<string name="flag_sign">Signer</string>
<string name="flag_encrypt">Chiffrer</string>
<string name="flag_authenticate">Authentifier</string>
<!--sentences-->
- <string name="wrong_passphrase">Phrase de passe erronée</string>
+ <string name="wrong_passphrase">Mot de passe erroné.</string>
<string name="no_filemanager_installed">Aucun gestionnaire de fichiers compatible installé.</string>
- <string name="passphrases_do_not_match">Les phrases de passe ne correspondent pas.</string>
- <string name="passphrase_must_not_be_empty">Veuillez saisir une phrase de passe</string>
+ <string name="passphrases_do_not_match">Les mots de passe ne correspondent pas.</string>
+ <string name="passphrase_must_not_be_empty">Veuillez saisir un mot de passe.</string>
<string name="passphrase_for_symmetric_encryption">Chriffrement symétrique.</string>
- <string name="passphrase_for">Saisir une phrase de passe pour « %s »</string>
+ <string name="passphrase_for">Saisir le mot de passe pour « %s »</string>
<string name="pin_for">Saisir le NIP pour « %s »</string>
- <string name="yubikey_pin_for">Saisir le NIP pour accéder à la Yubikey pour « %s »</string>
- <string name="nfc_text">Tenez la YubiKey contre le dos de votre appareil.</string>
+ <string name="yubikey_pin_for">Saisir le NIP pour accéder à la ClefYubi pour « %s »</string>
+ <string name="nfc_text">Tenez la ClefYubi contre le logo NFC au dos de votre appareil.</string>
+ <string name="nfc_wait">Conservez la ClefYubi contre le dos !</string>
+ <string name="nfc_finished">Retirez la ClefYubi maintenant.</string>
+ <string name="nfc_try_again_text">Retirez la ClefYubi maintenant et appuyez sur RESSAYER.</string>
<string name="file_delete_confirmation_title">Supprimer les fichiers originaux ?</string>
<string name="file_delete_confirmation">Les fichiers suivants seront détruits : %s</string>
<string name="file_delete_successful">%1$d fichiers sur %2$d ont déjà été supprimés. %3$s</string>
- <string name="no_file_selected">Choisir d\'abord un fichier.</string>
+ <string name="no_file_selected">Aucun fichier choisi.</string>
<string name="encrypt_sign_successful">Signé et/ou chiffré avec succès.</string>
<string name="encrypt_sign_clipboard_successful">Signé et/ou chiffré vers le presse-papiers avec succès.</string>
<string name="select_encryption_key">Choisir au moins une clef de chiffrement.</string>
- <string name="select_encryption_or_signature_key">Choisir au moins une clef de chiffrement ou de signature.</string>
+ <string name="error_no_encryption_or_signature_key">Choisir au moins une clef de chiffrement ou une clef de signature.</string>
<string name="specify_file_to_encrypt_to">Veuillez spécifier vers quel fichier chiffrer.\nAVERTISSEMENT : le fichier sera écrasé s\'il existe !</string>
<string name="specify_file_to_decrypt_to">Veuillez spécifier vers quel fichier déchiffrer.\nAVERTISSEMENT : le fichier sera écrasé s\'il existe !</string>
- <string name="specify_file_to_export_to">Veuillez spécifier vers quel fichier exporter.\nAVERTISSEMENT : le fichier sera écrasé s\'il existe !</string>
+ <string name="specify_backup_dest">Une sauvegarde excluant vos clefs sera faite. Veuillez spécifier un fichier de destination.\nAVERTISSEMENT : le fichier sera écrasé s\'il existe !</string>
+ <string name="specify_backup_dest_single">Cette clef sera partagée. Veuillez spécifier un fichier de destination.\nAVERTISSEMENT : le fichier sera écrasé s\'il existe !</string>
+ <string name="specify_backup_dest_secret_single">Une sauvegarde complète de votre clef sera faite. Veuillez spécifier un fichier de destination.\nAVERTISSEMENT : le fichier sera écrasé s\'il existe !</string>
+ <string name="specify_backup_dest_secret">Une sauvegarde complète de toutes les clefs, la vôtre incluse, sera faite. Veuillez spécifier un fichier de destination.\nAVERTISSEMENT : le fichier sera écrasé s\'il existe !</string>
<string name="key_deletion_confirmation_multi">Voulez-vous vraiment supprimer toutes les clefs sélectionnées ?</string>
<string name="secret_key_deletion_confirmation">Après suppression vous ne pourrez plus lire les messages chiffrés avec cette clef et vous perdrez toutes les confirmations de clefs faites avec elle !</string>
<string name="public_key_deletetion_confirmation">Supprimer la clef \'%s\' ?</string>
@@ -237,34 +297,38 @@
<string name="error_external_storage_not_ready">le stockage externe n\'est pas prêt</string>
<string name="error_key_size_minimum512bit">la taille de la clef doit être d\'au moins 512 bits</string>
<string name="error_unknown_algorithm_choice">choix d\'algorhitme inconnu</string>
- <string name="error_user_id_no_email">aucun courriel trouvé</string>
+ <string name="error_user_id_no_email">aucune adresse courriel trouvée</string>
<string name="error_key_needs_a_user_id">au moins une identité est nécessaire</string>
- <string name="error_no_signature_passphrase">aucune phrase de passe n\'a été donnée</string>
+ <string name="error_no_signature_passphrase">aucun mot de passé n\'a été donné</string>
<string name="error_no_signature_key">aucune clef de signature n\'a été donnée</string>
<string name="error_invalid_data">Aucun contenu OpenPGP valide chiffré ou signé !</string>
<string name="error_integrity_check_failed">la vérification de l\'intégrité a échoué ! Les données ont été modifiées !</string>
- <string name="error_wrong_passphrase">phrase de passe erronnée</string>
+ <string name="error_wrong_passphrase">mot de passe erroné</string>
<string name="error_could_not_extract_private_key">impossible d\'extraire la clef privée</string>
<!--errors without preceeding Error:-->
<string name="error_jelly_bean_needed">Il vous faut Android 4.1 pour utiliser la fonction Beam NFC d\'Android !</string>
<string name="error_nfc_needed">La NFC doit être activée !</string>
<string name="error_beam_needed">Beam doit être activé !</string>
<string name="error_nothing_import">Aucune clef trouvée !</string>
- <string name="error_contacts_key_id_missing">Échec lors de la récupération de l\'ID de clef à partir des contacts !</string>
+ <string name="error_nothing_import_selected">Aucune clef sélectionnée pour l\'importation !</string>
+ <string name="error_contacts_key_id_missing">Échec de récupération de l\'ID de clef des contacts !</string>
<string name="error_generic_report_bug">Une erreur générique est survenue, veuillez créer un nouveau rapport de bogue pour OpenKeychain.</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">Non signé</string>
<string name="decrypt_result_invalid_signature">Signature invalide !</string>
- <string name="decrypt_result_signature_uncertified">Signé par (non certifié)</string>
- <string name="decrypt_result_signature_certified">Signé par</string>
- <string name="decrypt_result_signature_expired_key">La clef est expirée !</string>
- <string name="decrypt_result_signature_revoked_key">La clef a été révoquée !</string>
- <string name="decrypt_result_signature_missing_key">Clef publique inconnue</string>
+ <string name="decrypt_result_insecure_cryptography">Signature invalide (cryptographie non fiable) !</string>
+ <string name="decrypt_result_signature_uncertified">Signé par un clef <b>non confirmée</b></string>
+ <string name="decrypt_result_signature_secret">Signé par votre clef</string>
+ <string name="decrypt_result_signature_certified">Signé par une clef confirmée</string>
+ <string name="decrypt_result_signature_expired_key">Signé par une clef <b>expirée</b> !</string>
+ <string name="decrypt_result_signature_revoked_key">Signé par une clef <b>révoquée</b> !</string>
+ <string name="decrypt_result_signature_missing_key">Signé par une <b>clef publique inconnue</b></string>
<string name="decrypt_result_encrypted">Chiffré</string>
<string name="decrypt_result_not_encrypted">Non chiffré</string>
+ <string name="decrypt_result_insecure">Chiffrement non fiable</string>
<string name="decrypt_result_action_show">Montrer</string>
<string name="decrypt_result_action_Lookup">Rechercher</string>
- <string name="decrypt_invalid_text">Soit la signature est invalide, soit la clef a été révoquée/est expirée. Vous ne pouvez pas être sûr de qui a écrit le texte. Voulez-vous quand même l\'afficher ?</string>
+ <string name="decrypt_invalid_text">Soit la signature est invalide, soit la clef a été révoquée. Vous ne pouvez pas être certain de l\'identité du rédacteur du texte. Voulez-vous quand même l\'afficher ?</string>
<string name="decrypt_invalid_button">Je comprends les risques, affichez-le !</string>
<!--Add keys-->
<string name="add_keys_my_key">Ma clef :</string>
@@ -274,6 +338,7 @@
<string name="progress_cancelling">annulation...</string>
<string name="progress_saving">sauvegarde...</string>
<string name="progress_importing">importation...</string>
+ <string name="progress_revoking_uploading">Révocation et téléversement de la clef...</string>
<string name="progress_updating">Mise à jour des clefs...</string>
<string name="progress_exporting">exportation...</string>
<string name="progress_uploading">téléversement...</string>
@@ -294,11 +359,14 @@
<string name="progress_modify_subkeyrevoke">révocation des sous-clefs...</string>
<string name="progress_modify_subkeystrip">dépouillement des sous-clefs...</string>
<string name="progress_modify_subkeyadd">ajout des sous-clefs...</string>
- <string name="progress_modify_passphrase">changement de la phrase de passe...</string>
+ <string name="progress_modify_passphrase">modification du mot passe...</string>
+ <string name="progress_modify_pin">changement du NIP...</string>
+ <string name="progress_modify_admin_pin">changement du NIP d\'admin....</string>
<plurals name="progress_exporting_key">
<item quantity="one">exportation de la clef...</item>
<item quantity="other">exportation des clefs...</item>
</plurals>
+ <string name="progress_start">préparation de l\'opération...</string>
<string name="progress_extracting_signature_key">extraction de la clef de signature...</string>
<string name="progress_extracting_key">extraction de la clef...</string>
<string name="progress_preparing_streams">préparation des flux...</string>
@@ -318,6 +386,8 @@
<string name="progress_deleting">suppression des clefs...</string>
<string name="progress_con_saving">consolider : enregistrement dans le cache...</string>
<string name="progress_con_reimport">consolider : réimportation...</string>
+ <string name="progress_verifying_keyserver_url">vérification du serveur de clefs...</string>
+ <string name="progress_starting_orbot">Démarrage d\'Orbot...</string>
<!--action strings-->
<string name="hint_cloud_search_hint">Chercher par nom, adresse courriel...</string>
<!--key bit length selections-->
@@ -359,11 +429,14 @@
<string name="import_tab_qr_code">Code QR/NFC</string>
<string name="import_import">Importer les clefs choisies</string>
<string name="import_qr_code_wrong">Code QR incorrecte ! Veuillez réessayer !</string>
- <string name="import_qr_code_too_short_fingerprint">L\'empreinte est trop courte (&lt; 16 caractères)</string>
- <string name="import_qr_code_button">Balayer le code QR</string>
+ <string name="import_qr_code_fp">L\'empreinte est malformée ou trop courte !</string>
+ <string name="import_qr_code_too_short_fingerprint">L\'empreinte est trop courte !</string>
+ <string name="import_qr_code_button">Lire le code QR</string>
<string name="import_qr_code_text">Placez votre appareil photo au-dessus du code QR !</string>
+ <!--Import from URL-->
+ <string name="import_url_warn_no_search_parameter">Aucune demande de recherche n\'a été définie. Vous pouvez quand même effectuer une recherche manuelle sur le serveur de clefs.</string>
<!--Generic result toast-->
- <string name="view_log">Détails</string>
+ <string name="snackbar_details">Détails</string>
<string name="with_warnings">, avec des avertissements</string>
<string name="with_cancelled">, jusqu\'à l\'annulation</string>
<!--Import result toast-->
@@ -399,8 +472,8 @@
<item quantity="other">%1$d clefs ont été supprimées avec succès</item>
</plurals>
<plurals name="delete_ok_but_fail_2">
- <item quantity="one">, mais échec lors de la suppression d\'une key%2$s.</item>
- <item quantity="other">, mais échec lors de la suppression de %1$d key%2$s.</item>
+ <item quantity="one">, mais échec de suppression d\'une key%2$s.</item>
+ <item quantity="other">, mais échec de suppression de %1$d key%2$s.</item>
</plurals>
<plurals name="delete_ok">
<item quantity="one">Une key%2$s supprimée avec succès.</item>
@@ -412,6 +485,11 @@
</plurals>
<string name="delete_nothing">Rien à supprimer.</string>
<string name="delete_cancelled">Opération de suppression annulée.</string>
+ <!--Revoke result toast (snackbar)-->
+ <string name="revoke_ok">Clef révoquée avec succès.</string>
+ <string name="revoke_fail">Erreur de révocation de la clef !</string>
+ <string name="revoke_nothing">Rien à révoquer.</string>
+ <string name="revoke_cancelled">Opération de révocation annulée.</string>
<!--Certify result toast-->
<plurals name="certify_keys_ok">
<item quantity="one">Une key%2$s certifiée avec succès.</item>
@@ -431,10 +509,10 @@
<string name="intent_send_encrypt">Chiffrer avec OpenKeychain</string>
<string name="intent_send_decrypt">Déchiffrer avec OpenKeychain</string>
<!--Remote API-->
- <string name="api_settings_show_info">Afficher les informations avancées</string>
- <string name="api_settings_hide_info">Masquer les informations avancées</string>
- <string name="api_settings_show_advanced">Afficher les paramètres avancés</string>
- <string name="api_settings_hide_advanced">Masquer les paramètres avancés</string>
+ <string name="api_settings_show_info">Montrer les informations détaillées</string>
+ <string name="api_settings_hide_info">Cacher les informations détaillées</string>
+ <string name="api_settings_show_advanced">Montrer les paramètres avancés</string>
+ <string name="api_settings_hide_advanced">Cacher les paramètres avancés</string>
<string name="api_settings_no_key">Aucune clef choisie</string>
<string name="api_settings_select_key">Choisir une clef</string>
<string name="api_settings_create_key">Créer une nouvelle clef</string>
@@ -445,9 +523,9 @@
<string name="api_settings_start">Lancer l\'application</string>
<string name="api_settings_delete_account">Supprimer le compte</string>
<string name="api_settings_package_name">Nom du paquet</string>
- <string name="api_settings_package_signature">SHA-256 de la signature du paquet</string>
- <string name="api_settings_accounts">Comptes (API dépréciée)</string>
- <string name="api_settings_advanced">Informations avancées</string>
+ <string name="api_settings_package_certificate">SHA-256 du certificat du paquet</string>
+ <string name="api_settings_accounts">Comptes (ancienne API)</string>
+ <string name="api_settings_advanced">Informations détaillées</string>
<string name="api_settings_allowed_keys">Clefs autorisées</string>
<string name="api_settings_settings">Paramètres</string>
<string name="api_settings_key">Clef du compte :</string>
@@ -458,15 +536,29 @@
<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>
- <string name="api_select_pub_keys_missing_text">Aucune clef n\'a été trouvée pour ces identités :</string>
- <string name="api_select_pub_keys_dublicates_text">Plus d\'une clef existe pour ces identités :</string>
+ <string name="api_select_pub_keys_missing_text">Aucune clef trouvée pour ces adresses courriel :</string>
+ <string name="api_select_pub_keys_dublicates_text">Plus d\'une clef existe pour ces adresses courriel :</string>
<string name="api_select_pub_keys_text">Veuillez revoir la liste des destinataires !</string>
<string name="api_select_pub_keys_text_no_user_ids">Veuillez choisir les destinataires !</string>
<string name="api_error_wrong_signature">La vérification de la signature a échoué ! Avez-vous installé cette appli à partir d\'une source différente ? Si vous êtes sûr que ce n\'est pas une attaque, révoquez l\'enregistrement de cette appli dans OpenKeychain et enregistrez-la à nouveau.</string>
<string name="api_select_sign_key_text">Veuillez choisir une de vos clefs existantes ou en créer une nouvelle.</string>
+ <string name="api_select_keys_text">Aucune clef autorisée n\'est capable de déchiffrer le contenu. Veuillez choisir les clefs autorisées.</string>
<!--Share-->
<string name="share_qr_code_dialog_title">Partager par un code QR</string>
<string name="share_nfc_dialog">Partager par la NFC</string>
+ <!--retry upload dialog-->
+ <string name="retry_up_dialog_title">Échec de téléversement</string>
+ <string name="retry_up_dialog_message">Échec de téléversement. Souhaitez-vous ressayer l\'opération ?</string>
+ <string name="retry_up_dialog_btn_reupload">Ressayer l\'opération</string>
+ <string name="retry_up_dialog_btn_cancel">Annuler l\'opération</string>
+ <!--Delete or revoke private key dialog-->
+ <string name="del_rev_dialog_message">Si vous ne voulez plus utiliser cette clef, elle devrait être révoquée et téléversée. Choisissez \'SUPPRIMER SEULEMENT\' si vous souhaitez retirer la clef d\'OpenKeychain mais continuerez à l\'utiliser ailleurs.</string>
+ <string name="del_rev_dialog_title">Révoquer/supprimer la clef \'%s\' </string>
+ <string name="del_rev_dialog_btn_revoke">Révoquer et téléverser</string>
+ <string name="del_rev_dialog_btn_delete">Supprimer seulement</string>
+ <!--Delete Or Revoke Dialog spinner-->
+ <string name="del_rev_dialog_choice_delete">Supprimer seulement</string>
+ <string name="del_rev_dialog_choice_rev_upload">Révoquer et téléverser</string>
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">1 clef choisie</item>
@@ -507,21 +599,21 @@
<string name="key_trust_no_cloud_evidence">Aucune preuve de fiabilité provenant du nuage pour cette clef.</string>
<string name="key_trust_start_cloud_search">Lancer la recherche</string>
<string name="key_trust_results_prefix">Keybase.io offre des « preuves » affirmant que le propriétaire de cette clef : </string>
- <string name="key_trust_header_text">Note : les preuves de keybase.io sont une fonction expérimentales d\'OpenKeychain. Nous vous encourageons à balayer des codes QR ou à échanger des clefs via NFC en plus de les confirmer.</string>
+ <string name="key_trust_header_text">Note : les preuves de keybase.io sont une fonction expérimentales d\'OpenKeychain. Nous vous encourageons à lire des codes QR ou à échanger des clefs via NFC en plus de les confirmer.</string>
<!--keybase proof stuff-->
- <string name="keybase_narrative_twitter">Publie vers Twitter en tant que</string>
- <string name="keybase_narrative_github">Est connu sur GitHub en tant que</string>
- <string name="keybase_narrative_dns">Contrôle le/les nom(s) de domaine</string>
- <string name="keybase_narrative_web_site">Peut publier vers le/les site(s) Web</string>
- <string name="keybase_narrative_reddit">Publie sur Reddit en tant que</string>
- <string name="keybase_narrative_coinbase">Est connu sur Coinbase en tant que</string>
- <string name="keybase_narrative_hackernews">Publie sur « Hacker News » en tant que</string>
- <string name="keybase_narrative_unknown">Type de preuve inconnu</string>
+ <string name="keybase_narrative_twitter">Publie sur Twitter en tant que %s</string>
+ <string name="keybase_narrative_github">Est connu sur GitHub en tant que %s</string>
+ <string name="keybase_narrative_dns">Contrôle le/les nom(s) de domaine %s</string>
+ <string name="keybase_narrative_web_site">Peut publier vers le/les site(s) Web %s</string>
+ <string name="keybase_narrative_reddit">Publie sur Reddit en tant que %s</string>
+ <string name="keybase_narrative_coinbase">Est connu sur Coinbase en tant que %s</string>
+ <string name="keybase_narrative_hackernews">Publie sur « Hacker News » en tant que %s</string>
+ <string name="keybase_narrative_unknown">Type de preuve inconnu %s</string>
<string name="keybase_proof_failure">Malheureusement cette preuve ne peut pas être vérifiée.</string>
<string name="keybase_unknown_proof_failure">Problème inconnu avec le vérificateur de preuve</string>
<string name="keybase_problem_fetching_evidence">Problème avec la preuve</string>
<string name="keybase_key_mismatch">L\'empreinte de clef ne correspondant pas à celle du billet de preuve</string>
- <string name="keybase_dns_query_failure">Échec lors de la récupération de l\'enregistrement DNS TXT</string>
+ <string name="keybase_dns_query_failure">Échec de récupération de l\'enregistrement DNS TXT</string>
<string name="keybase_no_prover_found">Aucun vérificateur de preuve trouvé pour</string>
<string name="keybase_message_payload_mismatch">Le billet de preuve déchiffré ne correspond pas à la valeur attendue</string>
<string name="keybase_message_fetching_data">Récupération de la preuve</string>
@@ -538,7 +630,7 @@
<string name="keybase_reddit_attribution">attribué par Reddit à</string>
<string name="keybase_verify">Vérifier</string>
<!--Edit key-->
- <string name="edit_key_action_change_passphrase">Changer la phrase de passe</string>
+ <string name="edit_key_action_change_passphrase">Changer le mot de passe</string>
<string name="edit_key_action_add_identity">Ajouter une identité</string>
<string name="edit_key_action_add_subkey">Ajouter une sous-clef</string>
<string name="edit_key_edit_user_id_title">Choisir une action !</string>
@@ -549,41 +641,62 @@
<string-array name="edit_key_edit_user_id_revert_revocation">
<item>Renverser la révocation</item>
</string-array>
- <string name="edit_key_edit_user_id_revoked">Cette identité a été révoquée ! Ceci ne peut pas être annulé.</string>
+ <string name="edit_key_edit_user_id_revoked">Cette identité a été révoquée ! Cela ne peut pas être annulé.</string>
<string name="edit_key_edit_subkey_title">Choisissez une action !</string>
<string-array name="edit_key_edit_subkey">
<item>Changer l\'expiration</item>
<item>Révoquer la sous-clef</item>
<item>Dépouiller la sous-clef</item>
+ <item>Déplacer la sous-clef vers la ClefYubi / carte à puce</item>
</string-array>
<string name="edit_key_new_subkey">nouvelle sous-clef</string>
<string name="edit_key_select_flag">Veuillez sélectionner au moins un drapeau !</string>
<string name="edit_key_error_add_identity">Ajouter au moins une identité !</string>
<string name="edit_key_error_add_subkey">Ajouter au moins une sous-clef !</string>
+ <string name="edit_key_error_bad_nfc_algo">L’algorithme n\'est pas pris en charge par cette carte à puce ! </string>
+ <string name="edit_key_error_bad_nfc_size">La taille de clef n\'est pas prise en charge par la carte à puce !</string>
+ <string name="edit_key_error_bad_nfc_stripped">Impossible de déplacer la clef vers la carte à puce (soit dépouillée, soit « dévier-vers-la-carte ») !</string>
<!--Create key-->
<string name="create_key_upload">Synchroniser avec le nuage</string>
<string name="create_key_empty">Ce champ est exigé</string>
- <string name="create_key_passphrases_not_equal">Les phrases de passe ne correspondent pas</string>
+ <string name="create_key_passphrases_not_equal">Les mots de passe ne correspondent pas</string>
<string name="create_key_final_text">Vous avez saisie l\'identité suivante :</string>
<string name="create_key_final_robot_text">Créer une clef peut prendre du temps, prenez donc un café en attendant...</string>
<string name="create_key_rsa">(3 sous-clefs, RSA, 4096 bits)</string>
<string name="create_key_custom">(configuration personnalisée de la clef)</string>
<string name="create_key_name_text">Choisissez un nom associé à cette clef. Ce peut être un nom complet, p. ex. « Maxime Tremblay », ou un pseudo, p. ex. « Maxou ».</string>
<string name="create_key_email_text">Saisissez votre adresse courriel principale utilisée pour les communications sécurisées.</string>
- <string name="create_key_passphrase_text">Choisissez une phrase de passe forte. Elle protège votre clef en cas de vol de votre appareil.</string>
+ <string name="create_key_passphrase_text">Choisissez un mot de passe fort. Il protège votre clef en cas de vol de votre appareil.</string>
<string name="create_key_hint_full_name">Nom complet ou pseudo</string>
<string name="create_key_edit">Changer la configuration de la clef</string>
<string name="create_key_add_email">Ajouter une adresse courriel</string>
<string name="create_key_add_email_text">Des adresses courriel supplémentaires sont aussi associées à cette clef et peuvent être utilisées pour des communications sécurisées.</string>
- <string name="create_key_email_already_exists_text">Le courriel a déjà été ajouté</string>
+ <string name="create_key_email_already_exists_text">L\'adresse courriel a déjà été ajoutée</string>
+ <string name="create_key_email_invalid_email">Le format de l\'adresse courriel est invalide</string>
+ <string name="create_key_yubi_key_pin_text">Veuillez mémoriser le NIP. Il sera exigé pour une utilisation ultérieure de votre ClefYubi. Prenez si possible le NIP d\'admin. en note et stockez-le dans un endroit sûr</string>
+ <string name="create_key_yubi_key_pin">NIP</string>
+ <string name="create_key_yubi_key_admin_pin">NIP d\'admin.</string>
+ <string name="create_key_yubi_key_pin_repeat_text">Veuillez saisir le NIP et le NIP d\'admin. pour continuer.</string>
+ <string name="create_key_yubi_key_pin_repeat">Répéter le NIP</string>
+ <string name="create_key_yubi_key_admin_pin_repeat">Répéter le NIP d\'admin.</string>
+ <string name="create_key_yubi_key_pin_not_correct">NIP erroné !</string>
<!--View key-->
<string name="view_key_revoked">Révoquée : la clef ne doit plus être utilisée !</string>
<string name="view_key_expired">Expirée : le contact doit prolonger la validité de la clef !</string>
<string name="view_key_expired_secret">Expirée : vous pouvez prolonger la validité de la clef en la modifiant !</string>
<string name="view_key_my_key">Ma clef</string>
<string name="view_key_verified">Clef confirmée</string>
- <string name="view_key_unverified">Non confirmée : balayez le code QR pour confirmer la clef !</string>
+ <string name="view_key_unverified">Non confirmée : lisez le code QR pour confirmer la clef !</string>
<string name="view_key_fragment_no_system_contact">&lt;aucun&gt;</string>
+ <!--Add/Edit keyserver-->
+ <string name="add_keyserver_dialog_title">Ajouter un serveur de clefs</string>
+ <string name="edit_keyserver_dialog_title">Modifier le serveur de clefs</string>
+ <string name="add_keyserver_verified">Le serveur de clefs a été vérifié !</string>
+ <string name="add_keyserver_without_verification">Le serveur de clefs a été ajouté sans vérification.</string>
+ <string name="add_keyserver_invalid_url">URL invalide !</string>
+ <string name="add_keyserver_connection_failed">Échec de connexion au serveur de clefs. Veuillez vérifier l\'URL et votre connexion Internet.</string>
+ <string name="keyserver_preference_deleted">%s supprimé</string>
+ <string name="keyserver_preference_cannot_delete_last">Impossible de supprimer le dernier serveur de clefs. Il en faut au moins un !</string>
<!--Navigation Drawer-->
<string name="nav_keys">Clefs</string>
<string name="nav_encrypt_decrypt">Chiffrer/déchiffrer</string>
@@ -591,6 +704,7 @@
<string name="drawer_open">Ouvrir le tiroir de navigation</string>
<string name="drawer_close">Fermer le tiroir de navigation</string>
<string name="my_keys">Mes clefs</string>
+ <string name="nav_backup">Sauvegarde</string>
<!--hints-->
<string name="encrypt_content_edit_text_hint">Saisir le texte</string>
<!--certs-->
@@ -600,15 +714,15 @@
<string name="cert_positive">positif</string>
<string name="cert_revoke">révoqué</string>
<string name="cert_verify_ok">OK</string>
- <string name="cert_verify_failed">échec!</string>
- <string name="cert_verify_error">erreur!</string>
+ <string name="cert_verify_failed">échec !</string>
+ <string name="cert_verify_error">erreur !</string>
<string name="cert_verify_unavailable">clef non disponible</string>
<!--LogType log messages. Errors should have _ERROR_ in their name and end with a !-->
<string name="msg_internal_error">Erreur interne !</string>
<string name="msg_cancelled">Opération annulée.</string>
<!--Import Public log entries-->
<string name="msg_ip_apply_batch">Application de l\'opération d\'insertion par lot.</string>
- <string name="msg_ip_bad_type_secret">Tentative d\'importer le trousseau secret comme public. Ceci est un bogue, veuillez remplir un rapport !</string>
+ <string name="msg_ip_bad_type_secret">Tentative d\'importer le trousseau secret comme public. C\'est un bogue, veuillez remplir un rapport !</string>
<string name="msg_ip_delete_old_fail">Aucune ancienne clef de supprimée (création d\'une nouvelle ?)</string>
<string name="msg_ip_delete_old_ok">L\'ancienne clef a été supprimée de la base de données</string>
<string name="msg_ip_encode_fail">Échec de l\'opération causé par une erreur d\'encodage</string>
@@ -665,7 +779,7 @@
<string name="msg_ip_success_identical">Le trousseau ne contient pas de nouvelle donnée, rien à faire</string>
<string name="msg_ip_reinsert_secret">Réinsertion de la clef secrète</string>
<string name="msg_ip_uid_cert_bad">Un mauvais certificat a été rencontré !</string>
- <string name="msg_ip_uid_cert_error">Erreur lors du traitement du certificat !</string>
+ <string name="msg_ip_uid_cert_error">Erreur de traitement du certificat !</string>
<string name="msg_ip_uid_cert_nonrevoke">Il y a déjà un certificat non-révocable, étape ignorée.</string>
<string name="msg_ip_uid_cert_old">Le certificat est plus ancien que le précédent, étape ignorée.</string>
<string name="msg_ip_uid_cert_new">Le certificat est plus récent, remplacement du précédent.</string>
@@ -698,11 +812,11 @@
</plurals>
<string name="msg_ip_uat_classifying">Organisation des attributs utilisateur</string>
<string name="msg_ip_uat_revoked">L\'attribut utilisateur est révoqué</string>
- <string name="msg_is_bad_type_public">Tentative d\'importer le trousseau public comme secret. Ceci est un bogue, veuillez remplir un rapport !</string>
- <string name="msg_is_bad_type_uncanon">Tentative d\'importer un trousseau sans canonicalisation. Ceci est un bogue, veuillez remplir un rapport !</string>
+ <string name="msg_is_bad_type_public">Tentative d\'importer le trousseau public comme secret. C\'est un bogue, veuillez remplir un rapport !</string>
+ <string name="msg_is_bad_type_uncanon">Tentative d\'importer un trousseau sans canonicalisation. C\'est un bogue, veuillez remplir un rapport !</string>
<!--Import Secret log entries-->
<string name="msg_is">Importation de la clef secrète %s</string>
- <string name="msg_is_db_exception">Erreur de base de données!</string>
+ <string name="msg_is_db_exception">Erreur de base de données !</string>
<string name="msg_is_importing_subkeys">Traitement des sous-clefs secrètes</string>
<string name="msg_is_error_io_exc">Erreur d\'encodage du trousseau</string>
<string name="msg_is_merge_public">Fusion des données importées dans le trousseau public existant</string>
@@ -711,16 +825,16 @@
<string name="msg_is_pubring_generate">Génération du trousseau public à partir du trousseau secret</string>
<string name="msg_is_subkey_nonexistent">La sous-clef %s n\'est pas disponible dans la clef secrète</string>
<string name="msg_is_subkey_ok">Marquer la sous-clef secrète %s comme disponible</string>
- <string name="msg_is_subkey_empty">Marquer la sous-clef secrète %s comme disponible, avec une phrase de passe vide</string>
+ <string name="msg_is_subkey_empty">La sous-clef secrète %s a été marquée comme disponible, sans mot de passe</string>
<string name="msg_is_subkey_pin">La sous-clef secrète %s a été marquée comme disponible, avec un NIP</string>
<string name="msg_is_subkey_stripped">Marquer la sous-clef secrète %s comme dépouillée</string>
- <string name="msg_is_subkey_divert">Marquer la sous-clef secrète %s comme « dévier vers la carte intelligente/la NFC »</string>
+ <string name="msg_is_subkey_divert">Marquer la sous-clef secrète %s comme « dévier-vers-la-carte »</string>
<string name="msg_is_success_identical">Le trousseau ne contient pas de nouvelle donnée, rien à faire</string>
<string name="msg_is_success">Importation du trousseau secret réussie</string>
<!--Keyring Canonicalization log entries-->
<string name="msg_kc_public">Canonicalisation du trousseau public %s</string>
<string name="msg_kc_secret">Canonicalisation du trousseau secret %s</string>
- <string name="msg_kc_error_v3">Ceci est une clef OpenPGP version 3, qui a été déprécié et n\'est plus pris en charge !</string>
+ <string name="msg_kc_error_v3">C\'est une clef OpenPGP version 3, qui a été déprécié et n\'est plus pris en charge !</string>
<string name="msg_kc_error_no_uid">Le trousseau n\'a pas d\'ID utilisateur valide !</string>
<string name="msg_kc_error_master_algo">La clef maîtresse utilise un algorithme (%s) inconnu ! </string>
<string name="msg_kc_error_dup_key">La sous-clef %s se présente deux fois dans le trousseau. Le trousseau est mal formé, pas d\'importation ! </string>
@@ -741,6 +855,7 @@
<string name="msg_kc_sub_bad_local">Suppression du certificat de liaison de la sous-clef ayant le drapeau « local »</string>
<string name="msg_kc_sub_bad_keyid">L\'ID de l\'émetteur de la liaison de la sous-clef ne correspond pas</string>
<string name="msg_kc_sub_bad_time">Suppression du certificat de liaison de la sous-clef ayant une estampille temporelle dans le futur</string>
+ <string name="msg_kc_sub_bad_time_early">L\'estampille temporelle du certificat de liaison de la sous-clef est antérieur à celui de la clef !</string>
<string name="msg_kc_sub_bad_type">Type de certificat de sous-clef inconnu : %s</string>
<string name="msg_kc_sub_dup">Suppression du certificat redondant de liaison de sous-clef</string>
<string name="msg_kc_sub_primary_bad">Suppression du certificat de liaison de la sous-clef à cause d\'un certificat de liaison principal invalide</string>
@@ -774,7 +889,7 @@
<string name="msg_kc_uid_revoke_old">Suppression du certificat de révocation périmé pour l\'ID utilisateur « %s »</string>
<string name="msg_kc_uid_no_cert">Aucun auto-certificat valide trouvé pour l\'ID utilisateur « %s », qui est maintenant enlevé du trousseau</string>
<string name="msg_kc_uid_remove">Suppression de l\'ID utilisateur invalide « %s »</string>
- <string name="msg_kc_uid_dup">Suppression de l\'ID d\'utilisateur en double « %s ». Le trousseau en contenait deux. Ceci pourrait entraîner des certificats manquants !</string>
+ <string name="msg_kc_uid_dup">Suppression de l\'ID d\'utilisateur en double « %s ». Le trousseau en contenait deux. Cela pourrait entraîner des certificats manquants !</string>
<string name="msg_kc_uid_warn_encoding">L\'ID d\'utilisateur ne passe pas le test UTF-8 !</string>
<string name="msg_kc_uat_jpeg">Traitement de l\'attribut utilisateur de type JPEG</string>
<string name="msg_kc_uat_unknown">Traitement de l\'attribut utilisateur de type inconnu</string>
@@ -784,7 +899,7 @@
<string name="msg_kc_uat_bad_type">Suppression du certificat d\'attribut utilisateur de type inconnu (%s)</string>
<string name="msg_kc_uat_bad">Suppression du mauvais auto-certificat pour l\'attribut utilisateur</string>
<string name="msg_kc_uat_cert_dup">Suppression de l\'auto-certificat périmé pour l\'attribut utilisateur</string>
- <string name="msg_kc_uat_dup">Suppression de l\'attribut utilisateur en double. Le trousseau en contenait deux. Ceci pourrait entraîner des certificats manquants !</string>
+ <string name="msg_kc_uat_dup">Suppression de l\'attribut utilisateur en double. Le trousseau en contenait deux. Cela pourrait entraîner des certificats manquants !</string>
<string name="msg_kc_uat_foreign">Suppression du certificat étranger d\'attribut utilisateur par</string>
<string name="msg_kc_uat_revoke_dup">Suppression du certificat de révocation redondant pour l\'attribut utilisateur</string>
<string name="msg_kc_uat_revoke_old">Suppression de l\'auto-certificat périmé pour l\'attribut utilisateur</string>
@@ -805,48 +920,62 @@
<string name="msg_cr_error_no_master">Aucune option de clef maîtresse n\'a été spécifiée !</string>
<string name="msg_cr_error_no_user_id">Les trousseaux doivent être créés avec au moins un ID utilisateur !</string>
<string name="msg_cr_error_no_certify">La clef maîtresse doit avoir le drapeau « certifié » !</string>
- <string name="msg_cr_error_null_expiry">L\'expiration ne peut pas être « pareille qu\'avant » à la création de la clef. Ceci est une erreur du programme, veuillez remplir un rapport de bogue !</string>
+ <string name="msg_cr_error_null_expiry">L\'expiration ne peut pas être « pareille qu\'avant » à la création de la clef. C\'est une erreur du programme, veuillez remplir un rapport de bogue !</string>
<string name="msg_cr_error_keysize_512">La taille de la clef doit être supérieure ou égale à 512 !</string>
- <string name="msg_cr_error_no_curve">Aucune taille de clef n\'a été spécifiée ! Ceci est une erreur de programmation, veuillez remplir un rapport de bogue !</string>
- <string name="msg_cr_error_no_keysize">Aucune courbe elliptique n\'a été spécifiée ! Ceci est une erreur de programmation, veuillez remplir un rapport de bogue !</string>
+ <string name="msg_cr_error_no_curve">Aucune taille de clef n\'a été spécifiée ! C\'est une erreur de programmation, veuillez remplir un rapport de bogue !</string>
+ <string name="msg_cr_error_no_keysize">Aucune courbe elliptique n\'a été spécifiée ! C\'est une erreur de programmation, veuillez remplir un rapport de bogue !</string>
<string name="msg_cr_error_internal_pgp">Erreur interne OpenPGP !</string>
- <string name="msg_cr_error_unknown_algo">L\'algorithme choisi est inconnu ! Ceci est une erreur de programmation, veuillez remplir un rapport de bogue !</string>
+ <string name="msg_cr_error_unknown_algo">L\'algorithme choisi est inconnu ! C\'est une erreur de programmation, veuillez remplir un rapport de bogue !</string>
<string name="msg_cr_error_flags_dsa">Les drapeaux de clef choisis sont incorrects, DSA ne peut pas être utilisé pour le chiffrement ! </string>
<string name="msg_cr_error_flags_elgamal">Les drapeaux de clef choisis sont incorrects, ElGamal ne peut pas être utilisé pour le chiffrement ! </string>
<string name="msg_cr_error_flags_ecdsa">Les drapeaux de clef choisis sont incorrects, ECDSA ne peut pas être utilisé pour le chiffrement ! </string>
<string name="msg_cr_error_flags_ecdh">Les drapeaux de clef choisis sont incorrects, ECDH ne peut pas être utilisé pour le chiffrement ! </string>
<!--modifySecretKeyRing-->
<string name="msg_mr">Modification du trousseau %s</string>
- <string name="msg_mf_error_divert_serial">Le numéro de série d\'une clef diriger-vers-la-carte doit avoir 16 bytes ! Ceci est une erreur de programmation, veuillez remplir un rapport de bogue !</string>
+ <string name="msg_mf_divert">Déviera vers la carte à puce pour les opérations de chiffrement</string>
+ <string name="msg_mf_error_divert_newsub">La création de nouvelles sous-clef n\'est pas prise en charge pour les clefs primaires « dévier-vers-la-carte » ! </string>
+ <string name="msg_mf_error_divert_serial">Le numéro de série d\'une clef « dévier-vers-la-carte » doit avoir 16 bytes ! C\'est une erreur de programmation, veuillez remplir un rapport de bogue !</string>
<string name="msg_mf_error_encode">Exception d\'encodage !</string>
<string name="msg_mf_error_fingerprint">L\'empreinte de clef effective ne correspond pas à celle attendue !</string>
- <string name="msg_mf_error_keyid">Aucune ID de clef. Ceci est une erreur interne, veuillez remplir un rapport de bogue !</string>
+ <string name="msg_mf_error_keyid">Aucune ID de clef. C\'est une erreur interne, veuillez remplir un rapport de bogue !</string>
<string name="msg_mf_error_integrity">Erreur interne, le contrôle d\'intégrité a échoué !</string>
<string name="msg_mf_error_master_none">Aucun certificat maître sur lequel se basé n\'a été trouvé ! (Tous révoqués ?)</string>
<string name="msg_mf_error_noexist_primary">Mauvais ID utilisateur principal spécifié !</string>
<string name="msg_mf_error_noexist_revoke">Mauvais ID utilisateur spécifié pour la révocation !</string>
- <string name="msg_mf_error_restricted">Tentative d\'exécution d\'une opération restreinte sans phrase de passe ! Ceci est une erreur de programmation, veuillez remplir un rapport de bogue !</string>
+ <string name="msg_mf_error_restricted">Tentative d\'exécution d\'une opération restreinte sans mot de passe ! C\'est une erreur de programmation, veuillez remplir un rapport de bogue !</string>
<string name="msg_mf_error_revoked_primary">Les ID utilisateurs révoqués ne peuvent pas être principaux !</string>
- <string name="msg_mf_error_null_expiry">L\'expiration ne peut pas être « pareille qu\'avant » lors de la création de sous-clefs. Ceci est une erreur de programmation, veuillez remplir un rapport de bogue !</string>
- <string name="msg_mf_error_passphrase_master">Erreur fatale lors du déchiffrement de la clef maîtresse ! Ceci est probablement une erreur de programmation, veuillez remplir un rapport de bogue !</string>
+ <string name="msg_mf_error_null_expiry">L\'expiration ne peut pas être « pareille qu\'avant » lors de la création de sous-clefs. C\'est une erreur de programmation, veuillez remplir un rapport de bogue !</string>
+ <string name="msg_mf_error_noop">Rien à faire !</string>
+ <string name="msg_mf_error_passphrase_master">Erreur fatale de déchiffrement de la clef maîtresse ! C\'est probablement une erreur de programmation, veuillez remplir un rapport de bogue !</string>
<string name="msg_mf_error_pgp">Erreur interne OpenPGP !</string>
<string name="msg_mf_error_sig">Exception de signature !</string>
+ <string name="msg_mf_error_sub_stripped">Impossible de modifier la sous-clef dépouillée %s !</string>
+ <string name="msg_mf_error_subkey_missing">Une action a été tentée sur la sous-clef manquante %s !</string>
+ <string name="msg_mf_error_conflicting_nfc_commands">Impossible de déplacer la clef vers la carte à puce dans une même opération créant une signature sur la carte.</string>
+ <string name="msg_mf_error_duplicate_keytocard_for_slot">La carte à puce ne prend en charge qu\'un emplacement par type de clef.</string>
+ <string name="msg_mf_error_invalid_flags_for_keytocard">Drapeaux de clef inadéquats pour la clef de carte à puce.</string>
<string name="msg_mf_master">Modification des certifications maîtresses</string>
<string name="msg_mf_notation_empty">Ajout d\'un paquet de notation vide</string>
<string name="msg_mf_notation_pin">Ajout d\'un paquet de notation NIP</string>
- <string name="msg_mf_passphrase">Changement de la phrase de passe pour le trousseau</string>
- <string name="msg_mf_passphrase_key">Rechiffrement de la sous-clef %s avec la nouvelle phrase de passe</string>
- <string name="msg_mf_passphrase_empty_retry">Échec lors de la définition de la nouvelle phrase de passe, nouvel essai avec une ancienne phrase de passe vide</string>
- <string name="msg_mf_passphrase_fail">La phrase de passe de la sous-clef n\'a pas pu être changée ! (Est-elle différente des autres clefs ?)</string>
+ <string name="msg_mf_passphrase">Changement de mot de passe pour le trousseau</string>
+ <string name="msg_mf_pin">Changement du NIP sur la carte</string>
+ <string name="msg_mf_admin_pin">Changement du NIP d\'admin. sur la carte</string>
+ <string name="msg_mf_passphrase_key">Rechiffrement de la sous-clef %s avec un nouveau mot de passe</string>
+ <string name="msg_mf_passphrase_empty_retry">Échec de définition du nouveau mot de passe, nouvel essai avec un ancien mot de passe vide</string>
+ <string name="msg_mf_passphrase_fail">Le mot de passe de la sous-clef n\'a pas pu être changé ! (Est-il différent des autres clefs ?)</string>
<string name="msg_mf_primary_replace_old">Remplacement du certificat de l\'ID utilisateur principal précédent</string>
<string name="msg_mf_primary_new">Génération d\'un nouveau certificat pour le nouvel ID utilisateur principal</string>
+ <string name="msg_mf_restricted_mode">Changement vers le mode opérationnel restreint</string>
<string name="msg_mf_subkey_change">Modification de la sous-clef %s</string>
- <string name="msg_mf_error_subkey_missing">Une action a été tentée sur la sous-clef manquante %s !</string>
+ <string name="msg_mf_require_divert">Déviation vers la carte à puce pour les opérations de chiffrement</string>
+ <string name="msg_mf_require_passphrase">Mot de passe exigé pour les opérations</string>
<string name="msg_mf_subkey_new">Ajout d\'une nouvelle sous-clef de type %s</string>
<string name="msg_mf_subkey_new_id">Nouvelle ID de sous-clef : %s</string>
<string name="msg_mf_error_past_expiry">La date d\'expiration ne peut pas être dans le passé !</string>
<string name="msg_mf_subkey_revoke">Révocation de la sous-clef %s</string>
<string name="msg_mf_subkey_strip">Dépouillement de la sous-clef %s</string>
+ <string name="msg_mf_keytocard_start">Déplacement de la sous-clef %s vers la carte à puce</string>
+ <string name="msg_mf_keytocard_finish">%1$s déplacé vers la carte à puce %2$s</string>
<string name="msg_mf_success">Trousseau modifié avec succès</string>
<string name="msg_mf_uid_add">Ajout de l\'ID utilisateur %s</string>
<string name="msg_mf_uid_primary">Changement de l\'ID utilisateur principal en %s</string>
@@ -859,7 +988,7 @@
<string name="msg_mf_unlock">Déverrouillage du trousseau</string>
<!--Consolidate-->
<string name="msg_con">Consolidation de la base de données</string>
- <string name="msg_con_error_bad_state">La consolidation a été commencée alors qu\'aucune base de données n\'était en cache ! Ceci est probablement une erreur de programmation, veuillez remplir un rapport de bogue !</string>
+ <string name="msg_con_error_bad_state">La consolidation a été commencée alors qu\'aucune base de données n\'était en cache ! C\'est probablement une erreur de programmation, veuillez remplir un rapport de bogue !</string>
<string name="msg_con_error_concurrent">La consolidation a été abandonnée, elle est déjà en cours sur un autre exétron !</string>
<string name="msg_con_save_secret">Enregistrement des trousseaux secrets</string>
<string name="msg_con_save_public">Enregistrement des trousseaux publiques</string>
@@ -887,23 +1016,25 @@
<item quantity="other">Réimportation de %d clefs secrètes</item>
</plurals>
<string name="msg_con_reimport_secret_skip">Aucune clef secrète à réimporter, étape ignorée...</string>
- <string name="msg_con_warn_delete_public">Une exception a eu lieu lors de la suppression du fichier de cache public</string>
- <string name="msg_con_warn_delete_secret">Une exception a eu lieu lors de la suppression du fichier de cache secret</string>
+ <string name="msg_con_warn_delete_public">Exception de suppression du fichier de cache public</string>
+ <string name="msg_con_warn_delete_secret">Exception de suppression du fichier de cache secret</string>
<!--Edit Key (higher level than modify)-->
<string name="msg_ed">Exécution de l\'opération sur la clef</string>
- <string name="msg_ed_caching_new">Mise en cache de la nouvelle phrase de passe</string>
- <string name="msg_ed_error_no_parcel">SaveKeyringParcel absent ! (Ceci est un bogue, veuillez le rapporter)</string>
+ <string name="msg_ed_caching_new">Mise en cache du nouveau mot de passe</string>
+ <string name="msg_ed_error_no_parcel">SaveKeyringParcel absent ! (C\'est un bogue, veuillez le signaler)</string>
<string name="msg_ed_error_key_not_found">Clef introuvable !</string>
+ <string name="msg_ed_error_extract_public_upload">Erreur d\'extraction de la clef publique pour le téléversement !</string>
<string name="msg_ed_fetching">Obtention de la clef à modifier (%s)</string>
<string name="msg_ed_success">Opération sur la clef réussie</string>
<!--Promote key-->
<string name="msg_pr">Promotion de la clef publique en clef secrète</string>
- <string name="msg_pr_error_already_secret">La clef est déjà une clef secrète !</string>
+ <string name="msg_pr_all">Promotion de toutes les sous-clefs</string>
<string name="msg_pr_error_key_not_found">Clef introuvable !</string>
<string name="msg_pr_fetching">Obtention de la clef à modifier (%s)</string>
+ <string name="msg_pr_subkey_match">Promotion de la sous-clef : %s</string>
+ <string name="msg_pr_subkey_nomatch">La sous-clef n\'est pas sur la ClefYubi : %s</string>
<string name="msg_pr_success">Clef promue avec succès</string>
<!--Other messages used in OperationLogs-->
- <string name="msg_ek_error_divert">La modification des clefs NFC n\'est pas (encore) prise en charge !</string>
<string name="msg_ek_error_dummy">Impossible de modifier un trousseau avec une clef maîtresse dépouillée !</string>
<string name="msg_ek_error_not_found">Clef introuvable !</string>
<!--Messages for DecryptVerify operation-->
@@ -919,26 +1050,27 @@
<string name="msg_dc_clear_meta_size_unknown">La taille du fichier est inconnue</string>
<string name="msg_dc_clear_meta_time">Heure de modification : %s</string>
<string name="msg_dc_clear_signature_bad">La vérification de la signature n\'est PAS CORRECTE !</string>
- <string name="msg_dc_error_unsupported_hash_algo">Algorithme de hachage non pris en charge et potentiellement non sécurisé ! </string>
<string name="msg_dc_clear_signature_check">Vérification des données de signature</string>
<string name="msg_dc_clear_signature_ok">La vérification de la signature OK</string>
<string name="msg_dc_clear_signature">Enregistrement des données de signature pour plus tard</string>
<string name="msg_dc_clear">Traitement des données de texte en clair</string>
- <string name="msg_dc_error_bad_passphrase">Erreur de déverrouillage de la clef, phrase de passe erronée !</string>
+ <string name="msg_dc_error_bad_passphrase">Erreur de déverrouillage de la clef, mot de passe erroné !</string>
+ <string name="msg_dc_error_sym_passphrase">Erreur de déchiffrement de données (phrase de passe erronée ?)</string>
+ <string name="msg_dc_error_corrupt_data"> Les données sont corrompues !</string>
<string name="msg_dc_error_extract_key">Erreur inconnue de déverrouillage de la clef !</string>
<string name="msg_dc_error_integrity_check">Erreur de vérification de l\'intégrité !</string>
- <string name="msg_dc_error_integrity_missing">Vérification de l\'intégrité absente ! Ceci peut arriver car l\'application n\'est pas à jour, ou à cause d\'une attaque par mise à niveau inférieur.</string>
- <string name="msg_dc_error_invalid_siglist">Aucune donnée de signature valide trouvée !</string>
- <string name="msg_dc_error_io">Une exception E/S a été rencontrée durant l\'opération !</string>
+ <string name="msg_dc_error_invalid_data">Aucune donnée OpenPGP valide chiffrée ou signée n\'a été trouvée !</string>
+ <string name="msg_dc_error_io">Erreur de lecture des données d\'entrée !</string>
+ <string name="msg_dc_error_input">Erreur d\'ouverture du flux de données d\'entrée !</string>
<string name="msg_dc_error_no_data">Aucune donnée chiffrée n\'a été trouvée dans le flux !</string>
<string name="msg_dc_error_no_key">Aucune donnée chiffrée avec une clef secrète connue n\'a été trouvée dans le flux !</string>
<string name="msg_dc_error_pgp_exception">Une exception OpenPGP a été rencontrée durant l\'opération !</string>
<string name="msg_dc_integrity_check_ok">Vérification de l\'intégrité OK !</string>
<string name="msg_dc_ok_meta_only">Seules les métadonnées ont été demandées, déchiffrement ignoré</string>
<string name="msg_dc_ok">Déchiffrement/Vérification terminé</string>
- <string name="msg_dc_pass_cached">Utilisation de la phrase de passe du cache</string>
+ <string name="msg_dc_pass_cached">Utilisation du mot de passe du cache</string>
<string name="msg_dc_pending_nfc">Jeton NFC exigé, demande de saisie à l\'utilisateur...</string>
- <string name="msg_dc_pending_passphrase">Phrase de passe exigée, demande de saisie à l\'utilisateur...</string>
+ <string name="msg_dc_pending_passphrase">Mot de passe exigé, demande de saisie à l\'utilisateur...</string>
<string name="msg_dc_prep_streams">Préparation des flux pour le déchiffrement</string>
<string name="msg_dc">Début de l\'opération de déchiffrement...</string>
<string name="msg_dc_sym_skip">Données symétriques non autorisées, étape ignorée...</string>
@@ -947,7 +1079,10 @@
<string name="msg_dc_trail_sym">Des données traînantes chiffrées symétriquement ont été rencontrées</string>
<string name="msg_dc_trail_unknown">Des données traînantes de type inconnu ont été rencontrées</string>
<string name="msg_dc_unlocking">Déverrouillage de la clef secrète</string>
- <string name="msg_dc_old_symmetric_encryption_algo">Un algorithme de chiffrement possiblement non sécurisé à été utilisé !</string>
+ <string name="msg_dc_insecure_symmetric_encryption_algo">Un algorithme de chiffrement non fiable a été utilisé ! Cela peut être dû à une application ancienne ou à une attaque.</string>
+ <string name="msg_dc_insecure_hash_algo">Un algorithme de hachage non fiable a été utilisé ! Cela peut être dû à une application ancienne ou à une attaque.</string>
+ <string name="msg_dc_insecure_mdc_missing">Il manque le paquet du code de détection des modifications (MDC) ! Cela peut être dû à une application de chiffrement ancienne ou à une attaque par mise à niveau inférieur.</string>
+ <string name="msg_dc_insecure_key">Clef non fiable : soit la longueur de donnée de RSA/DSA/ElGamal est trop courte ou la courbe/algorithme ECC est considérée comme non fiable ! Cela peut être dû à une application ancienne ou à une attaque.</string>
<!--Messages for VerifySignedLiteralData operation-->
<string name="msg_vl">Lancement de la vérification de la signature</string>
<string name="msg_vl_error_no_siglist">Aucune liste de signatures dans les données littérales signées</string>
@@ -967,21 +1102,19 @@
<string name="msg_se_error_no_input">Aucune entrée donnée !</string>
<string name="msg_se_error_input_uri_not_found">Erreur d\'ouverture de l\'URI en lecture !</string>
<string name="msg_se_error_output_uri_not_found">Erreur d\'ouverture de l\'URI en écriture !</string>
- <string name="msg_se_error_too_many_inputs">Plus d\'entrées spécifiées que de sorties ! Ceci est probablement une erreur de programmation, veuillez remplir un rapport de bogue !</string>
- <string name="msg_se_warn_output_left">Il reste des sorties mais pas d\'entrée. Ceci est probablement une erreur de programmation, veuillez remplir un rapport de bogue !</string>
+ <string name="msg_se_error_too_many_inputs">Plus d\'entrées spécifiées que de sorties ! C\'est probablement une erreur de programmation, veuillez remplir un rapport de bogue !</string>
<string name="msg_se_success">Opération de signature/chiffrement réussie</string>
<!--Messages for PgpSignEncrypt operation-->
<string name="msg_pse_asymmetric">Préparation des clefs publiques pour le chiffrement</string>
<string name="msg_pse_clearsign_only">La signature de texte en clair n\'est pas prise en charge !</string>
<string name="msg_pse_compressing">Préparation de la compression</string>
<string name="msg_pse_encrypting">Chiffrement des données</string>
- <string name="msg_pse_error_bad_passphrase">Phrase de passe erronée !</string>
- <string name="msg_pse_error_hash_algo">L’algorithme de hachage demandé n\'est pas pris en charge par cette clef ! </string>
+ <string name="msg_pse_error_bad_passphrase">Mot de passe erroné !</string>
<string name="msg_pse_error_io">Une exception E/S a été rencontrée durant l\'opération !</string>
<string name="msg_pse_error_key_sign">La clef de signature choisie ne peut pas signer les données !</string>
<string name="msg_pse_error_sign_key">Erreur de récupération de la clef de signature !</string>
<string name="msg_pse_error_nfc">Erreur de données NFC !</string>
- <string name="msg_pse_error_no_passphrase">Aucune phrase de passe fournie !</string>
+ <string name="msg_pse_error_no_passphrase">Aucun mot de passe fourni !</string>
<string name="msg_pse_error_pgp">Erreur interne OpenPGP !</string>
<string name="msg_pse_error_sig">Une exception de signature OpenPGP a été rencontrée !</string>
<string name="msg_pse_error_unlock">Erreur inconnue de déverrouillage de la clef !</string>
@@ -990,7 +1123,7 @@
<string name="msg_pse_key_warn">Clef erronée pour le chiffrement : %s</string>
<string name="msg_pse_ok">Opération de signature/chiffrement réussie !</string>
<string name="msg_pse_pending_nfc">Jeton NFC exigé, demande de saisie à l\'utilisateur...</string>
- <string name="msg_pse_pending_passphrase">Phrase de passe exigée, demande de saisie à l\'utilisateur...</string>
+ <string name="msg_pse_pending_passphrase">Mot de passe exigé, demande de saisie à l\'utilisateur...</string>
<string name="msg_pse_signing">Signature des données (sans chiffrement)</string>
<string name="msg_pse_signing_cleartext">Création d\'une signature de texte en clair</string>
<string name="msg_pse_signing_detached">Création d\'une signature détachée</string>
@@ -1010,16 +1143,17 @@
<string name="msg_crt_error_master_not_found">Clef maîtresse introuvable !</string>
<string name="msg_crt_error_nothing">Aucune clef certifiée !</string>
<string name="msg_crt_error_unlock">Erreur de déverrouillage de la clef maîtresse !</string>
- <string name="msg_crt_error_divert">La certification par la NFC n\'est pas (encore) prise en charge !</string>
<string name="msg_crt">Certification des trousseaux</string>
<string name="msg_crt_master_fetch">Récupération de la clef maîtresse de certification</string>
+ <string name="msg_crt_nfc_return">Retour vers l\'écran NFC</string>
<string name="msg_crt_save">Enregistrement de la clef certifiée %s</string>
<string name="msg_crt_saving">Enregistrement des trousseaux</string>
<string name="msg_crt_unlock">Déverrouillage de la clef maîtresse</string>
<string name="msg_crt_success">Identités certifiées avec succès</string>
<string name="msg_crt_warn_not_found">Clef introuvable !</string>
- <string name="msg_crt_warn_cert_failed">Échec lors de la génération du certificat !</string>
- <string name="msg_crt_warn_save_failed">Échec lors de l\'opération d\'enregistrement !</string>
+ <string name="msg_crt_warn_cert_failed">Échec de génération du certificat !</string>
+ <string name="msg_crt_warn_save_failed">Échec de l\'opération d\'enregistrement !</string>
+ <string name="msg_crt_warn_upload_failed">Échec de l\'opération de téléversement !</string>
<string name="msg_crt_upload_success">Clef téléversée vers le serveur avec succès</string>
<plurals name="msg_import">
<item quantity="one">Importation de la clef</item>
@@ -1028,14 +1162,15 @@
<string name="msg_import_fetch_error_decode">Erreur de décodage du trousseau récupéré !</string>
<string name="msg_import_fetch_error">La clef n\'a pas pu être récupérée ! (problèmes réseau ?)</string>
<string name="msg_import_fetch_keybase">Récupération en provenance du keybase.io : %s</string>
- <string name="msg_import_fetch_keyserver_error">Impossible de récupérer la clef à partir de la base de clefs !</string>
+ <string name="msg_import_fetch_keyserver_error">Impossible de récupérer la clef sur les serveurs de clefs : %s</string>
<string name="msg_import_fetch_keyserver">Récupération en provenance du serveur de clefs : %s</string>
<string name="msg_import_fetch_keyserver_ok">Récupération de la clef est réussie !</string>
<string name="msg_import_keyserver">Utilisation du serveur de clefs %s</string>
<string name="msg_import_fingerprint_error">L\'empreinte de clef récupérée ne correspond pas à celle attendu !</string>
<string name="msg_import_fingerprint_ok">Vérification de l\'empreinte OK !</string>
<string name="msg_import_merge">Fusion des données récupérées</string>
- <string name="msg_import_error">Échec lors de l\'opération d\'importation !</string>
+ <string name="msg_import_merge_error">Erreur de fusion des données récupérées !</string>
+ <string name="msg_import_error">Échec de l\'opération d\'importation !</string>
<string name="msg_import_error_io">Échec de l\'opération causé par une erreur d\'E/S !</string>
<string name="msg_import_partial">Opération d\'importation réussie, avec des erreurs !</string>
<string name="msg_import_success">Opération d\'importation réussie !</string>
@@ -1043,8 +1178,10 @@
<item quantity="one">Exportation d\'une clef</item>
<item quantity="other">Exportation de %d clefs</item>
</plurals>
+ <string name="msg_export_file_name">Nom de fichier : %s</string>
<string name="msg_export_all">Exportation de toutes les clefs</string>
<string name="msg_export_public">Exportation de la clef publique %s</string>
+ <string name="msg_export_upload_public">Téléversement de la clef publique %s</string>
<string name="msg_export_secret">Exportation de la clef secrète %s</string>
<string name="msg_export_error_no_file">Aucun nom de fichier spécifié !</string>
<string name="msg_export_error_fopen">Erreur d\'ouverture du fichier !</string>
@@ -1054,7 +1191,9 @@
<string name="msg_export_error_db">Erreur de base de données !</string>
<string name="msg_export_error_io">Erreur d\'entrée/sortie !</string>
<string name="msg_export_error_key">Erreur de prétraitement des données de la clef !</string>
+ <string name="msg_export_error_upload">Échec de téléversement de la clef vers le serveur. Veuillez vérifier votre connexion Internet.</string>
<string name="msg_export_success">Opération d\'exportation réussie !</string>
+ <string name="msg_export_upload_success">Téléversement vers le serveur de clefs réussi</string>
<string name="msg_del_error_empty">Rien à supprimer !</string>
<string name="msg_del_error_multi_secret">Les clefs secrètes ne peuvent être supprimées qu\'individuellement !</string>
<plurals name="msg_del">
@@ -1062,20 +1201,25 @@
<item quantity="other">Suppression de %d clefs</item>
</plurals>
<string name="msg_del_key">Suppression de la clef %s</string>
- <string name="msg_del_key_fail">Échec lors de la suppression de la clef %s</string>
+ <string name="msg_del_key_fail">Échec de suppression de la clef %s</string>
<string name="msg_del_consolidate">Consolidation de la base de données après la suppression de la clef secrète</string>
<plurals name="msg_del_ok">
<item quantity="one">Suppression de la clef réussie</item>
<item quantity="other">Suppression de %d clefs réussie</item>
</plurals>
<plurals name="msg_del_fail">
- <item quantity="one">Échec lors de la suppression d\'une clef</item>
- <item quantity="other">Échec lors de la suppression de %d clefs</item>
+ <item quantity="one">Échec de suppression d\'une clef</item>
+ <item quantity="other">Échec de suppression de %d clefs</item>
</plurals>
+ <string name="msg_revoke_error_empty">Rien à révoquer !</string>
+ <string name="msg_revoke_error_not_found">Impossible de trouver la clef a révoquer !</string>
+ <string name="msg_revoke_key">Révocation de la clef %s</string>
+ <string name="msg_revoke_key_fail">Échec de révocation de la clef</string>
+ <string name="msg_revoke_ok">Clef révoquée avec succès</string>
<string name="msg_acc_saved">Compte enregistré</string>
<string name="msg_download_success">Téléchargement réussi !</string>
<string name="msg_download_no_valid_keys">Aucune clef valide n\'a été trouvée dans le fichier/presse-papiers |</string>
- <string name="msg_download_no_pgp_parts">À FAIRE : pluriels !</string>
+ <string name="msg_download_no_pgp_parts">À FAIRE : les pluriels !</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">une partie du fichier chargé est un objet OpenPGP valide mais pas une clef OpenPGP</item>
<item quantity="other">certaines parties du fichier chargé sont des objets OpenPGP valides mais pas des clefs OpenPGP</item>
@@ -1084,6 +1228,14 @@
<string name="msg_download_too_many_responses">La requête de recherche de clef a retourné trop de candidats. Veuillez raffiner votre requête !</string>
<string name="msg_download_query_too_short_or_too_many_responses">Soit aucune clef ou trop de clefs ont été trouvées. Veuillez améliorer votre requête !</string>
<string name="msg_download_query_failed">Une erreur est survenue lors de la recherche de clefs.</string>
+ <!--Messages for Keybase Verification operation-->
+ <string name="msg_keybase_verification">Tentative de vérification « keybase » pour %s</string>
+ <string name="msg_keybase_error_no_prover">Aucun vérificateur de preuve trouvé pour %s</string>
+ <string name="msg_keybase_error_fetching_evidence">Problème de récupération de la preuve</string>
+ <string name="msg_keybase_error_key_mismatch">L\'empreinte de la clef ne correspond pas à celle du billet de preuve</string>
+ <string name="msg_keybase_error_dns_fail">Échec de récupération de l\'enregistrement DNS TXT</string>
+ <string name="msg_keybase_error_specific">%s</string>
+ <string name="msg_keybase_error_msg_payload_mismatch">Le billet de preuve déchiffré ne correspond pas à la valeur attendue</string>
<!--Messages for Export Log operation-->
<string name="msg_export_log_start">Exportation du journal</string>
<string name="msg_export_log_error_fopen">Erreur d\'ouverture du fichier !</string>
@@ -1091,17 +1243,31 @@
<string name="msg_export_log_error_writing">Erreur E/S d\'écriture vers le fichier !</string>
<string name="msg_export_log_success">Journal exporté avec succès !</string>
<!--PassphraseCache-->
- <string name="passp_cache_notif_click_to_clear">Cliquer ici pour effacer les phrases de passe mises en cache</string>
- <string name="passp_cache_notif_n_keys">OpenKeychain a mis en cache %d phrases de passe</string>
- <string name="passp_cache_notif_keys">Phrases de passe mises en cache :</string>
- <string name="passp_cache_notif_clear">Effacer le cache</string>
- <string name="passp_cache_notif_pwd">Phrase de passe</string>
+ <string name="passp_cache_notif_click_to_clear">Toucher pour effacer les mots de passe.</string>
+ <plurals name="passp_cache_notif_n_keys">
+ <item quantity="one">%d mot de passe mémorisé</item>
+ <item quantity="other">%d mots de passe mémorisés</item>
+ </plurals>
+ <string name="passp_cache_notif_keys">Mots de passe mémorisés</string>
+ <string name="passp_cache_notif_clear">Effacer les mots de passe</string>
+ <string name="passp_cache_notif_pwd">Mot de passe</string>
+ <!--Keyserver sync-->
+ <string name="keyserver_sync_orbot_notif_title">Synchroniser à partir du nuage (Orbot exigé)</string>
+ <string name="keyserver_sync_orbot_notif_msg">Toquer pour lancer Orbot</string>
+ <string name="keyserver_sync_orbot_notif_start">Lancer Orbot</string>
+ <string name="keyserver_sync_orbot_notif_ignore">Directe</string>
<!--First Time-->
<string name="first_time_text1">Reprenez le contrôle de votre vie privée avec OpenKeychain |</string>
<string name="first_time_create_key">Créer ma clef</string>
<string name="first_time_import_key">Importer la clef d\'un fichier</string>
- <string name="first_time_yubikey">Utiliser YubiKey NEO</string>
+ <string name="first_time_yubikey">Utiliser le NEO de la ClefYubi</string>
<string name="first_time_skip">Ignorer le paramétrage</string>
+ <string name="first_time_blank_yubikey">Voulez-vous utiliser cette ClefYubi NEO vide avec OpenKeychain ?\n\nVeuillez retirer la ClefYubi maintenant, vous serez informé quand elle sera requise de nouveau !</string>
+ <string name="first_time_blank_yubikey_yes">Utiliser cette ClefYubi</string>
+ <string name="backup_text">Les sauvegardes incluant vos propres clefs ne doivent jamais être partagées avec d\'autres personnes !</string>
+ <string name="backup_all">Toutes les clefs + vos propres clefs</string>
+ <string name="backup_public_keys">Toutes les clefs</string>
+ <string name="backup_section">Sauvegarde</string>
<!--unsorted-->
<string name="section_certifier_id">Certificateur</string>
<string name="section_cert">Détails du certificat</string>
@@ -1118,40 +1284,46 @@
<string name="error_key_not_found">Clef introuvable !</string>
<string name="error_key_processing">Erreur de traitement de la clef !</string>
<string name="key_stripped">dépouillée</string>
- <string name="key_divert">dévier vers la carte intelligente/la NFC</string>
- <string name="key_no_passphrase">aucune phrase de passe</string>
+ <string name="key_divert">dévier vers la carte à puce</string>
+ <string name="key_no_passphrase">aucun mot de passe</string>
<string name="key_unavailable">non disponible</string>
<string name="secret_cannot_multiple">Vos propres clefs ne peuvent être supprimées qu\'individuellement !</string>
<string name="title_view_cert">Voir les détails du certificat</string>
<string name="unknown_algorithm">inconnu</string>
<string name="can_sign_not">impossible de signer</string>
<string name="error_no_encrypt_subkey">Aucune sous-clef de chiffrement n\'est proposée !</string>
- <string name="info_no_manual_account_creation">Ne pas créer de comptes-OpenKeychain manuellement.\nPour plus d\'informations, consultez l\'Aide.</string>
<string name="contact_show_key">Montrer la clef (%s)</string>
<string name="swipe_to_update">Glisser vers le bas pour mettre à jour à partir du serveur de clefs</string>
<string name="error_no_file_selected">Choisir au moins un fichier à chiffrer !</string>
- <string name="error_multi_not_supported">L\'enregistrement des fichiers multiples n\'est pas pris en charge. Ceci est actuellement une limitation d\'Android.</string>
+ <string name="error_multi_files">L\'enregistrement de fichiers multiples n\'est pas pris en charge. C\'est une restriction actuelle d\'Android.</string>
+ <string name="error_multi_clipboard">Le chiffrement de fichiers multiples vers le presse-papiers n\'est pas pris en charge.</string>
+ <string name="error_detached_signature">Les opérations de signature seule des fichiers binaires ne sont pas prises en charge. Choisissez au moins une clef de chiffrement.</string>
+ <string name="error_empty_text">Taper un texte à chiffrer !</string>
<string name="key_colon">Clef :</string>
<string name="exchange_description">Pour démarrer un échange de clef, choisir le nombre de participants du côté droit, puis cliquer sur le bouton « Démarrer l\'échange ».\n\Deux questions de plus seront posées pour s\'assurer que seuls les bons participants sont dans l\'échange et que les empreintes sont correctes.</string>
<string name="btn_start_exchange">Démarrer l\'échange</string>
<string name="user_id_none"><![CDATA[<none>]]></string>
+ <!--Android Account-->
+ <string name="account_no_manual_account_creation">Vous ne pouvez pas créer de compte OpenKeychain manuellement..</string>
+ <string name="account_privacy_title">Confidentialité</string>
+ <string name="account_privacy_text">OpenKeychain ne synchronise pas vos contacts avec l\'Internet. Il ne fait que relier les contacts aux clefs d\'après les noms et les adresses courriel. Il le fait hors ligne sur votre appareil.</string>
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
<string name="title_unlock_method">Choisir une méthode de déverrouillage</string>
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
- <string name="enter_passphrase">Saisir la phrase de passe</string>
- <string name="passphrase">Phrase de passe</string>
- <string name="noPassphrase">Aucune phrase de passe</string>
- <string name="no_passphrase_set">Aucune phrase de passe définie</string>
- <string name="passphrases_match">Les phrases de passe ne correspondent pas</string>
- <string name="passphrase_saved">Phrase de passe enregistrée</string>
- <string name="passphrase_invalid">Phrase de passe invalide</string>
- <string name="missing_passphrase">Phrase de passe manquante</string>
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
+ <string name="enter_passphrase">Saisir le mot de passe</string>
+ <string name="passphrase">Mot de passe</string>
+ <string name="noPassphrase">Aucun mot de passe</string>
+ <string name="no_passphrase_set">Aucun mot de passe défini</string>
+ <string name="passphrases_match">Les mots de passe ne correspondent pas</string>
+ <string name="passphrase_saved">Mot de passe enregistré</string>
+ <string name="passphrase_invalid">Mot de passe invalide</string>
+ <string name="missing_passphrase">Mot de passe manquant</string>
<string name="passphrase_again">De nouveau</string>
<string name="lockpattern">Schéma de verrouillage</string>
<string name="lockpatternNFC">NFC + schéma de verrouillage</string>
<string name="unlock_method">Méthode de déverrouillage</string>
- <string name="set_passphrase">Définir la phrase de passe</string>
+ <string name="set_passphrase">Définir le mot de passe</string>
<string name="draw_lockpattern">Dessiner le schéma de verrouillage</string>
<string name="nfc_title">NFC</string>
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
@@ -1161,4 +1333,62 @@
<string name="nfc_write_succesful">Écrit avec succès sur la balise NFC</string>
<string name="unlocked">Déverrouillé</string>
<string name="nfc_settings">Paramètres</string>
+ <string name="snack_yubikey_view">Visualiser</string>
+ <string name="snack_yubikey_import">Importer</string>
+ <string name="button_bind_key">Relier la clef</string>
+ <string name="yubikey_serno">No de série : %s</string>
+ <string name="yubikey_key_holder">Détenteur de la clef :</string>
+ <string name="yubikey_key_holder_not_set">Détenteur de la clef : &lt;not set&gt;</string>
+ <string name="yubikey_status_bound">La ClefYubi correspond et est reliée à la clef</string>
+ <string name="yubikey_status_unbound">La ClefYubi correspond et peut être reliée à la clef</string>
+ <string name="yubikey_status_partly">La ClefYubi correspond et est partiellement reliée à la clef</string>
+ <string name="yubikey_create">Tenez la ClefYubi contre le dos de votre appareil.</string>
+ <string name="btn_import">Importer</string>
+ <string name="snack_yubi_other">Une clef différente est stockée sur la ClefYubi !</string>
+ <string name="error_nfc">Erreur NFC ; %s</string>
+ <plurals name="error_pin">
+ <item quantity="one">NIP erroné !\nil reste %d essai.</item>
+ <item quantity="other">NIP erroné !\nil reste %d essais.</item>
+ </plurals>
+ <string name="error_nfc_terminated">La ClefYubi est en état de fin d\'opération.</string>
+ <string name="error_nfc_wrong_length">Le NIP saisi est trop court. Les NIP comportent au moins 6 chiffres.</string>
+ <string name="error_nfc_conditions_not_satisfied">Les conditions d\'utilisation ne sont pas satisfaites.</string>
+ <string name="error_nfc_security_not_satisfied">L\'état de sécurité n\'est pas satisfait.</string>
+ <string name="error_nfc_authentication_blocked">NIP bloqué après trop d\'essais.</string>
+ <string name="error_nfc_data_not_found">Clef ou objet introuvable.</string>
+ <string name="error_nfc_unknown">Erreur inconnue</string>
+ <string name="error_nfc_bad_data">La ClefYubi a signalé des données invalides.</string>
+ <string name="error_nfc_chaining_error">La ClefYubi attendait la dernière commande d\'une chaîne.</string>
+ <string name="error_nfc_header">La ClefYubi a signalé %s bytes invalides.</string>
+ <string name="error_nfc_tag_lost">La ClefYubi a été retirée trop tôt. Conservez la ClefYubi contre le dos jusqu\'à la fin de l\'opération.</string>
+ <string name="error_nfc_try_again">Ressayer</string>
+ <string name="error_pin_nodefault">Le NIP par défaut a été rejeté !</string>
+ <string name="error_temp_file">Erreur de création du fichier temporaire.</string>
+ <string name="btn_delete_original">Supprimer le fichier original</string>
+ <string name="snack_encrypt_filenames_on">Les noms de fichiers <b>sont</b> chiffrés.</string>
+ <string name="snack_encrypt_filenames_off">Les noms de fichiers <b>ne sont pas</b> chiffrés.</string>
+ <string name="snack_armor_on">Sortie encodée comme texte.</string>
+ <string name="snack_armor_off">Sortie encodée comme exécutable.</string>
+ <string name="snack_compression_on">Compression <b>activée</b>.</string>
+ <string name="snack_compression_off">Compression <b>désactivée</b>.</string>
+ <string name="error_loading_keys">Erreur de chargement des clefs !</string>
+ <string name="error_empty_log">(erreur, journal vide)</string>
+ <string name="error_reading_text">Impossible de lire l\'entrée à déchiffrer !</string>
+ <string name="filename_unknown">&lt;aucun nom de fichier&gt;</string>
+ <string name="filename_unknown_text">&lt;données texte en clair &gt;</string>
+ <string name="intent_show">Montrer le contenu signé/chiffré</string>
+ <string name="view_internal">Visualiser dans OpenKeychain</string>
+ <string name="error_preparing_data">Erreur de préparation des données !</string>
+ <string name="label_clip_title">Données chiffrées</string>
+ <string name="progress_processing">Traitement...</string>
+ <string name="error_saving_file">Erreur d\'enregistrement du fichier !</string>
+ <string name="file_saved">Fichier enregistré !</string>
+ <string name="file_delete_ok">Fichier original supprimé.</string>
+ <string name="file_delete_none">Aucun fichier supprimé (déjà supprimé ?)</string>
+ <string name="file_delete_exception">Impossible de supprimer le fichier original !</string>
+ <string name="error_clipboard_empty">Le presse-papiers est vide !</string>
+ <string name="error_clipboard_copy">Erreur de copie des données vers le presse-papiers !</string>
+ <string name="error_scan_fp">Erreur de numérisation de l\'empreinte !</string>
+ <string name="error_scan_match">Les empreintes ne correspondent pas !</string>
+ <string name="error_expiry_past">La date d\'expiration est dans le passé !</string>
</resources>
diff --git a/OpenKeychain/src/main/res/values-bg/strings.xml b/OpenKeychain/src/main/res/values-hu/strings.xml
index 518b80f47..b0fd66dfe 100644
--- a/OpenKeychain/src/main/res/values-bg/strings.xml
+++ b/OpenKeychain/src/main/res/values-hu/strings.xml
@@ -7,6 +7,12 @@
<!--button-->
<!--menu-->
<!--label-->
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <!--proxy type choices and values-->
+ <!--OrbotHelper strings-->
+ <!--InstallDialogFragment strings-->
+ <!--StartOrbotDialogFragment strings-->
<!--choice-->
<!--key flags-->
<!--sentences-->
@@ -27,13 +33,18 @@
<!--compression-->
<!--Help-->
<!--Import-->
+ <!--Import from URL-->
<!--Generic result toast-->
<!--Import result toast-->
<!--Delete result toast-->
+ <!--Revoke result toast (snackbar)-->
<!--Certify result toast-->
<!--Intent labels-->
<!--Remote API-->
<!--Share-->
+ <!--retry upload dialog-->
+ <!--Delete or revoke private key dialog-->
+ <!--Delete Or Revoke Dialog spinner-->
<!--Key list-->
<!--Key view-->
<!--Key trust-->
@@ -41,6 +52,7 @@
<!--Edit key-->
<!--Create key-->
<!--View key-->
+ <!--Add/Edit keyserver-->
<!--Navigation Drawer-->
<!--hints-->
<!--certs-->
@@ -59,12 +71,15 @@
<!--Messages for VerifySignedLiteralData operation-->
<!--Messages for SignEncrypt operation-->
<!--Messages for PgpSignEncrypt operation-->
+ <!--Messages for Keybase Verification operation-->
<!--Messages for Export Log operation-->
<!--PassphraseCache-->
+ <!--Keyserver sync-->
<!--First Time-->
<!--unsorted-->
+ <!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
</resources>
diff --git a/OpenKeychain/src/main/res/values-is/strings.xml b/OpenKeychain/src/main/res/values-is/strings.xml
deleted file mode 100644
index 518b80f47..000000000
--- a/OpenKeychain/src/main/res/values-is/strings.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<resources>
- <!--GENERAL: Please put all strings inside quotes as described in example 1 on
- http://developer.android.com/guide/topics/resources/string-resource.html (scroll down to "Escaping apostrophes and quotes").-->
- <!--title-->
- <!--section-->
- <!--button-->
- <!--menu-->
- <!--label-->
- <!--choice-->
- <!--key flags-->
- <!--sentences-->
- <!--errors
- no punctuation, all lowercase,
- they will be put after "error_message", e.g. "Error: file not found"-->
- <!--errors without preceeding Error:-->
- <!--results shown after decryption/verification-->
- <!--Add keys-->
- <!--progress dialogs, usually ending in '…'-->
- <!--action strings-->
- <!--key bit length selections-->
- <!--elliptic curve names-->
- <!--not in for now, see SaveKeyringParcel
- <string name="key_curve_bp_p256">"Brainpool P-256"</string>
- <string name="key_curve_bp_p384">"Brainpool P-384"</string>
- <string name="key_curve_bp_p512">"Brainpool P-512"</string>-->
- <!--compression-->
- <!--Help-->
- <!--Import-->
- <!--Generic result toast-->
- <!--Import result toast-->
- <!--Delete result toast-->
- <!--Certify result toast-->
- <!--Intent labels-->
- <!--Remote API-->
- <!--Share-->
- <!--Key list-->
- <!--Key view-->
- <!--Key trust-->
- <!--keybase proof stuff-->
- <!--Edit key-->
- <!--Create key-->
- <!--View key-->
- <!--Navigation Drawer-->
- <!--hints-->
- <!--certs-->
- <!--LogType log messages. Errors should have _ERROR_ in their name and end with a !-->
- <!--Import Public log entries-->
- <!--Import Secret log entries-->
- <!--Keyring Canonicalization log entries-->
- <!--Keyring merging log entries-->
- <!--createSecretKeyRing-->
- <!--modifySecretKeyRing-->
- <!--Consolidate-->
- <!--Edit Key (higher level than modify)-->
- <!--Promote key-->
- <!--Other messages used in OperationLogs-->
- <!--Messages for DecryptVerify operation-->
- <!--Messages for VerifySignedLiteralData operation-->
- <!--Messages for SignEncrypt operation-->
- <!--Messages for PgpSignEncrypt operation-->
- <!--Messages for Export Log operation-->
- <!--PassphraseCache-->
- <!--First Time-->
- <!--unsorted-->
- <!--Passphrase wizard-->
- <!--TODO: rename all the things!-->
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
- <!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
-</resources>
diff --git a/OpenKeychain/src/main/res/values-it/strings.xml b/OpenKeychain/src/main/res/values-it/strings.xml
index 6db044b49..9f58dc3ba 100644
--- a/OpenKeychain/src/main/res/values-it/strings.xml
+++ b/OpenKeychain/src/main/res/values-it/strings.xml
@@ -4,39 +4,60 @@
http://developer.android.com/guide/topics/resources/string-resource.html (scroll down to "Escaping apostrophes and quotes").-->
<string name="app_name">OpenKeychain</string>
<!--title-->
+ <string name="title_encrypt_text">Codifica</string>
+ <string name="title_encrypt_files">Codifica</string>
<string name="title_decrypt">Decodifica</string>
<string name="title_add_subkey">Aggiungi Sottochiave</string>
<string name="title_edit_key">Modifica Chiave</string>
+ <string name="title_preferences">Impostazioni</string>
<string name="title_api_registered_apps">Apps</string>
- <string name="title_change_passphrase">Cambia Frase Di Accesso</string>
+ <string name="title_key_server_preference">Server chiavi OpenPGP</string>
+ <string name="title_change_passphrase">Cambia la password</string>
<string name="title_share_fingerprint_with">Condivi impronta con...</string>
<string name="title_share_key">Condividi chiave con...</string>
<string name="title_share_file">Condividi file con...</string>
+ <string name="title_share_message">Condividi testo con...</string>
<string name="title_encrypt_to_file">Codifica File</string>
<string name="title_decrypt_to_file">Decodifica File</string>
<string name="title_import_keys">Importa Chiavi</string>
- <string name="title_export_key">Esporta Chiave</string>
- <string name="title_export_keys">Esporta Chiavi</string>
+ <string name="title_export_key">Backup chiave</string>
+ <string name="title_export_keys">Backup chiavi</string>
<string name="title_key_not_found">Chiave Non Trovata</string>
<string name="title_send_key">Carica sul Server delle Chiavi</string>
+ <string name="title_certify_key">Conferma chiave</string>
<string name="title_key_details">Dettagli Chiave</string>
<string name="title_help">Aiuto</string>
<string name="title_log_display">Registro</string>
<string name="title_exchange_keys">Scambia le chiavi</string>
- <string name="title_advanced_key_info">Informazioni avanzate sulla chiave</string>
+ <string name="title_advanced_key_info">Informazioni avanzate</string>
+ <string name="title_delete_secret_key">Cancellare la TUA chiave \'%s\'?</string>
+ <string name="title_export_log">Esporta log</string>
+ <string name="title_manage_my_keys">Gestisci le mie chiavi</string>
<!--section-->
<string name="section_user_ids">Identità</string>
+ <string name="section_yubikey">YubiKey</string>
+ <string name="section_should_you_trust">Ci si potrà fidare di questa chiave?</string>
+ <string name="section_proof_details">Verifica prova</string>
+ <string name="section_cloud_evidence">Prova dalla cloud</string>
<string name="section_keys">Sottochiavi</string>
<string name="section_cloud_search">Ricerca</string>
- <string name="section_passphrase_cache">Cache Frase di Accesso</string>
+ <string name="section_passphrase_cache">Trattamento Password/PIN</string>
+ <string name="section_certify">Conferma</string>
<string name="section_actions">Azioni</string>
<string name="section_share_key">Chiave</string>
<string name="section_key_server">Server delle Chiavi</string>
<string name="section_fingerprint">Impronta</string>
+ <string name="section_encrypt">Codifica</string>
+ <string name="section_decrypt">Decodifica / verifica</string>
+ <string name="section_current_expiry">Scadenza attuale</string>
+ <string name="section_new_expiry">Nuova scadenza</string>
<!--button-->
<string name="btn_decrypt_verify_file">Decodifica, verifica e salva su file</string>
<string name="btn_encrypt_share_file">Codifica e condividi file</string>
+ <string name="btn_encrypt_save_file">Codifica e salva file</string>
+ <string name="btn_save_file">Salva file</string>
<string name="btn_save">Salva</string>
+ <string name="btn_view_log">Mostra log</string>
<string name="btn_do_not_save">Annulla</string>
<string name="btn_delete">Elimina</string>
<string name="btn_no_date">Nessuna Scadenza</string>
@@ -44,31 +65,50 @@
<string name="btn_export_to_server">Carica sul Server delle Chiavi</string>
<string name="btn_next">Prossimo</string>
<string name="btn_back">Precedente</string>
+ <string name="btn_no">No</string>
+ <string name="btn_match">Impronte digitali ugali</string>
+ <string name="btn_share_encrypted_signed">Codifica e condividi testo</string>
+ <string name="btn_copy_encrypted_signed">Codifica e copia testo</string>
<string name="btn_view_cert_key">Mostra chiave di certificazione</string>
<string name="btn_create_key">Crea chiave</string>
<string name="btn_add_files">Aggiungi file(s)</string>
- <string name="btn_add_share_decrypted_text">Condividi testo decifrato</string>
- <string name="btn_decrypt_and_verify">e verifica le firme</string>
- <string name="btn_decrypt_files">Decifra files</string>
+ <string name="btn_share_decrypted_text">Condividi testo decifrato</string>
+ <string name="btn_copy_decrypted_text">Copia testo decifrato</string>
+ <string name="btn_decrypt_clipboard">Leggi dagli appunti</string>
+ <string name="btn_decrypt_files">Seleziona input file</string>
+ <string name="btn_encrypt_files">Codifica file</string>
+ <string name="btn_encrypt_text">Codifica testo</string>
+ <string name="btn_add_email">Aggiungi indirizzo e-mail aggiuntivo</string>
+ <string name="btn_unlock">Sblocca</string>
+ <string name="btn_add_keyserver">Aggiungi</string>
+ <string name="btn_save_default">Salva come predefinito</string>
+ <string name="btn_saved">Salvato!</string>
<!--menu-->
<string name="menu_preferences">Impostazioni</string>
<string name="menu_help">Aiuto</string>
- <string name="menu_export_key">Esporta su un file</string>
<string name="menu_delete_key">Cancella chiave</string>
+ <string name="menu_manage_keys">Gestisci mie chiavi</string>
<string name="menu_search">Cerca</string>
+ <string name="menu_nfc_preferences">Impostazioni NFC</string>
<string name="menu_beam_preferences">Impostazioni Beam</string>
<string name="menu_encrypt_to">Codifica su...</string>
<string name="menu_select_all">Seleziona tutto</string>
<string name="menu_export_all_keys">Esporta tutte le chiavi</string>
- <string name="menu_advanced">Mostra informazioni avanzate</string>
+ <string name="menu_update_all_keys">Aggiorna tutte le chiavi</string>
+ <string name="menu_advanced">Informazioni avanzate</string>
+ <string name="menu_certify_fingerprint">Confermare tramite il confronto delle impronte digitali</string>
+ <string name="menu_export_log">Esporta log</string>
+ <string name="menu_keyserver_add">Aggiungi</string>
<!--label-->
+ <string name="label_message">Testo</string>
<string name="label_file">File</string>
<string name="label_files">File(s)</string>
<string name="label_file_colon">File:</string>
- <string name="label_no_passphrase">Nessuna Frase di Accesso</string>
- <string name="label_passphrase">Frase di Accesso</string>
+ <string name="label_no_passphrase">Nessuna password</string>
+ <string name="label_passphrase">Password</string>
<string name="label_unlock">Sbloccando...</string>
- <string name="label_passphrase_again">Ripeti Frase di Accesso</string>
+ <string name="label_passphrase_again">Ripeti password</string>
+ <string name="label_show_passphrase">Mostra password</string>
<string name="label_algorithm">Algortimo</string>
<string name="label_ascii_armor">Documenti con Armatura ASCII</string>
<string name="label_file_ascii_armor">Abilita Armatura ASCII</string>
@@ -76,16 +116,20 @@
<string name="label_write_version_header_summary">Scrive \'OpenKeychain v2.7\' nelle firme OpenPGP, testi cifrati e chiavi esportate</string>
<string name="label_use_default_yubikey_pin">Utilizza il PIN predefinito di YubiKey</string>
<string name="label_use_num_keypad_for_yubikey_pin">Utilizza la tastiera numerica per il PIN di YubiKey</string>
- <string name="label_asymmetric_from">Firmato da:</string>
+ <string name="label_label_use_default_yubikey_pin_summary">Usa PIN predefinito (123456) per accedere YubiKeys tramite NFC</string>
<string name="label_to">Codifica per:</string>
+ <string name="label_delete_after_encryption">Elimina file dopo la codifica</string>
<string name="label_delete_after_decryption">Elimina dopo la decodifica</string>
<string name="label_encryption_algorithm">Algoritmo di codifica</string>
<string name="label_hash_algorithm">Algoritmo Hash</string>
- <string name="label_symmetric">Cifra con password</string>
- <string name="label_passphrase_cache_subs">Memorizza password tramite sottochiave</string>
+ <string name="label_symmetric">Codifica con password</string>
+ <string name="label_passphrase_cache_ttl">Tempo da ricordare</string>
+ <string name="label_passphrase_cache_subs">Ricorda le password di sottochiave</string>
+ <string name="label_message_compression">Compressione testo</string>
<string name="label_file_compression">Compressione file</string>
- <string name="label_keyservers">Server Chiavi</string>
+ <string name="label_keyservers">Seleziona server chiavi OpenPGP</string>
<string name="label_key_id">ID Chiave</string>
+ <string name="label_key_created">Chiave creata %s</string>
<string name="label_creation">Creazione</string>
<string name="label_expiry">Scadenza</string>
<string name="label_usage">Utilizzo</string>
@@ -98,8 +142,39 @@
<string name="label_send_key">Sincronizza con il cloud</string>
<string name="label_fingerprint">Impronta</string>
<string name="expiry_date_dialog_title">Impostare la data di scadenza</string>
- <string name="label_first_keyserver_is_used">(Primo server delle chiavi elencato è il preferito)</string>
+ <string name="label_keyservers_title">Server chiavi</string>
+ <string name="label_keyserver_settings_hint">Trascina per cambiare ordine, toccare per modificare/cancellare</string>
+ <string name="label_selected_keyserver_title">Server chiavi scelto</string>
<string name="label_preferred">preferiti</string>
+ <string name="label_enable_compression">Abilitare compressione</string>
+ <string name="label_encrypt_filenames">Codifica nome dei file</string>
+ <string name="label_hidden_recipients">Nascondi destinatari</string>
+ <string name="label_verify_keyserver">Verificare server chiavi</string>
+ <string name="label_enter_keyserver_url">Inserisci URL server chiavi</string>
+ <string name="label_keyserver_dialog_delete">Cancella server chiavi</string>
+ <string name="pref_keyserver">Server chiavi OpenPGP</string>
+ <string name="pref_keyserver_summary">Cerca chiavi su server chiavi OpenPGP selezionati (protocollo HKP)</string>
+ <string name="pref_keybase">keybase.io</string>
+ <string name="pref_keybase_summary">Ricerca chiavi su keybase.io</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <string name="pref_proxy_type_title">Tipo proxy</string>
+ <!--proxy type choices and values-->
+ <string name="pref_proxy_type_choice_http">HTTP</string>
+ <string name="pref_proxy_type_choice_socks">SOCKS</string>
+ <!--OrbotHelper strings-->
+ <string name="orbot_ignore_tor">Non usare Tor</string>
+ <!--InstallDialogFragment strings-->
+ <string name="orbot_install_dialog_title">Installa Orbot per usare Tor?</string>
+ <string name="orbot_install_dialog_install">Installa</string>
+ <string name="orbot_install_dialog_content">Orbot deve essere installato e attivato per fare attraversare il traffico dei dati tramite proxy. Vuoi installare Orbot?</string>
+ <string name="orbot_install_dialog_cancel">Annulla</string>
+ <string name="orbot_install_dialog_ignore_tor">Non usare Tor</string>
+ <!--StartOrbotDialogFragment strings-->
+ <string name="orbot_start_dialog_title">Attivare Orbot?</string>
+ <string name="orbot_start_btn">Attivare Orbot</string>
+ <string name="orbot_start_dialog_start">Attivare Orbot</string>
+ <string name="orbot_start_dialog_cancel">Annulla</string>
<string name="user_id_no_name">&lt;nessun nome&gt;</string>
<string name="none">&lt;nessuno&gt;</string>
<plurals name="n_keys">
@@ -139,17 +214,27 @@
<string name="flag_encrypt">Codifica</string>
<string name="flag_authenticate">Convalida</string>
<!--sentences-->
- <string name="wrong_passphrase">Frase di Accesso errata</string>
+ <string name="wrong_passphrase">Password errata</string>
<string name="no_filemanager_installed">Nessun gestore file compatibile installato.</string>
- <string name="passphrases_do_not_match">Le frasi di accesso non corrispondono.</string>
- <string name="passphrase_must_not_be_empty">Si prega di inserire una frase di accesso.</string>
+ <string name="passphrases_do_not_match">Le password non corrispondono.</string>
+ <string name="passphrase_must_not_be_empty">Si prega di inserire una password.</string>
<string name="passphrase_for_symmetric_encryption">Codifica Simmetrica.</string>
- <string name="passphrase_for">Inserisci la frase di accesso per \'%s\'</string>
- <string name="no_file_selected">Seleziona un file prima.</string>
+ <string name="passphrase_for">Inserisci la password per \'%s\'</string>
+ <string name="pin_for">Inserisci il PIN per \'%s\'</string>
+ <string name="yubikey_pin_for">Inserisci il PIN per accedere a YubiKey con \'%s\'</string>
+ <string name="file_delete_confirmation_title">Eliminare i file originali?</string>
+ <string name="file_delete_confirmation">I seguenti file verranno cancellati:%s</string>
+ <string name="file_delete_successful">%1$d di %2$d file sono stati cancellati.%3$s</string>
+ <string name="no_file_selected">Nessun file selezionato.</string>
<string name="encrypt_sign_successful">Firmato e/o codificato con successo.</string>
<string name="encrypt_sign_clipboard_successful">Firmato e/o codificato con successo negli appunti.</string>
<string name="select_encryption_key">Seleziona almeno una chiave di codifica.</string>
- <string name="select_encryption_or_signature_key">Seleziona almeno una chiave di codifica o di firma.</string>
+ <string name="error_no_encryption_or_signature_key">Selezionare almeno una chiave per la cifratura o una chiave per la firma.</string>
+ <string name="specify_file_to_encrypt_to">Per favore specifica il file da codificare entro.\nATTENZIONE: Il file sarà sovrascritto se esistente.</string>
+ <string name="specify_file_to_decrypt_to">Per favore specifica il file da decifrare entro.\nATTENZIONE: Il file sarà sovrascritto se esistente.</string>
+ <string name="key_deletion_confirmation_multi">Vuoi veramente eliminare tutte le chiavi selezionate?</string>
+ <string name="secret_key_deletion_confirmation">Dopo la cancellazione non sarai in grado di leggere i messaggi cifrati con questa chiave e perderai tutte le conferme principali fatti con essa!</string>
+ <string name="public_key_deletetion_confirmation">Elimnina chiave \'%s\'?</string>
<string name="also_export_secret_keys">Esporta anche chiave segreta</string>
<string name="reinstall_openkeychain">Hai riscontrato un bug noto con Android. Si prega di reinstallare OpenKeychain se vuoi collegare i tuoi contatti con le chiavi.</string>
<string name="key_exported">1 chiave esportata correttamente.</string>
@@ -157,6 +242,7 @@
<string name="no_keys_exported">Nessuna chiave esportata.</string>
<string name="key_creation_el_gamal_info">Nota: supporto sottochiavi solo per ElGamal.</string>
<string name="key_not_found">Impossibile trovare la chiave %08X.</string>
+ <string name="specify_file_to_export_log_to">Per favore specifica il file da esportare entro.\nATTENZIONE: Il file sarà sovrascritto se esistente.</string>
<plurals name="bad_keys_encountered">
<item quantity="one">%d chiave segreta corrotta ignorata. Tra l\'altro hai esportato con l\'opzione\n --export-secret-subkeys\nAssicurati di esportare con\n --export-secret-keys\ninvece.</item>
<item quantity="other">%d chiavi segrete corrotte ignorate. Tra l\'altro hai esportato con l\'opzione\n --export-secret-subkeys\nAssicurati di esportare con\n --export-secret-keys\ninvece.</item>
@@ -165,40 +251,47 @@
<string name="nfc_successful">Chiave correttamente inviata tramite NFC Beam!</string>
<string name="key_copied_to_clipboard">Chiave copiata negli appunti!</string>
<string name="fingerprint_copied_to_clipboard">Impronta copiata negli appunti!</string>
+ <string name="select_key_to_certify">Per favore seleziona una chiave da utilizzare per la conferma!</string>
<string name="key_too_big_for_sharing">Chiave troppo grande per essere condivisa in questo modo!</string>
<string name="text_copied_to_clipboard">Il testo è stato copiato sulla lavagna!</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">non sono stati cancellati. Cancellare manualmente!</string>
+ <string name="error_file_added_already">%s è già stato aggiunto.</string>
<string name="error_file_not_found">File non trovato</string>
<string name="error_no_secret_key_found">nessuna chiave privata adatta trovata</string>
<string name="error_external_storage_not_ready">memoria esterna non pronta</string>
<string name="error_key_size_minimum512bit">La grandezza della chiave deve essere almeno di 512bit</string>
<string name="error_unknown_algorithm_choice">opzione algoritmo sconosciuta</string>
- <string name="error_user_id_no_email">Nessuna email trovata</string>
+ <string name="error_user_id_no_email">nessun indirizzo di posta elettronica trovato</string>
<string name="error_key_needs_a_user_id">neccessaria almeno una identità</string>
- <string name="error_no_signature_passphrase">nessuna frase di accesso inserita</string>
+ <string name="error_no_signature_passphrase">nessuna password</string>
<string name="error_no_signature_key">nessuna chiave di firma inserita</string>
<string name="error_invalid_data">Contenuti OpenPGP firmati o codificati non validi!</string>
<string name="error_integrity_check_failed">Controllo di integrita\' fallito! I dati sono stati modificati!</string>
- <string name="error_wrong_passphrase">frase di accesso errata</string>
+ <string name="error_wrong_passphrase">Password errata</string>
<string name="error_could_not_extract_private_key">impossibile estrarre la chiave privata</string>
<!--errors without preceeding Error:-->
<string name="error_jelly_bean_needed">Devi avere Android 4.1 per usare Android NFC Beam!</string>
+ <string name="error_nfc_needed">NFC deve essere abilitato!</string>
<string name="error_nothing_import">Nessuna chiave trovata!</string>
+ <string name="error_nothing_import_selected">Nessune chiavi selezionate per l\'importazione!</string>
<string name="error_contacts_key_id_missing">Recupero dell\'ID della chiave dai contatti fallito!</string>
<string name="error_generic_report_bug">Si è verificato un errore generico, si prega di creare una nuova segnalazione di errore per OpenKeychain.</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">Non Firmato</string>
<string name="decrypt_result_invalid_signature">Firma non valida!</string>
- <string name="decrypt_result_signature_uncertified">Firmato da (non certificato!)</string>
- <string name="decrypt_result_signature_certified">Firmato da</string>
- <string name="decrypt_result_signature_expired_key">Chiave scaduta!</string>
- <string name="decrypt_result_signature_revoked_key">Chiave revocata!</string>
- <string name="decrypt_result_signature_missing_key">Chiave pubblica sconosciuta</string>
+ <string name="decrypt_result_signature_uncertified">Firmato da chiave <b> non confermata </b></string>
+ <string name="decrypt_result_signature_secret">Firmato con tua chiave</string>
+ <string name="decrypt_result_signature_certified">Firmato con chiave confermata</string>
+ <string name="decrypt_result_signature_expired_key">Firmato con chiave <b>scaduta</b>!</string>
+ <string name="decrypt_result_signature_revoked_key">Firmato con chiave <b>revocata</b>!</string>
+ <string name="decrypt_result_signature_missing_key">Firmato con chiave <b>pubblica sconusciuta</b></string>
<string name="decrypt_result_encrypted">Codificato</string>
<string name="decrypt_result_not_encrypted">Non Codificato</string>
<string name="decrypt_result_action_show">Mostra</string>
+ <string name="decrypt_invalid_button">Capisco i rischi, visualizza!</string>
<!--Add keys-->
<string name="add_keys_my_key">Mia chiave:</string>
<!--progress dialogs, usually ending in '…'-->
@@ -207,6 +300,7 @@
<string name="progress_cancelling">cancellando...</string>
<string name="progress_saving">salvataggio...</string>
<string name="progress_importing">importazione...</string>
+ <string name="progress_updating">Aggiorna chiavi...</string>
<string name="progress_exporting">esportazione...</string>
<string name="progress_uploading">caricamento...</string>
<string name="progress_building_key">fabbricazione chiave...</string>
@@ -225,7 +319,9 @@
<string name="progress_modify_subkeyrevoke">Revoca sottochiave...</string>
<string name="progress_modify_subkeystrip">pulizia sottochiavi...</string>
<string name="progress_modify_subkeyadd">Aggiunta sottochiave...</string>
- <string name="progress_modify_passphrase">Cambio frase di accesso...</string>
+ <string name="progress_modify_passphrase">cambiando la password...</string>
+ <string name="progress_modify_pin">cambiando PIN...</string>
+ <string name="progress_modify_admin_pin">cambiando Admin PIN...</string>
<plurals name="progress_exporting_key">
<item quantity="one">esportazione chiave...</item>
<item quantity="other">esportazione chiavi...</item>
@@ -277,6 +373,7 @@
<!--Help-->
<string name="help_tab_start">Inizia</string>
<string name="help_tab_faq">FAQ</string>
+ <string name="help_tab_wot">Conferma chiave</string>
<string name="help_tab_nfc_beam">NFC Beam</string>
<string name="help_tab_changelog">Novita\'</string>
<string name="help_tab_about">Info</string>
@@ -288,8 +385,10 @@
<string name="import_tab_qr_code">Codice QR/NFC</string>
<string name="import_import">Importa chiavi selezionate</string>
<string name="import_qr_code_wrong">Codica QR deformato! Prova di nuovo!</string>
- <string name="import_qr_code_too_short_fingerprint">Impronta troppo corta (&lt; 16 caratteri)</string>
+ <string name="import_qr_code_button">Scansione codice QR</string>
+ <!--Import from URL-->
<!--Generic result toast-->
+ <string name="snackbar_details">Dettagli</string>
<string name="with_warnings">, con avvisi</string>
<string name="with_cancelled">, fino all\'annullamento</string>
<!--Import result toast-->
@@ -322,6 +421,7 @@
<!--Delete result toast-->
<string name="delete_nothing">Niente da cancellare.</string>
<string name="delete_cancelled">Operazione di eliminazione cancellata.</string>
+ <!--Revoke result toast (snackbar)-->
<!--Certify result toast-->
<!--Intent labels-->
<string name="intent_decrypt_file">Decodifica File con OpenKeychain</string>
@@ -329,12 +429,9 @@
<string name="intent_send_encrypt">Codifica con OpenKeychain</string>
<string name="intent_send_decrypt">Decodifica con OpenKeychain</string>
<!--Remote API-->
- <string name="api_settings_show_info">Mostra informazioni dettagliate</string>
- <string name="api_settings_hide_info">Nascondi informazioni dettagliate</string>
- <string name="api_settings_show_advanced">Mostra impostazioni avanzate</string>
- <string name="api_settings_hide_advanced">Nascondi impostazioni avanzate</string>
<string name="api_settings_no_key">Nessuna chiave selezionata</string>
<string name="api_settings_select_key">Seleziona chiave</string>
+ <string name="api_settings_create_key">Crea chiave nuova</string>
<string name="api_settings_save">Salva</string>
<string name="api_settings_save_msg">L\'account è stato salvato</string>
<string name="api_settings_cancel">Annulla</string>
@@ -342,7 +439,6 @@
<string name="api_settings_start">Avvia applicazione</string>
<string name="api_settings_delete_account">Cancella account</string>
<string name="api_settings_package_name">Nome Pacchetto</string>
- <string name="api_settings_package_signature">SHA-256 della Firma del Pacchetto</string>
<string name="api_settings_settings">Impostazioni</string>
<string name="api_settings_key">Chiave account:</string>
<string name="api_settings_accounts_empty">Nessun account collegato a questa applicazione</string>
@@ -353,14 +449,15 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<string name="api_register_allow">Permetti accesso</string>
<string name="api_register_disallow">Nega accesso</string>
<string name="api_register_error_select_key">Per favore selezionare una chiave!</string>
- <string name="api_select_pub_keys_missing_text">Nessuna chiave trovata per queste identità:</string>
- <string name="api_select_pub_keys_dublicates_text">Esistono piu\' di una chiave per queste identità:</string>
<string name="api_select_pub_keys_text">Per favore ricontrolla la lista destinatari!</string>
<string name="api_select_pub_keys_text_no_user_ids">Per favore seleziona i destinatari!</string>
<string name="api_error_wrong_signature">Controllo della firma fallito! Hai installato questa app da una fonte diversa? Se sei sicuro che non sia un attacco, revoca la registrazione di questa app in OpenKeychain e dopo registra di nuovo l\'app.</string>
<!--Share-->
<string name="share_qr_code_dialog_title">Condividi tramite Codice QR</string>
<string name="share_nfc_dialog">Condividi tramite NFC</string>
+ <!--retry upload dialog-->
+ <!--Delete or revoke private key dialog-->
+ <!--Delete Or Revoke Dialog spinner-->
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">1 chiave selezionata.</item>
@@ -392,7 +489,6 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<!--Key trust-->
<!--keybase proof stuff-->
<!--Edit key-->
- <string name="edit_key_action_change_passphrase">Cambia Frase Di Accesso</string>
<string name="edit_key_action_add_identity">Aggiungi Identità</string>
<string name="edit_key_action_add_subkey">Aggiungi Sottochiave</string>
<string name="edit_key_edit_user_id_title">Seleziona un azione!</string>
@@ -405,24 +501,19 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
</string-array>
<string name="edit_key_edit_user_id_revoked">Questa identità è stata revocata. Ciò non può essere annullato.</string>
<string name="edit_key_edit_subkey_title">Seleziona un azione!</string>
- <string-array name="edit_key_edit_subkey">
- <item>Cambia Scadenza</item>
- <item>Revoca Sottochiave</item>
- <item>Pulisci Sottochiave</item>
- </string-array>
<string name="edit_key_new_subkey">nuova sottochiave</string>
<string name="edit_key_select_flag">Per favore seleziona almeno una spunta!</string>
<string name="edit_key_error_add_identity">Aggiungi almeno una identità</string>
<string name="edit_key_error_add_subkey">Aggiungi almeno una sottochiave!</string>
<!--Create key-->
<string name="create_key_empty">Questo campo è necessario</string>
- <string name="create_key_passphrases_not_equal">Le frasi di accesso non coincidono.</string>
<string name="create_key_final_text">Hai inserito la seguente identità:</string>
<string name="create_key_final_robot_text">La creazione di una chiave richiede un po\' di tempo, prendi un caffè nel frattempo...</string>
<string name="create_key_rsa">(3 sottochiavi, RSA, 4096 bit)</string>
<string name="create_key_custom">(personalizza la configurazione della chiave)</string>
<string name="create_key_edit">Cambia configurazione della chiave</string>
<!--View key-->
+ <!--Add/Edit keyserver-->
<!--Navigation Drawer-->
<string name="nav_keys">Chiavi</string>
<string name="nav_apps">Apps</string>
@@ -521,7 +612,6 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<string name="msg_is_pubring_generate">Generazione portachiavi pubblico da portachiavi privato</string>
<string name="msg_is_subkey_nonexistent">Sottochiave %s non disponibile nella chiave segreta</string>
<string name="msg_is_subkey_ok">Sottochiave segreta %s marcata come disponibile</string>
- <string name="msg_is_subkey_empty">Sottochiave segreta %s marcata come disponibile, con frase di accesso vuota</string>
<string name="msg_is_subkey_stripped">Sottochiave segreta %s marcata come ripulita</string>
<string name="msg_is_success_identical">Il portachiavi non contiene nuovi dati, nulla da eseguire</string>
<string name="msg_is_success">Portachiavi segreto importato con successo</string>
@@ -580,12 +670,9 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<string name="msg_mf_error_null_expiry">La data di scadenza non può essere \"come prima\" sulla creazione di sottochiavi. Questo è un errore di programmazione, si prega di inviare una segnalazione di bug!</string>
<string name="msg_mf_error_passphrase_master">Errore irreversibile nella decodifica della chiave principale! Questo è probabilmente un errore di programmazione, si prega di inviare una segnalazione di bug!</string>
<string name="msg_mf_error_sig">Eccezione di firma!</string>
+ <string name="msg_mf_error_subkey_missing">Tentativo di operare su sottochiave %s mancante!</string>
<string name="msg_mf_master">Modifica delle certificazioni principali</string>
- <string name="msg_mf_passphrase_key">Ri-codifica sottochiave %s con nuova frase di accesso</string>
- <string name="msg_mf_passphrase_empty_retry">Impostazione nuova frase di accesso fallita, provo di nuovo con frase precedente di accesso vuota</string>
- <string name="msg_mf_passphrase_fail">La frase di accesso per la sottochiave non può essere modificata! (Ne ha una diversa dalle altre chiavi?)</string>
<string name="msg_mf_subkey_change">Modifica sottochiave %s</string>
- <string name="msg_mf_error_subkey_missing">Tentativo di operare su sottochiave %s mancante!</string>
<string name="msg_mf_subkey_new_id">Nuovo ID sottochiave: %s</string>
<string name="msg_mf_error_past_expiry">La data di scadenza non può essere nel passato!</string>
<string name="msg_mf_subkey_revoke">Revoca sottochiave %s</string>
@@ -626,7 +713,6 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<!--Edit Key (higher level than modify)-->
<!--Promote key-->
<!--Other messages used in OperationLogs-->
- <string name="msg_ek_error_divert">La Modifica delle chiavi NFC non è (ancora) supportata!</string>
<string name="msg_ek_error_not_found">Chiave non trovata!</string>
<!--Messages for DecryptVerify operation-->
<string name="msg_dc_clear_meta_file">Nome file: %s</string>
@@ -637,7 +723,6 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<string name="msg_dc_clear_signature_ok">Controllo firma OK</string>
<string name="msg_dc_error_integrity_check">Errore di controllo di integrità!</string>
<string name="msg_dc_integrity_check_ok">Controllo integrità OK!</string>
- <string name="msg_dc_pass_cached">Uso della frase di accesso dalla cache</string>
<string name="msg_dc">Inizio operazione di decodifica...</string>
<string name="msg_dc_sym_skip">Dati simmetrici non permessi, proseguo...</string>
<string name="msg_dc_unlocking">Sblocco chiave segreta</string>
@@ -654,13 +739,10 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<item quantity="one">parte del file caricato e\' un oggetto OpenPGP valido, ma non una chave OpenPGP</item>
<item quantity="other">parti del file caricato sono oggetti OpenPGP validi, ma non chavi OpenPGP</item>
</plurals>
+ <!--Messages for Keybase Verification operation-->
<!--Messages for Export Log operation-->
<!--PassphraseCache-->
- <string name="passp_cache_notif_click_to_clear">Clicca per rimuovere la frase di accesso nella cache</string>
- <string name="passp_cache_notif_n_keys">OpenKeychain ha memorizzato nella cache %d frasi di accesso</string>
- <string name="passp_cache_notif_keys">Frasi di accesso memorizzate:</string>
- <string name="passp_cache_notif_clear">Pulisci Cache</string>
- <string name="passp_cache_notif_pwd">Frase di Accesso</string>
+ <!--Keyserver sync-->
<!--First Time-->
<string name="first_time_text1">Riappropriati della tua privacy con OpenKeychain!</string>
<string name="first_time_skip">Salta Installazione</string>
@@ -675,20 +757,19 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
<string name="error_key_not_found">Chiave non trovata!</string>
<string name="error_key_processing">Errore di elaborazione chiave!</string>
<string name="key_stripped">ripulito</string>
- <string name="key_no_passphrase">Nessuna Frase di Accesso</string>
<string name="key_unavailable">non disponibile</string>
<string name="secret_cannot_multiple">Le vostre chiavi possono essere eliminate solo singolarmente!</string>
<string name="title_view_cert">Visualizza Dettagli Certificati</string>
<string name="unknown_algorithm">sconosciuto</string>
<string name="can_sign_not">non può firmare</string>
<string name="error_no_encrypt_subkey">Nessuna sottochiave di codifica disponibile!</string>
- <string name="info_no_manual_account_creation">Non creare account OpenKeychain manualmente.\nPer ulteriori informazioni, vedere la Guida.</string>
<string name="contact_show_key">Mostra chiave (%s)</string>
<string name="error_no_file_selected">Seleziona almeno un file da codificare!</string>
- <string name="error_multi_not_supported">Il salvataggio di più file non è supportato. Questa è una limitazione corrente di Android.</string>
<string name="key_colon">Chiave:</string>
+ <!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
+ <string name="file_saved">File salvato!</string>
</resources>
diff --git a/OpenKeychain/src/main/res/values-ja/strings.xml b/OpenKeychain/src/main/res/values-ja/strings.xml
index 13647a1bb..02892dfb2 100644
--- a/OpenKeychain/src/main/res/values-ja/strings.xml
+++ b/OpenKeychain/src/main/res/values-ja/strings.xml
@@ -7,13 +7,12 @@
<string name="title_encrypt_text">暗号化</string>
<string name="title_encrypt_files">暗号化</string>
<string name="title_decrypt">復号化</string>
- <string name="title_unlock">鍵のロック解除</string>
<string name="title_add_subkey">副鍵の追加</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_passphrase">パスフレーズの変更</string>
+ <string name="title_key_server_preference">OpenPGP鍵サーバ</string>
+ <string name="title_change_passphrase">パスワードの変更</string>
<string name="title_share_fingerprint_with">...で指紋の共有</string>
<string name="title_share_key">...で鍵の共有</string>
<string name="title_share_file">...でファイルの共有</string>
@@ -21,8 +20,8 @@
<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_export_key">鍵のバックアップ</string>
+ <string name="title_export_keys">鍵のバックアップ</string>
<string name="title_key_not_found">鍵が見当りません</string>
<string name="title_send_key">鍵サーバへアップロード</string>
<string name="title_certify_key">鍵の確認</string>
@@ -30,33 +29,39 @@
<string name="title_help">ヘルプ</string>
<string name="title_log_display">ログ</string>
<string name="title_exchange_keys">鍵の交換</string>
- <string name="title_advanced_key_info">鍵の詳細情報</string>
+ <string name="title_advanced_key_info">拡張情報</string>
<string name="title_delete_secret_key">あなたの鍵 \'%s\' を削除しますか?</string>
<string name="title_export_log">エクスポートログ</string>
<string name="title_manage_my_keys">自分の鍵の管理</string>
<!--section-->
<string name="section_user_ids">ユーザID</string>
+ <string name="section_yubikey">YubiKey</string>
<string name="section_linked_system_contact">リンクしているシステムの連絡先</string>
<string name="section_should_you_trust">この鍵を信頼しますか?</string>
<string name="section_proof_details">証明検証</string>
<string name="section_cloud_evidence">クラウドからの証明</string>
<string name="section_keys">副鍵</string>
<string name="section_cloud_search">クラウド検索</string>
- <string name="section_passphrase_cache">パスフレーズキャッシュ</string>
+ <string name="section_passphrase_cache">パスワード/PINの取り扱い</string>
+ <string name="section_proxy_settings">プロキシの設定</string>
+ <string name="section_gui">インタフェース</string>
+ <string name="section_sync_settings">同期設定</string>
<string name="section_certify">確認</string>
<string name="section_actions">アクション</string>
<string name="section_share_key">鍵</string>
<string name="section_key_server">鍵サーバ</string>
<string name="section_fingerprint">指紋</string>
<string name="section_encrypt">暗号化</string>
- <string name="section_decrypt">復号化</string>
+ <string name="section_decrypt">復号 / 検証</string>
<string name="section_current_expiry">現在の期限</string>
<string name="section_new_expiry">新しい期限</string>
<!--button-->
<string name="btn_decrypt_verify_file">復号化と検証、そしてファイルの保存</string>
<string name="btn_encrypt_share_file">暗号化してファイルを共有</string>
<string name="btn_encrypt_save_file">暗号化してファイルを保存</string>
+ <string name="btn_save_file">ファイルの保存</string>
<string name="btn_save">保存</string>
+ <string name="btn_view_log">ログを見る</string>
<string name="btn_do_not_save">キャンセル</string>
<string name="btn_delete">削除</string>
<string name="btn_no_date">満了なし</string>
@@ -71,17 +76,21 @@
<string name="btn_view_cert_key">検証した鍵を見る</string>
<string name="btn_create_key">鍵の生成</string>
<string name="btn_add_files">ファイルの追加</string>
- <string name="btn_add_share_decrypted_text">復号化したテキストの共有</string>
- <string name="btn_decrypt_clipboard">クリップボードからテキストを復号化</string>
- <string name="btn_decrypt_and_verify">と署名の検証</string>
- <string name="btn_decrypt_files">ファイルの復号化</string>
+ <string name="btn_share_decrypted_text">復号化したテキストの共有</string>
+ <string name="btn_copy_decrypted_text">復号化したテキストのコピー</string>
+ <string name="btn_decrypt_clipboard">クリップボードから読み取り</string>
+ <string name="btn_decrypt_files">入力ファイルの選択</string>
<string name="btn_encrypt_files">ファイルの暗号化</string>
<string name="btn_encrypt_text">テキストの暗号化</string>
<string name="btn_add_email">追加のEメールアドレスを追加</string>
+ <string name="btn_unlock">アンロック</string>
+ <string name="btn_add_keyserver">追加</string>
+ <string name="btn_save_default">デフォルトとして保存</string>
+ <string name="btn_saved">保存しました!</string>
<!--menu-->
<string name="menu_preferences">設定</string>
<string name="menu_help">ヘルプ</string>
- <string name="menu_export_key">ファイルへのエクスポート</string>
+ <string name="menu_export_key">ファイルへバックアップ</string>
<string name="menu_delete_key">鍵の削除</string>
<string name="menu_manage_keys">自分の鍵の管理</string>
<string name="menu_search">検索</string>
@@ -91,19 +100,20 @@
<string name="menu_select_all">すべて選択</string>
<string name="menu_export_all_keys">すべての鍵のエクスポート</string>
<string name="menu_update_all_keys">全部のキーをアップデートする</string>
- <string name="menu_advanced">詳細情報を表示</string>
+ <string name="menu_advanced">拡張情報</string>
<string name="menu_certify_fingerprint">指紋比較による確認</string>
<string name="menu_export_log">エクスポートログ</string>
+ <string name="menu_keyserver_add">追加</string>
<!--label-->
<string name="label_message">テキスト</string>
<string name="label_file">ファイル</string>
<string name="label_files">ファイル</string>
<string name="label_file_colon">ファイル:</string>
- <string name="label_no_passphrase">パスフレーズなし</string>
- <string name="label_passphrase">パスフレーズ</string>
+ <string name="label_no_passphrase">パスワードなし</string>
+ <string name="label_passphrase">パスワード</string>
<string name="label_unlock">アンロック...</string>
- <string name="label_passphrase_again">再度パスフレーズを入力</string>
- <string name="label_show_passphrase">パスフレーズ表示</string>
+ <string name="label_passphrase_again">再度パスワードを入力</string>
+ <string name="label_show_passphrase">パスワード表示</string>
<string name="label_algorithm">アルゴリズム</string>
<string name="label_ascii_armor">アスキー形式ファイル</string>
<string name="label_file_ascii_armor">アスキー形式ファイルを有効</string>
@@ -118,13 +128,14 @@
<string name="label_delete_after_decryption">復号化後に削除</string>
<string name="label_encryption_algorithm">暗号化アルゴリズム</string>
<string name="label_hash_algorithm">ハッシュアルゴリズム</string>
- <string name="label_symmetric">パスフレーズで暗号化</string>
- <string name="label_passphrase_cache_ttl">キャッシュ時間</string>
- <string name="label_passphrase_cache_subs">副鍵のパスフレーズをキャッシュ</string>
+ <string name="label_symmetric">パスワードで暗号化</string>
+ <string name="label_passphrase_cache_ttl">時刻を忘れない</string>
+ <string name="label_passphrase_cache_subs">副鍵のパスワードを忘れない</string>
<string name="label_message_compression">テキストの圧縮</string>
<string name="label_file_compression">ファイルの圧縮</string>
- <string name="label_keyservers">鍵サーバ</string>
+ <string name="label_keyservers">OpenPGP鍵サーバを選択</string>
<string name="label_key_id">鍵ID</string>
+ <string name="label_key_created">%s で鍵を生成</string>
<string name="label_creation">生成</string>
<string name="label_expiry">満了</string>
<string name="label_usage">使い方</string>
@@ -137,15 +148,55 @@
<string name="label_send_key">クラウドによる同期</string>
<string name="label_fingerprint">指紋</string>
<string name="expiry_date_dialog_title">期限日時を設定</string>
- <string name="label_first_keyserver_is_used">(リストの最初の鍵サーバが優先されます)</string>
+ <string name="label_keyservers_title">鍵サーバ</string>
+ <string name="label_keyserver_settings_hint">ドラッグで順序変更、タップで編集/削除</string>
+ <string name="label_selected_keyserver_title">選択した鍵サーバ</string>
<string name="label_preferred">優先</string>
<string name="label_enable_compression">圧縮を有効</string>
<string name="label_encrypt_filenames">暗号化するファイル名</string>
<string name="label_hidden_recipients">受信者を隠す</string>
- <string name="pref_keyserver">キーサーバーで探す</string>
- <string name="pref_keyserver_summary">HKPキーサーバーで探す</string>
- <string name="pref_keybase">Keybase.ioで探す</string>
- <string name="pref_keybase_summary">Keybase.ioのインデックスで探す</string>
+ <string name="label_verify_keyserver">鍵サーバを検証</string>
+ <string name="label_enter_keyserver_url">鍵サーバのURLを入力</string>
+ <string name="label_keyserver_dialog_delete">鍵サーバの削除</string>
+ <string name="label_theme">テーマ</string>
+ <string name="pref_keyserver">OpenPGP鍵サーバ</string>
+ <string name="pref_keyserver_summary">選択したOpenPGP鍵サーバで鍵を探す (HKPプロトコル)</string>
+ <string name="pref_keybase">Keybase.io</string>
+ <string name="pref_keybase_summary">Keybase.ioで鍵を探す</string>
+ <string name="label_sync_settings_keyserver_title">鍵の自動アップデート</string>
+ <string name="label_sync_settings_keyserver_summary_on">1週間以上古い鍵なら鍵サーバへアップデートを問合せる</string>
+ <string name="label_sync_settings_keyserver_summary_off">鍵を自動でアップデートしない</string>
+ <string name="label_sync_settings_contacts_title">鍵の連絡先を同期する</string>
+ <string name="label_sync_settings_contacts_summary_on">オフランで完結した、鍵と連絡先のメールアドレスでの関連付けを行う</string>
+ <string name="label_sync_settings_contacts_summary_off">新しい鍵は連絡先と関連付けしない</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <string name="keyserver_sync_settings_title">鍵の自動アップデート</string>
+ <!--Proxy Preferences-->
+ <string name="pref_proxy_tor_title">Torを有効</string>
+ <string name="pref_proxy_tor_summary">Orbotのインストールが要求されます</string>
+ <string name="pref_proxy_normal_title">その他のプロキシを有効</string>
+ <string name="pref_proxy_host_title">プロキシのホスト</string>
+ <string name="pref_proxy_host_err_invalid">プロキシのホストは空にはできません</string>
+ <string name="pref_proxy_port_title">プロキシのポート</string>
+ <string name="pref_proxy_port_err_invalid">入力したポート番号が正しくない</string>
+ <string name="pref_proxy_type_title">プロキシの種別</string>
+ <!--proxy type choices and values-->
+ <string name="pref_proxy_type_choice_http">HTTP</string>
+ <string name="pref_proxy_type_choice_socks">SOCKS</string>
+ <!--OrbotHelper strings-->
+ <string name="orbot_ignore_tor">Torを使わない</string>
+ <!--InstallDialogFragment strings-->
+ <string name="orbot_install_dialog_title">Torで使うOrbotをインストールしますか?</string>
+ <string name="orbot_install_dialog_install">インストール</string>
+ <string name="orbot_install_dialog_cancel">キャンセル</string>
+ <string name="orbot_install_dialog_ignore_tor">Torを使わない</string>
+ <!--StartOrbotDialogFragment strings-->
+ <string name="orbot_start_dialog_title">Orbotを始めますか?</string>
+ <string name="orbot_start_dialog_content">Orbot は動作しても表れません。スタートしてTorに接続しますか?</string>
+ <string name="orbot_start_btn">Orbotを始める</string>
+ <string name="orbot_start_dialog_start">Orbotを始める</string>
+ <string name="orbot_start_dialog_cancel">キャンセル</string>
+ <string name="orbot_start_dialog_ignore_tor">Torを使わない</string>
<string name="user_id_no_name">&lt;名前なし&gt;</string>
<string name="none">&lt;無し&gt;</string>
<plurals name="n_keys">
@@ -177,32 +228,36 @@
<string name="filemanager_title_open">開く...</string>
<string name="error">エラー</string>
<string name="error_message">エラー: %s</string>
+ <string name="theme_dark">ダーク</string>
+ <string name="theme_light">ライト</string>
<!--key flags-->
<string name="flag_certify">証明</string>
<string name="flag_sign">署名</string>
<string name="flag_encrypt">暗号化</string>
<string name="flag_authenticate">認証</string>
<!--sentences-->
- <string name="wrong_passphrase">良くないパスフレーズ</string>
+ <string name="wrong_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="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="passphrase_for">\'%s\' にパスワードを入れてください</string>
<string name="pin_for">\'%s\' にPINを入力してください</string>
<string name="yubikey_pin_for">\'%s\' の Yubikey にアクセスするためのPINを入力してください</string>
- <string name="nfc_text">あなたのデバイスの背面にYubiKeyを固定してください。</string>
+ <string name="nfc_text">YubiKeyをあなたのデバイスの背中にあるNFCの印に対向させて固定してください。</string>
+ <string name="nfc_wait">YubiKeyを背後に維持してください!</string>
+ <string name="nfc_finished">YubiKeyを取り外してください。</string>
+ <string name="nfc_try_again_text">Yubikeyを離し、再実行を推してください。</string>
<string name="file_delete_confirmation_title">オリジナルのファイルを削除しますか?</string>
<string name="file_delete_confirmation">以下のファイルを削除します:%s</string>
<string name="file_delete_successful">%1$d 中の %2$d のファイルの削除が完了しました。%3$s</string>
- <string name="no_file_selected">最初にファイルを選択してください。</string>
+ <string name="no_file_selected">ファイルが選択されていません。</string>
<string name="encrypt_sign_successful">署名/暗号化に成功しました。</string>
<string name="encrypt_sign_clipboard_successful">クリップボードの中身の署名/暗号化に成功しました。</string>
<string name="select_encryption_key">少なくとも1つの暗号化鍵を選択して下さい。</string>
- <string name="select_encryption_or_signature_key">少なくとも1つの暗号化鍵か署名鍵を選択して下さい。</string>
+ <string name="error_no_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="key_deletion_confirmation_multi">選択したすべての鍵を本当に削除してよいですか?</string>
<string name="secret_key_deletion_confirmation">削除後はこの鍵で暗号化されたメッセージが読めなくなります、またその鍵で行われたすべての鍵確認を失います!</string>
<string name="public_key_deletetion_confirmation">鍵 \'%s\' を削除しますか?</string>
@@ -239,31 +294,35 @@
<string name="error_external_storage_not_ready">外部ストレージが準備できていません</string>
<string name="error_key_size_minimum512bit">鍵サイズは最低でも512bit必要です</string>
<string name="error_unknown_algorithm_choice">未知のアルゴリズムを選択しています</string>
- <string name="error_user_id_no_email">メールが見付かりません</string>
+ <string name="error_user_id_no_email">メールアドレスが見付かりません</string>
<string name="error_key_needs_a_user_id">最低でも1つのユーザIDが必要です</string>
- <string name="error_no_signature_passphrase">パスフレーズが与えられていません</string>
+ <string name="error_no_signature_passphrase">パスワードが与えられていません</string>
<string name="error_no_signature_key">署名鍵を与えられていません</string>
<string name="error_invalid_data">暗号化もしくは署名の検証されてないOpenPGPの内容!</string>
<string name="error_integrity_check_failed">完全性チェックが失敗しました! データに変更があります!</string>
- <string name="error_wrong_passphrase">正しくないパスフレーズです</string>
+ <string name="error_wrong_passphrase">正しくないパスワードです</string>
<string name="error_could_not_extract_private_key">秘密鍵を取り出すことができません</string>
<!--errors without preceeding Error:-->
<string name="error_jelly_bean_needed">Android NFC Beam機能を使うにはAndroid 4.1 が必要です!</string>
<string name="error_nfc_needed">NFCを有効にしてください!</string>
<string name="error_beam_needed">Beamを有効にしてください!</string>
<string name="error_nothing_import">鍵が見当りません!</string>
+ <string name="error_nothing_import_selected">インポートの鍵が選択されていません!</string>
<string name="error_contacts_key_id_missing">連絡帳から鍵IDの回収が失敗しました!</string>
<string name="error_generic_report_bug">一般エラーが発生しました、この新しいバグの情報をOpenKeychainプロジェクトに送ってください</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">未署名</string>
<string name="decrypt_result_invalid_signature">無効な署名です!</string>
- <string name="decrypt_result_signature_uncertified">署名(未検証!)</string>
- <string name="decrypt_result_signature_certified">署名</string>
- <string name="decrypt_result_signature_expired_key">鍵が期限切れ!</string>
- <string name="decrypt_result_signature_revoked_key">鍵が破棄されています!</string>
- <string name="decrypt_result_signature_missing_key">不明な公開鍵</string>
+ <string name="decrypt_result_insecure_cryptography">不正な署名 (セキュアではない暗号による)!</string>
+ <string name="decrypt_result_signature_uncertified"><b>未検証</b> の鍵で署名</string>
+ <string name="decrypt_result_signature_secret">あなたの鍵で署名</string>
+ <string name="decrypt_result_signature_certified">確認済みの鍵での署名</string>
+ <string name="decrypt_result_signature_expired_key"><b>期限切れ</b> の鍵での署名!</string>
+ <string name="decrypt_result_signature_revoked_key"><b>破棄された</b> 鍵での署名!</string>
+ <string name="decrypt_result_signature_missing_key"><b>不明な公開鍵</b> での署名</string>
<string name="decrypt_result_encrypted">暗号化</string>
<string name="decrypt_result_not_encrypted">未暗号化</string>
+ <string name="decrypt_result_insecure">セキュアではない暗号化</string>
<string name="decrypt_result_action_show">表示</string>
<string name="decrypt_result_action_Lookup">検出</string>
<string name="decrypt_invalid_text">署名が無効もしくは鍵が破棄された/期限が切れています。なのでだれかがあなたへ文書を書くなどとても出きません。まだ表示しますか?</string>
@@ -276,6 +335,7 @@
<string name="progress_cancelling">キャンセル中...</string>
<string name="progress_saving">保存...</string>
<string name="progress_importing">インポート...</string>
+ <string name="progress_revoking_uploading">鍵の破棄とアップロード...</string>
<string name="progress_updating">キーをアップデート中。。。</string>
<string name="progress_exporting">エクスポート...</string>
<string name="progress_uploading">アップロード中...</string>
@@ -296,10 +356,13 @@
<string name="progress_modify_subkeyrevoke">副鍵の破棄中...</string>
<string name="progress_modify_subkeystrip">副鍵のストリップ中...</string>
<string name="progress_modify_subkeyadd">副鍵を追加中...</string>
- <string name="progress_modify_passphrase">パスフレーズの変更中...</string>
+ <string name="progress_modify_passphrase">パスワードの変更中...</string>
+ <string name="progress_modify_pin">PINを変更...</string>
+ <string name="progress_modify_admin_pin">管理者PINを変更...</string>
<plurals name="progress_exporting_key">
<item quantity="other">鍵のエクスポート...</item>
</plurals>
+ <string name="progress_start">操作準備中...</string>
<string name="progress_extracting_signature_key">署名鍵の取り出し中...</string>
<string name="progress_extracting_key">鍵の取り出し中...</string>
<string name="progress_preparing_streams">ストリームの準備中...</string>
@@ -319,6 +382,8 @@
<string name="progress_deleting">鍵の削除中...</string>
<string name="progress_con_saving">統合: キャッシュへ保存…</string>
<string name="progress_con_reimport">統合: 再インポート中…</string>
+ <string name="progress_verifying_keyserver_url">鍵サーバの検証...</string>
+ <string name="progress_starting_orbot">Orbotを始める...</string>
<!--action strings-->
<string name="hint_cloud_search_hint">名前、Email...で検索</string>
<!--key bit length selections-->
@@ -360,11 +425,14 @@
<string name="import_tab_qr_code">QRコード/NFC</string>
<string name="import_import">選択した鍵のインポート</string>
<string name="import_qr_code_wrong">不適QRコード! もう一度!</string>
- <string name="import_qr_code_too_short_fingerprint">指紋が短かすぎます (&lt; 16 文字)</string>
+ <string name="import_qr_code_fp">指紋が正しくないか短かすぎます!</string>
+ <string name="import_qr_code_too_short_fingerprint">指紋が短かすぎます!</string>
<string name="import_qr_code_button">QCコードのスキャン</string>
<string name="import_qr_code_text">カメラをQRコードにかざしてください!</string>
+ <!--Import from URL-->
+ <string name="import_url_warn_no_search_parameter">検索要求が定義されていません。手動で鍵サーバで検索を試みることができます。</string>
<!--Generic result toast-->
- <string name="view_log">概要</string>
+ <string name="snackbar_details">詳細</string>
<string name="with_warnings">、とワーニング</string>
<string name="with_cancelled">、キャンセルされるまで</string>
<!--Import result toast-->
@@ -403,6 +471,11 @@
</plurals>
<string name="delete_nothing">削除するものがありません。</string>
<string name="delete_cancelled">削除操作をキャンセルしました。</string>
+ <!--Revoke result toast (snackbar)-->
+ <string name="revoke_ok">鍵の破棄に成功しました。</string>
+ <string name="revoke_fail">鍵の破棄エラー!</string>
+ <string name="revoke_nothing">破棄するものがありません。</string>
+ <string name="revoke_cancelled">破棄操作をキャンセルしました。</string>
<!--Certify result toast-->
<plurals name="certify_keys_ok">
<item quantity="other">%1$d 個の鍵 %2$s の検証に成功。</item>
@@ -419,10 +492,10 @@
<string name="intent_send_encrypt">OpenKeychainで暗号化</string>
<string name="intent_send_decrypt">OpenKeychainで復号化</string>
<!--Remote API-->
- <string name="api_settings_show_info">詳細情報を表示</string>
- <string name="api_settings_hide_info">詳細情報を非表示</string>
+ <string name="api_settings_show_info">拡張情報を表示</string>
+ <string name="api_settings_hide_info">拡張情報を非表示</string>
<string name="api_settings_show_advanced">拡張設定を表示</string>
- <string name="api_settings_hide_advanced">拡張設定を隠す</string>
+ <string name="api_settings_hide_advanced">拡張設定を非表示</string>
<string name="api_settings_no_key">鍵が選択されていない</string>
<string name="api_settings_select_key">鍵の選択</string>
<string name="api_settings_create_key">新しい鍵の生成</string>
@@ -433,9 +506,9 @@
<string name="api_settings_start">アプリケーションを開始</string>
<string name="api_settings_delete_account">アカウントを削除</string>
<string name="api_settings_package_name">パッケージ名</string>
- <string name="api_settings_package_signature">パッケージの署名 SHA-256</string>
- <string name="api_settings_accounts">アカウント(deprecated API)</string>
- <string name="api_settings_advanced">詳細情報</string>
+ <string name="api_settings_package_certificate">パッケージの署名 SHA-256</string>
+ <string name="api_settings_accounts">アカウント(古いAPI)</string>
+ <string name="api_settings_advanced">拡張情報</string>
<string name="api_settings_allowed_keys">受け入れる鍵</string>
<string name="api_settings_settings">設定</string>
<string name="api_settings_key">アカウント鍵:</string>
@@ -451,15 +524,26 @@
<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_missing_text">このメールアドレスの鍵が見付からない</string>
+ <string name="api_select_pub_keys_dublicates_text">このメールアドレスについて1つ以上の鍵が存在します:</string>
<string name="api_select_pub_keys_text">受信者リストを確認してください!</string>
<string name="api_select_pub_keys_text_no_user_ids">受信者を選択してください!</string>
<string name="api_error_wrong_signature">署名チェックが失敗! 違うところからこのアプリをインストールしましたか? もし攻撃されてでなくそうであるなら、OpenKeychainにあるこのアプリの登録を破棄し、再度アプリを登録してください。</string>
<string name="api_select_sign_key_text">すでにある鍵1つを選択するか新規に作成してください。</string>
+ <string name="api_select_keys_text">この内容を復号化することができる許可された鍵がありません。許可する鍵を選択してください。</string>
<!--Share-->
<string name="share_qr_code_dialog_title">QRコードで共有</string>
<string name="share_nfc_dialog">NFCで共有</string>
+ <!--retry upload dialog-->
+ <string name="retry_up_dialog_title">アップロード失敗</string>
+ <string name="retry_up_dialog_btn_reupload">操作再実行</string>
+ <string name="retry_up_dialog_btn_cancel">操作取り止め</string>
+ <!--Delete or revoke private key dialog-->
+ <string name="del_rev_dialog_btn_revoke">破棄とアップロード</string>
+ <string name="del_rev_dialog_btn_delete">削除のみ</string>
+ <!--Delete Or Revoke Dialog spinner-->
+ <string name="del_rev_dialog_choice_delete">削除のみ</string>
+ <string name="del_rev_dialog_choice_rev_upload">破棄とアップロード</string>
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="other">%d の鍵を選択。</item>
@@ -501,14 +585,14 @@
<string name="key_trust_results_prefix">Keybase.ioはこのキーのオーナーだと言う証拠を提供している:</string>
<string name="key_trust_header_text">注意:Keybase.ioのオーナー証拠は実験的な機会です。キーを確認することに加えて、QRコードや、NFCでキーを交換するのもお勧めする。</string>
<!--keybase proof stuff-->
- <string name="keybase_narrative_twitter">Twitterへ以下のIDで投稿</string>
- <string name="keybase_narrative_github">Githubでは以下のIDで知られています</string>
- <string name="keybase_narrative_dns">制御下にあるドメイン名(たち)</string>
- <string name="keybase_narrative_web_site">サイトに投稿できる</string>
- <string name="keybase_narrative_reddit">Redditへ以下のIDで投稿</string>
- <string name="keybase_narrative_coinbase">Coinbaseでは以下で知られています</string>
- <string name="keybase_narrative_hackernews">Hacker Newsへ以下のIDで投稿</string>
- <string name="keybase_narrative_unknown">不明な確認種別</string>
+ <string name="keybase_narrative_twitter">Twitterへ以下のIDで投稿 %s</string>
+ <string name="keybase_narrative_github">Githubでは %s で知られています</string>
+ <string name="keybase_narrative_dns">ドメイン名 %s を制御しています</string>
+ <string name="keybase_narrative_web_site">Webサイト %s に投稿できます</string>
+ <string name="keybase_narrative_reddit">Redditへ以下のIDで投稿 %s</string>
+ <string name="keybase_narrative_coinbase">Conbaseでは %s で知られています</string>
+ <string name="keybase_narrative_hackernews">Hacker Newsへ以下のIDで投稿 %s</string>
+ <string name="keybase_narrative_unknown">不明な検証種別 %s</string>
<string name="keybase_proof_failure">不幸にもこの証明は検証されていません。</string>
<string name="keybase_unknown_proof_failure">証明チェッカで正当に評価されない問題</string>
<string name="keybase_problem_fetching_evidence">確認に問題があります</string>
@@ -530,7 +614,7 @@
<string name="keybase_reddit_attribution">Redditの属性</string>
<string name="keybase_verify">検証</string>
<!--Edit key-->
- <string name="edit_key_action_change_passphrase">パスフレーズの変更</string>
+ <string name="edit_key_action_change_passphrase">パスワードの変更</string>
<string name="edit_key_action_add_identity">ユーザIDの追加</string>
<string name="edit_key_action_add_subkey">副鍵の追加</string>
<string name="edit_key_edit_user_id_title">アクションを選んでください!</string>
@@ -547,27 +631,38 @@
<item>期限の変更</item>
<item>副鍵の破棄</item>
<item>副鍵のストリップ</item>
+ <item>副鍵をYubiKey/スマートカードへ移動</item>
</string-array>
<string name="edit_key_new_subkey">新しい副鍵</string>
<string name="edit_key_select_flag">最低1つフラグを選択してください!</string>
<string name="edit_key_error_add_identity">最低でも1つのユーザIDを追加!</string>
<string name="edit_key_error_add_subkey">最低でも1つの副鍵を追加!</string>
+ <string name="edit_key_error_bad_nfc_algo">スマートカードではアルゴリズムをサポートしません!</string>
+ <string name="edit_key_error_bad_nfc_size">スマートカードでは鍵サイズをサポートしません!</string>
+ <string name="edit_key_error_bad_nfc_stripped">鍵をスマートカードに移動できません(ストリップしてあるか、\'カードへ迂回\'がない)</string>
<!--Create key-->
<string name="create_key_upload">クラウドとの同期</string>
<string name="create_key_empty">このフィールドは必須です</string>
- <string name="create_key_passphrases_not_equal">パスフレーズが一致しない</string>
+ <string name="create_key_passphrases_not_equal">パスワードが一致しない</string>
<string name="create_key_final_text">あたなが入力したIDは以下です:</string>
<string name="create_key_final_robot_text">しばらくの間鍵を生成しています、その間はコーヒーでもどうぞ....</string>
<string name="create_key_rsa">(3副鍵、RSA, 4096 bit)</string>
<string name="create_key_custom">(個別の鍵設定)</string>
<string name="create_key_name_text">この鍵に紐付ける名前を選択してください。これにはフルネーム、例えば「山田太郎」かニックネーム、例えば「たろすけ」にできます。</string>
<string name="create_key_email_text">あなたが秘密の通信で使うメインのEメールアドレスを入力してください。</string>
- <string name="create_key_passphrase_text">強度の高いパスフレーズを選択してください。そうすることであなたのデバイスが盗まれてもあなたの鍵を守ります。</string>
+ <string name="create_key_passphrase_text">強度の高いパスワードを選択してください。そうすることであなたのデバイスが盗まれてもあなたの鍵を守ります。</string>
<string name="create_key_hint_full_name">フルネームかニックネーム</string>
<string name="create_key_edit">鍵の設定変更</string>
<string name="create_key_add_email">Eメールアドレスの追加</string>
<string name="create_key_add_email_text">追加のEメールアドレスがこの鍵に紐付きそしてセキュアな通信に使うことができます。</string>
- <string name="create_key_email_already_exists_text">メールはすでに追加している</string>
+ <string name="create_key_email_already_exists_text">メールアドレスがすでに追加されている</string>
+ <string name="create_key_email_invalid_email">メールアドレスのフォーマットが無効です</string>
+ <string name="create_key_yubi_key_pin">PIN</string>
+ <string name="create_key_yubi_key_admin_pin">管理者PIN</string>
+ <string name="create_key_yubi_key_pin_repeat_text">処理を続けるためPINおよび管理者PINの入力をしてください。</string>
+ <string name="create_key_yubi_key_pin_repeat">再度PINを入力</string>
+ <string name="create_key_yubi_key_admin_pin_repeat">再度管理者PINを入力</string>
+ <string name="create_key_yubi_key_pin_not_correct">PINが正しくありません!</string>
<!--View key-->
<string name="view_key_revoked">破棄: 鍵はもう使われません!</string>
<string name="view_key_expired">期限切れ: この連絡先は鍵の妥当性を拡張する必要があります!</string>
@@ -576,6 +671,14 @@
<string name="view_key_verified">確認済みの鍵</string>
<string name="view_key_unverified">未確認: QRコードをスキャンして鍵を確認!</string>
<string name="view_key_fragment_no_system_contact">&lt;なし&gt;</string>
+ <!--Add/Edit keyserver-->
+ <string name="add_keyserver_dialog_title">鍵サーバを追加</string>
+ <string name="edit_keyserver_dialog_title">鍵サーバの編集</string>
+ <string name="add_keyserver_verified">鍵サーバを検証しました!</string>
+ <string name="add_keyserver_without_verification">鍵サーバを検証なしで追加した。</string>
+ <string name="add_keyserver_invalid_url">無効なURLです!</string>
+ <string name="add_keyserver_connection_failed">鍵サーバへの接続し失敗。URLとあなたのインターネット接続をチェックしてください。</string>
+ <string name="keyserver_preference_deleted">%s を削除</string>
<!--Navigation Drawer-->
<string name="nav_keys">鍵</string>
<string name="nav_encrypt_decrypt">暗号化/復号化</string>
@@ -583,6 +686,7 @@
<string name="drawer_open">ナビゲーションドロワーを開く</string>
<string name="drawer_close">ナビゲーションドロワーを閉める</string>
<string name="my_keys">自分の鍵</string>
+ <string name="nav_backup">バックアップ</string>
<!--hints-->
<string name="encrypt_content_edit_text_hint">テキストを入力</string>
<!--certs-->
@@ -700,10 +804,10 @@
<string name="msg_is_pubring_generate">秘密鍵の鍵輪から公開鍵の鍵輪を生成中</string>
<string name="msg_is_subkey_nonexistent">秘密鍵の副鍵 %s が利用不可能</string>
<string name="msg_is_subkey_ok">秘密鍵の副鍵 %s を利用可能としてマーク</string>
- <string name="msg_is_subkey_empty">秘密鍵の副鍵 %s を利用可能としてマーク、空のパスフレーズで</string>
+ <string name="msg_is_subkey_empty">空のパスワードで秘密鍵の副鍵 %s を利用可能としてマーク</string>
<string name="msg_is_subkey_pin">秘密鍵の副鍵 %s をPINとして利用可能とマーク</string>
<string name="msg_is_subkey_stripped">秘密鍵の副鍵 %s をストリップとしてマーク</string>
- <string name="msg_is_subkey_divert">秘密鍵の副鍵 %s を\'スマートカード/NFCへ迂回\'としてマーク</string>
+ <string name="msg_is_subkey_divert">秘密副鍵 %s を\'カードへ迂回\' としてマーク</string>
<string name="msg_is_success_identical">鍵輪にデータがないため、なにもしません</string>
<string name="msg_is_success">秘密鍵の鍵輪のインポートに成功</string>
<!--Keyring Canonicalization log entries-->
@@ -730,6 +834,7 @@
<string name="msg_kc_sub_bad_local">\'ローカル\'フラグ付きの証明が付随する副鍵を破棄中</string>
<string name="msg_kc_sub_bad_keyid">副鍵の発行者のIDと付随するIDがミスマッチ</string>
<string name="msg_kc_sub_bad_time">未来にタイムスタンプがある証明が付随する副鍵を破棄中</string>
+ <string name="msg_kc_sub_bad_time_early">副鍵と結びついている証明は鍵のタイムスタンプより前の日時です!</string>
<string name="msg_kc_sub_bad_type">不明な検証のタイプ: %sの副鍵</string>
<string name="msg_kc_sub_dup">証明が付随する重複する副鍵を破棄中</string>
<string name="msg_kc_sub_primary_bad">付随する主たる証明が無効であるものが付随する副鍵を破棄中</string>
@@ -804,6 +909,7 @@
<string name="msg_cr_error_flags_ecdh">問題のある鍵フラグが選択されています、楕円曲線DHは署名に使えません!</string>
<!--modifySecretKeyRing-->
<string name="msg_mr">鍵輪 %s を変更中</string>
+ <string name="msg_mf_divert">カードでの暗号化操作に切り替えられます</string>
<string name="msg_mf_error_divert_serial">カードに対比した鍵のシリアル番号には16バイトは必要です!これはプロラグラムエラーで、バグレポートでファイルの提出をお願いします!</string>
<string name="msg_mf_error_encode">エンコード例外!</string>
<string name="msg_mf_error_fingerprint">現実の鍵指紋が想定の1つと合致しませんでした!</string>
@@ -812,28 +918,40 @@
<string name="msg_mf_error_master_none">マスター認証が操作で見付かりませんでした(すべて破棄しましたか?)</string>
<string name="msg_mf_error_noexist_primary">問題のある主ユーザIDが指定された!</string>
<string name="msg_mf_error_noexist_revoke">破棄において問題のあるユーザIDが指定された!</string>
- <string name="msg_mf_error_restricted">パスフレーズのない厳密な捜査を実行してみてください!それはプロラミングエラーで、バグレポートでファイルの提出をお願いします!</string>
+ <string name="msg_mf_error_restricted">パスパスワードのない厳密な操作を実行してみてください!これはプロラミングエラーで、バグレポートでファイルの提出をお願いします!</string>
<string name="msg_mf_error_revoked_primary">主ユーザIDの破棄はできません!</string>
<string name="msg_mf_error_null_expiry">副鍵の生成時に期限を\"過去\"とすることはできません。これはプログラムエラーで、バグレポートでファイルの提出をお願いします!</string>
+ <string name="msg_mf_error_noop">なにもできません!</string>
<string name="msg_mf_error_passphrase_master">主鍵の復号で致命的な失敗! これはプログラムエラーの場合がありますので、バグレポートでファイルの提出をお願いします!</string>
<string name="msg_mf_error_pgp">PGP内部エラー!</string>
<string name="msg_mf_error_sig">署名例外!</string>
+ <string name="msg_mf_error_sub_stripped">ストリップした副鍵である %s は変更できません!</string>
+ <string name="msg_mf_error_subkey_missing">見付からない副鍵 %s の操作をしようとした!</string>
+ <string name="msg_mf_error_conflicting_nfc_commands">カード上で鍵の署名を作るのと同じ操作でスマートカードに移動できません</string>
+ <string name="msg_mf_error_duplicate_keytocard_for_slot">スマートカードでは鍵の種別ごとに1つのスロットのみサポートします。</string>
+ <string name="msg_mf_error_invalid_flags_for_keytocard">スマートカードの鍵に不適合な鍵のフラグです。</string>
<string name="msg_mf_master">マスター認証を変更</string>
<string name="msg_mf_notation_empty">空のノーテーションパケットを追加</string>
<string name="msg_mf_notation_pin">PINノーテーションパケットを追加</string>
- <string name="msg_mf_passphrase">鍵輪のパスフレーズの変更中</string>
- <string name="msg_mf_passphrase_key">副鍵 %s を新しいパスフレーズで再暗号化</string>
- <string name="msg_mf_passphrase_empty_retry">新しいパスフレーズの設定に失敗しました、空の古いパスフレーズで再度試してください</string>
- <string name="msg_mf_passphrase_fail">副鍵のパスフレーズは変更されていません! (他の鍵とは異なるになっていませんか?)</string>
+ <string name="msg_mf_passphrase">鍵輪のパスワードを変更中</string>
+ <string name="msg_mf_pin">カードのPINを変更</string>
+ <string name="msg_mf_admin_pin">カードの管理者PINを変更</string>
+ <string name="msg_mf_passphrase_key">副鍵 %s を新しいパスワードで再暗号化</string>
+ <string name="msg_mf_passphrase_empty_retry">新しいパスワードの設定に失敗しました、空の古いパスフレーズで再度試してください</string>
+ <string name="msg_mf_passphrase_fail">副鍵のパスワードは変更されていません! (他の鍵とは異なっていませんか?)</string>
<string name="msg_mf_primary_replace_old">以前の主ユーザIDで証明を入れ替え中</string>
<string name="msg_mf_primary_new">新しい主ユーザIDで新しい証明を生成中</string>
+ <string name="msg_mf_restricted_mode">制限操作モードへ変更</string>
<string name="msg_mf_subkey_change">副鍵 %s を変更中</string>
- <string name="msg_mf_error_subkey_missing">見付からない副鍵 %s の操作をしようとした!</string>
+ <string name="msg_mf_require_divert">スマートカードでの暗号化操作に切り替え</string>
+ <string name="msg_mf_require_passphrase">操作にパスワードが必要です</string>
<string name="msg_mf_subkey_new">種類%sの新しい副鍵を追加</string>
<string name="msg_mf_subkey_new_id">新しい副鍵 ID: %s</string>
<string name="msg_mf_error_past_expiry">期限切れ日を過去にはできません!</string>
<string name="msg_mf_subkey_revoke">副鍵 %s を破棄中</string>
<string name="msg_mf_subkey_strip">副鍵 %s のストリップ中</string>
+ <string name="msg_mf_keytocard_start">副鍵 %s をスマートカードへ移動</string>
+ <string name="msg_mf_keytocard_finish">%1$s をスマートカード %2$s へ移動した</string>
<string name="msg_mf_success">鍵輪の変更に成功</string>
<string name="msg_mf_uid_add">ユーザID %s を追加中</string>
<string name="msg_mf_uid_primary">主UIDを %s に変更中</string>
@@ -876,19 +994,20 @@
<string name="msg_con_warn_delete_secret">秘密鍵のキャッシュファイルを削除中に例外発生</string>
<!--Edit Key (higher level than modify)-->
<string name="msg_ed">キー操作の実行</string>
- <string name="msg_ed_caching_new">新しいパスフレーズをキャッシュ</string>
+ <string name="msg_ed_caching_new">新しいパスワードをキャッシュ</string>
<string name="msg_ed_error_no_parcel">SaveKeyringParcel欠落!(これはバグです、レポートしてください)</string>
<string name="msg_ed_error_key_not_found">鍵が見当りません!</string>
<string name="msg_ed_fetching">フェッチした鍵を変更 (%s)</string>
<string name="msg_ed_success">鍵の操作に成功</string>
<!--Promote key-->
<string name="msg_pr">公開鍵が秘密鍵に昇格しました</string>
- <string name="msg_pr_error_already_secret">鍵はすでに秘密鍵となっています!</string>
+ <string name="msg_pr_all">副鍵すべてを昇格</string>
<string name="msg_pr_error_key_not_found">鍵が見当りません!</string>
<string name="msg_pr_fetching">フェッチした鍵を変更 (%s)</string>
+ <string name="msg_pr_subkey_match">副鍵を昇格: %s</string>
+ <string name="msg_pr_subkey_nomatch">YubiKeyに副鍵がありません: %s</string>
<string name="msg_pr_success">鍵は正常に昇格しました</string>
<!--Other messages used in OperationLogs-->
- <string name="msg_ek_error_divert">NFCの鍵の編集は(まだ)サポートされていません!</string>
<string name="msg_ek_error_dummy">ストリップした主鍵では鍵輪を編集できません!</string>
<string name="msg_ek_error_not_found">鍵が見当りません!</string>
<!--Messages for DecryptVerify operation-->
@@ -904,26 +1023,25 @@
<string name="msg_dc_clear_meta_size_unknown">ファイルサイズが不明</string>
<string name="msg_dc_clear_meta_time">更新日時: %s</string>
<string name="msg_dc_clear_signature_bad">署名の確認がOKではありません!</string>
- <string name="msg_dc_error_unsupported_hash_algo">サポート外かつセキュアでない可能性があるハッシュアルゴリズム!</string>
<string name="msg_dc_clear_signature_check">署名データの検証中</string>
<string name="msg_dc_clear_signature_ok">署名の確認はOKです</string>
<string name="msg_dc_clear_signature">後程署名データを保存します</string>
<string name="msg_dc_clear">平文データの処理中</string>
- <string name="msg_dc_error_bad_passphrase">鍵のロック解除エラー、パスフレーズに問題があります!</string>
+ <string name="msg_dc_error_bad_passphrase">鍵のロック解除エラー、パスワードに問題があります!</string>
+ <string name="msg_dc_error_corrupt_data">データが破損しています!</string>
<string name="msg_dc_error_extract_key">鍵のロック解除で不明なエラー!</string>
<string name="msg_dc_error_integrity_check">完全性チェックエラー!</string>
- <string name="msg_dc_error_integrity_missing">完全聖チェックの欠落!これは暗号化アプリケーションが期限切れになった場合、もしくは暗号強度低下攻撃がある場合に発生します。</string>
- <string name="msg_dc_error_invalid_siglist">正常な署名データが見付からなかった!</string>
- <string name="msg_dc_error_io">操作中にIO例外に当たりました!</string>
+ <string name="msg_dc_error_invalid_data">正常ではないOpenPGPの暗号化か署名のデーータを検出しました!</string>
+ <string name="msg_dc_error_input">入力データストリームのオープンエラー!</string>
<string name="msg_dc_error_no_data">ストリーム中に暗号化されたデータが見付からなかった!</string>
<string name="msg_dc_error_no_key">ストリーム中に既知の秘密鍵で暗号化されたデータが見付からなかった!</string>
<string name="msg_dc_error_pgp_exception">操作中にPGP例外に当たりました!</string>
<string name="msg_dc_integrity_check_ok">完全性チェックOK!</string>
<string name="msg_dc_ok_meta_only">メタデータだけが要求され、暗号化をスキップしました</string>
<string name="msg_dc_ok">復号化/検証完了</string>
- <string name="msg_dc_pass_cached">キャッシュからパスフレーズを利用します。</string>
+ <string name="msg_dc_pass_cached">キャッシュからパスワードを利用します。</string>
<string name="msg_dc_pending_nfc">NFCトークンが必要、ユーザー入力を要求中...</string>
- <string name="msg_dc_pending_passphrase">パスフレーズが必要、ユーザー入力を要求中...</string>
+ <string name="msg_dc_pending_passphrase">パスワードが必要、ユーザー入力を要求中...</string>
<string name="msg_dc_prep_streams">暗号化のストリームの検証</string>
<string name="msg_dc">復号化操作を開始します...</string>
<string name="msg_dc_sym_skip">受け入れできない対称暗号データです、スキップします...</string>
@@ -932,7 +1050,6 @@
<string name="msg_dc_trail_sym">追跡で遭遇、対称暗号化されたデータ</string>
<string name="msg_dc_trail_unknown">追跡で未知のタイプのデータに遭遇</string>
<string name="msg_dc_unlocking">秘密鍵のロック解除</string>
- <string name="msg_dc_old_symmetric_encryption_algo">セキュアでない可能性がある暗号化アルゴリズムが利用されています!</string>
<!--Messages for VerifySignedLiteralData operation-->
<string name="msg_vl">署名の確認開始</string>
<string name="msg_vl_error_no_siglist">署名済み固定データに署名リストがありません</string>
@@ -953,20 +1070,18 @@
<string name="msg_se_error_input_uri_not_found">読み出すためにURIを開く時にエラー!</string>
<string name="msg_se_error_output_uri_not_found">書き込むためにURIを開く時にエラー!</string>
<string name="msg_se_error_too_many_inputs">不明な出力以上の入力過多です! これはプログラミングのエラーで、バグレポートの提出をお願いします!</string>
- <string name="msg_se_warn_output_left">得られた出力が入力から乖離している。これはプログラミングのエラーで、バグレポートの提出をお願いします!</string>
<string name="msg_se_success">署名/暗号化操作に成功!</string>
<!--Messages for PgpSignEncrypt operation-->
<string name="msg_pse_asymmetric">暗号化のための公開鍵の準備</string>
<string name="msg_pse_clearsign_only">クリアテキスト署名の入力はサポートされていません!</string>
<string name="msg_pse_compressing">圧縮の準備</string>
<string name="msg_pse_encrypting">データ暗号化</string>
- <string name="msg_pse_error_bad_passphrase">駄目なパスフレーズ!</string>
- <string name="msg_pse_error_hash_algo">この鍵ではサポートされていないハッシュアルゴリズムを要求されています!</string>
+ <string name="msg_pse_error_bad_passphrase">駄目なパスワード!</string>
<string name="msg_pse_error_io">操作中にIO例外に当たりました!</string>
<string name="msg_pse_error_key_sign">選択した署名鍵で署名データを作れません!</string>
<string name="msg_pse_error_sign_key">署名鍵の取得エラー!</string>
<string name="msg_pse_error_nfc">NFC データエラー!</string>
- <string name="msg_pse_error_no_passphrase">パスフレーズが提供されてない!</string>
+ <string name="msg_pse_error_no_passphrase">パスワードが提供されてない!</string>
<string name="msg_pse_error_pgp">OpenPGP内部エラー!</string>
<string name="msg_pse_error_sig">OpenPGP署名例外に当たりました!</string>
<string name="msg_pse_error_unlock">鍵のロック解除で不明なエラー!</string>
@@ -975,7 +1090,7 @@
<string name="msg_pse_key_warn">暗号化に問題のある鍵: %s</string>
<string name="msg_pse_ok">署名/暗号化操作に成功!</string>
<string name="msg_pse_pending_nfc">NFCトークンが必要、ユーザー入力を要求中...</string>
- <string name="msg_pse_pending_passphrase">パスフレーズが必要、ユーザー入力を要求中...</string>
+ <string name="msg_pse_pending_passphrase">パスワードが必要、ユーザー入力を要求中...</string>
<string name="msg_pse_signing">署名データ(暗号化なし)</string>
<string name="msg_pse_signing_cleartext">クリアテキスト署名作成中</string>
<string name="msg_pse_signing_detached">分離署名作成中</string>
@@ -983,13 +1098,19 @@
<string name="msg_pse">署名/暗号化操作を開始します</string>
<string name="msg_pse_symmetric">対称暗号の準備</string>
<string name="msg_crt_certifying">検証の生成中</string>
+ <plurals name="msg_crt_certify_uids">
+ <item quantity="other">%1$d のユーザIDで鍵 %2$s の検証中</item>
+ </plurals>
+ <plurals name="msg_crt_certify_uats">
+ <item quantity="other">%1$d のユーザ属性で鍵 %2$s の検証中</item>
+ </plurals>
<string name="msg_crt_error_self">自己証明書的な発行は行えません!</string>
<string name="msg_crt_error_master_not_found">主鍵が見当りません!</string>
<string name="msg_crt_error_nothing">検証できた鍵がない!</string>
<string name="msg_crt_error_unlock">主鍵のロック解除エラー!</string>
- <string name="msg_crt_error_divert">NFCでの検証は(まだ)サポートしていません!</string>
<string name="msg_crt">鍵輪の検証</string>
<string name="msg_crt_master_fetch">検証中の主鍵のフェッチ</string>
+ <string name="msg_crt_nfc_return">NFCの画面に復帰</string>
<string name="msg_crt_save">検証した鍵の保存中 %s</string>
<string name="msg_crt_saving">鍵輪の保存中</string>
<string name="msg_crt_unlock">主鍵のロック解除</string>
@@ -997,6 +1118,7 @@
<string name="msg_crt_warn_not_found">鍵が見当りません!</string>
<string name="msg_crt_warn_cert_failed">証明の生成が失敗!</string>
<string name="msg_crt_warn_save_failed">保存操作が失敗!</string>
+ <string name="msg_crt_warn_upload_failed">アップロード操作に失敗!</string>
<string name="msg_crt_upload_success">鍵をサーバにアップロードしました</string>
<plurals name="msg_import">
<item quantity="other">%d 個の鍵のインポート</item>
@@ -1004,13 +1126,14 @@
<string name="msg_import_fetch_error_decode">鍵輪のデコードエラー</string>
<string name="msg_import_fetch_error">鍵の展開ができません! (ネットワークの問題?)</string>
<string name="msg_import_fetch_keybase">keybase.ioから回収: %s</string>
- <string name="msg_import_fetch_keyserver_error">keybaseからの鍵の展開ができません!</string>
+ <string name="msg_import_fetch_keyserver_error">鍵サーバからの展開: %s</string>
<string name="msg_import_fetch_keyserver">鍵サーバからの回収: %s</string>
<string name="msg_import_fetch_keyserver_ok">鍵の展開に成功</string>
<string name="msg_import_keyserver">鍵サーバ %s を使う</string>
<string name="msg_import_fingerprint_error">フェッチした鍵の鍵指紋が完全には一致しませんでした!</string>
<string name="msg_import_fingerprint_ok">指紋チェックOK!</string>
<string name="msg_import_merge">展開したデータをマージ</string>
+ <string name="msg_import_merge_error">展開したデータのマージでエラー!</string>
<string name="msg_import_error">インポート操作に失敗!</string>
<string name="msg_import_error_io">I/Oエラーによりインポート操作が失敗しました!</string>
<string name="msg_import_partial">インポート操作に成功、ただしエラーあり!</string>
@@ -1030,6 +1153,7 @@
<string name="msg_export_error_io">入出力エラー!</string>
<string name="msg_export_error_key">鍵データの事前処理のエラー!</string>
<string name="msg_export_success">エクスポート操作に成功!</string>
+ <string name="msg_export_upload_success">鍵サーバへアップロードに成功</string>
<string name="msg_del_error_empty">削除するものがありません!</string>
<string name="msg_del_error_multi_secret">秘密鍵は個別にしか削除できません!</string>
<plurals name="msg_del">
@@ -1044,6 +1168,11 @@
<plurals name="msg_del_fail">
<item quantity="other">%d 個の鍵の削除に失敗</item>
</plurals>
+ <string name="msg_revoke_error_empty">破棄するものがありません!</string>
+ <string name="msg_revoke_error_not_found">破棄する鍵が見付かりません!</string>
+ <string name="msg_revoke_key">鍵 %s を破棄中</string>
+ <string name="msg_revoke_key_fail">鍵の破棄に失敗しました</string>
+ <string name="msg_revoke_ok">鍵の破棄に成功しました</string>
<string name="msg_acc_saved">アカウント保存</string>
<string name="msg_download_success">ダウンロードに成功しました!</string>
<string name="msg_download_no_valid_keys">ファイル/クリップボードにて正しい鍵が見付かりません!</string>
@@ -1055,6 +1184,13 @@
<string name="msg_download_too_many_responses">鍵検索のクエリが沢山の候補を返しました。クエリを精密化してください!</string>
<string name="msg_download_query_too_short_or_too_many_responses">鍵がまったく無いか、多すぎる鍵が見付かりました。クエリを改善してください!</string>
<string name="msg_download_query_failed">鍵の検索時にエラーが発生しました。</string>
+ <!--Messages for Keybase Verification operation-->
+ <string name="msg_keybase_error_no_prover">%s で検証チェッカを見付けることができませんでした</string>
+ <string name="msg_keybase_error_fetching_evidence">検証の取得で問題がある</string>
+ <string name="msg_keybase_error_key_mismatch">鍵の指紋が証明ポストと一致しませんでした</string>
+ <string name="msg_keybase_error_dns_fail">DNS TXT レコードの検索が失敗</string>
+ <string name="msg_keybase_error_specific">%s</string>
+ <string name="msg_keybase_error_msg_payload_mismatch">復号化した検証ポストが指定した値と一致しない</string>
<!--Messages for Export Log operation-->
<string name="msg_export_log_start">ログのエクスポート</string>
<string name="msg_export_log_error_fopen">ファイルオープン中のエラー</string>
@@ -1062,17 +1198,24 @@
<string name="msg_export_log_error_writing">ファイルへの書き込みでI/Oエラー!</string>
<string name="msg_export_log_success">ログのエクスポートに成功しました!</string>
<!--PassphraseCache-->
- <string name="passp_cache_notif_click_to_clear">クリックしてパスフレーズのキャッシュをクリア</string>
- <string name="passp_cache_notif_n_keys">OpenKeychainは %d のパスフレーズをキャッシュしています</string>
- <string name="passp_cache_notif_keys">パスフレーズのキャッシュ:</string>
- <string name="passp_cache_notif_clear">キャッシュクリア</string>
- <string name="passp_cache_notif_pwd">パスフレーズ</string>
+ <string name="passp_cache_notif_click_to_clear">タッチしてパスワードをクリア。</string>
+ <plurals name="passp_cache_notif_n_keys">
+ <item quantity="other">%d のパスワードを忘れない</item>
+ </plurals>
+ <string name="passp_cache_notif_keys">パスワードを忘れない</string>
+ <string name="passp_cache_notif_clear">パスワードのクリア</string>
+ <string name="passp_cache_notif_pwd">パスワード</string>
+ <!--Keyserver sync-->
<!--First Time-->
<string name="first_time_text1">OpenKeychainであなたのプライバシーを取り戻しましょう!</string>
<string name="first_time_create_key">自分のキーを作る</string>
<string name="first_time_import_key">ファイルから鍵をインポート</string>
<string name="first_time_yubikey">YubiKey NEOを使用する</string>
<string name="first_time_skip">セットアップをスキップ</string>
+ <string name="first_time_blank_yubikey_yes">YubiKeyを使用する</string>
+ <string name="backup_all">すべての鍵とあなた所有の鍵</string>
+ <string name="backup_public_keys">すべての鍵</string>
+ <string name="backup_section">バックアップ</string>
<!--unsorted-->
<string name="section_certifier_id">検証者</string>
<string name="section_cert">証明の詳細</string>
@@ -1081,6 +1224,7 @@
<string name="empty_certs">この鍵に証明がない</string>
<string name="certs_text">あなたの検証された自己証明とあなたの鍵で生成された証明がここに表示されます</string>
<string name="section_uids_to_certify">ユーザID</string>
+ <string name="certify_text">インポートした鍵には\"アイデンティティ\": 名前とメールアドレス を含みます。正確に期待したものと一致しているか認定されているものを選択します。</string>
<string name="certify_fingerprint_text">表示している指紋を、文字対文字で、あなたのパートナーの表示しているものと比較</string>
<string name="certify_fingerprint_text2">表示している指紋は一致しましたか?</string>
<string name="label_revocation">破棄の理由</string>
@@ -1088,41 +1232,42 @@
<string name="error_key_not_found">鍵が見当りません!</string>
<string name="error_key_processing">鍵処理中のエラー!</string>
<string name="key_stripped">スリム化</string>
- <string name="key_divert">カード/NFCへ迂回</string>
- <string name="key_no_passphrase">パスフレーズなし</string>
+ <string name="key_divert">スマートカードへ迂回</string>
+ <string name="key_no_passphrase">パスワードなし</string>
<string name="key_unavailable">存在しない</string>
<string name="secret_cannot_multiple">あなたが所有者の鍵は個別にしか削除できません!</string>
<string name="title_view_cert">証明の詳細を見る</string>
<string name="unknown_algorithm">不明</string>
<string name="can_sign_not">署名不可</string>
<string name="error_no_encrypt_subkey">暗号化の副鍵がありません!</string>
- <string name="info_no_manual_account_creation">OpenKeychainのアカウントを手動では生成できません.
-より詳細は、ヘルプを参照のこと。</string>
<string name="contact_show_key">鍵 (%s) を表示</string>
<string name="swipe_to_update">下スワイプでキーサーバから更新します</string>
<string name="error_no_file_selected">暗号化するファイルを少なくとも1つ選択して下さい。</string>
- <string name="error_multi_not_supported">複数ファイルの保存はサポートされていません。これは現在のAndroidでの制限です。</string>
+ <string name="error_detached_signature">バイナリファイルの署名のみの操作はサポートされません、最低1つは暗号化鍵を選択してください。</string>
+ <string name="error_empty_text">テキストの入力を暗号化!</string>
<string name="key_colon">鍵:</string>
<string name="exchange_description">鍵交換の開始は、右側の参加者の番号を選択し、その後、\"交換開始\"ボタンを推します。\n\n2つ以上の質問で交換にいる右の参加者とその指紋が正しいかを確認してください。</string>
<string name="btn_start_exchange">交換開始</string>
<string name="user_id_none"><![CDATA[<none>]]></string>
+ <!--Android Account-->
+ <string name="account_privacy_title">プライバシー</string>
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
<string name="title_unlock_method">アンロックする手段を選択してください</string>
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
- <string name="enter_passphrase">パスフレーズの入力</string>
- <string name="passphrase">パスフレーズ</string>
- <string name="noPassphrase">パスフレーズなし</string>
- <string name="no_passphrase_set">パスフレーズが設定されてない</string>
- <string name="passphrases_match">パスフレーズが一致しない</string>
- <string name="passphrase_saved">パスフレーズを保存</string>
- <string name="passphrase_invalid">無効なパスフレーズ</string>
- <string name="missing_passphrase">パスフレーズがありません</string>
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
+ <string name="enter_passphrase">パスワードの入力</string>
+ <string name="passphrase">パスワード</string>
+ <string name="noPassphrase">パスワードなし</string>
+ <string name="no_passphrase_set">パスワードなしに設定</string>
+ <string name="passphrases_match">パスワードが一致</string>
+ <string name="passphrase_saved">パスワードを保存</string>
+ <string name="passphrase_invalid">無効なパスワード</string>
+ <string name="missing_passphrase">パスワードがありません</string>
<string name="passphrase_again">もう一度</string>
<string name="lockpattern">ロックパターン</string>
<string name="lockpatternNFC">NFCとロックパターン</string>
<string name="unlock_method">アンロック手段</string>
- <string name="set_passphrase">パスフレーズの設定</string>
+ <string name="set_passphrase">パスワードを設定</string>
<string name="draw_lockpattern">ロックパターンを描いてください</string>
<string name="nfc_title">NFC</string>
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
@@ -1132,4 +1277,48 @@
<string name="nfc_write_succesful">NFCタグに書けました!</string>
<string name="unlocked">アンロック</string>
<string name="nfc_settings">設定</string>
+ <string name="snack_yubikey_view">閲覧</string>
+ <string name="snack_yubikey_import">インポート</string>
+ <string name="button_bind_key">鍵と紐付け</string>
+ <string name="yubikey_serno">シリアルナンバー: %s</string>
+ <string name="yubikey_key_holder">鍵ホルダ:</string>
+ <string name="yubikey_key_holder_not_set">鍵ホルダ: &lt;未設定&gt;</string>
+ <string name="yubikey_status_bound">鍵がYubiKeyがマッチし紐付いている</string>
+ <string name="yubikey_status_unbound">YubiKeyがマッチ、鍵に紐付けることができる</string>
+ <string name="yubikey_status_partly">YubiKeyがマッチ、鍵に部分的に紐付いている</string>
+ <string name="yubikey_create">あなたのデバイスの背面にYubiKeyを固定してください。</string>
+ <string name="btn_import">インポート</string>
+ <string name="snack_yubi_other">違う鍵がYubiKeyに格納されています!</string>
+ <string name="error_nfc">NFCエラー: %s</string>
+ <string name="error_nfc_terminated">YubiKeyが完了状態</string>
+ <string name="error_nfc_data_not_found">鍵もしくはオブジェクトが見当りません。</string>
+ <string name="error_nfc_unknown">不明なエラー</string>
+ <string name="error_nfc_bad_data">YubiKeyが不正なデータを報告した。</string>
+ <string name="error_nfc_header">YubiKeyが不正な%sバイトを報告。</string>
+ <string name="error_nfc_try_again">再実行</string>
+ <string name="error_pin_nodefault">デフォルトのPINは棄却されました!</string>
+ <string name="error_temp_file">一時ファイルの生成でエラーしました。</string>
+ <string name="btn_delete_original">オリジナルのファイルを削除します</string>
+ <string name="snack_encrypt_filenames_on">ファイル名を暗号化<b>した</b>。</string>
+ <string name="snack_encrypt_filenames_off">ファイル名を暗号化<b>していません</b>。</string>
+ <string name="snack_armor_on">エンコードしたものをテキストとして出力。</string>
+ <string name="snack_armor_off">エンコードしたものをバイナリとして出力。</string>
+ <string name="snack_compression_on">圧縮を<b>有効化</b>。</string>
+ <string name="snack_compression_off">圧縮を<b>無効化</b>。</string>
+ <string name="error_loading_keys">鍵の読み込みエラー!</string>
+ <string name="error_empty_log">(エラー、空のログ)</string>
+ <string name="error_reading_text">復号化のための入力が読めない!</string>
+ <string name="intent_show">署名/暗号化した内容を表示</string>
+ <string name="view_internal">OpenKeychainで閲覧</string>
+ <string name="error_preparing_data">データの処理でエラー!</string>
+ <string name="label_clip_title">暗号化データ</string>
+ <string name="progress_processing">処理中...</string>
+ <string name="error_saving_file">ファイルの保存でエラー!</string>
+ <string name="file_saved">ファイルを保存した!</string>
+ <string name="file_delete_ok">元のファイルを削除。</string>
+ <string name="error_clipboard_empty">クリップボードが空です!</string>
+ <string name="error_clipboard_copy">クリップボードへのデータコピーでエラー!</string>
+ <string name="error_scan_fp">指紋の読み取りエラー!</string>
+ <string name="error_scan_match">指紋が一致しません!</string>
+ <string name="error_expiry_past">期限切れ日が過去です!</string>
</resources>
diff --git a/OpenKeychain/src/main/res/values-nl/strings.xml b/OpenKeychain/src/main/res/values-nl/strings.xml
index 74e2c7117..f12896b14 100644
--- a/OpenKeychain/src/main/res/values-nl/strings.xml
+++ b/OpenKeychain/src/main/res/values-nl/strings.xml
@@ -7,12 +7,11 @@
<string name="title_encrypt_text">Versleutelen</string>
<string name="title_encrypt_files">Versleutelen</string>
<string name="title_decrypt">Ontsleutelen</string>
- <string name="title_unlock">Ontgrendel sleutel</string>
<string name="title_add_subkey">Voeg subsleutel toe</string>
<string name="title_edit_key">Sleutel bewerken</string>
<string name="title_preferences">Instellingen</string>
<string name="title_api_registered_apps">Apps</string>
- <string name="title_key_server_preference">Sleutelservers</string>
+ <string name="title_key_server_preference">OpenPGP-sleutelservers</string>
<string name="title_change_passphrase">Wachtwoord wijzigen</string>
<string name="title_share_fingerprint_with">Vingerafdruk delen met…</string>
<string name="title_share_key">Sleutel delen met…</string>
@@ -21,8 +20,6 @@
<string name="title_encrypt_to_file">Versleutelen naar bestand</string>
<string name="title_decrypt_to_file">Ontsleutelen naar bestand</string>
<string name="title_import_keys">Sleutels importeren</string>
- <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_send_key">Upload naar sleutelserver</string>
<string name="title_certify_key">Sleutel bevestigen</string>
@@ -30,33 +27,38 @@
<string name="title_help">Help</string>
<string name="title_log_display">Log</string>
<string name="title_exchange_keys">Sleutels uitwisselen</string>
- <string name="title_advanced_key_info">Geavanceerde sleutelinfo</string>
+ <string name="title_advanced_key_info">Uitgebreide informatie</string>
<string name="title_delete_secret_key">JOUW sleutel \'%s\' verwijderen?</string>
<string name="title_export_log">Log exporteren</string>
<string name="title_manage_my_keys">Beheer mijn sleutels</string>
<!--section-->
<string name="section_user_ids">Identiteiten</string>
+ <string name="section_yubikey">YubiKey</string>
<string name="section_linked_system_contact">Verbonden systeemcontact</string>
<string name="section_should_you_trust">Zou je deze sleutel vertrouwen?</string>
<string name="section_proof_details">Bewijs van verificatie</string>
<string name="section_cloud_evidence">Bewijzen van de cloud</string>
<string name="section_keys">Subsleutels</string>
<string name="section_cloud_search">Cloud zoeken</string>
- <string name="section_passphrase_cache">Wachtwoordcache</string>
+ <string name="section_passphrase_cache">Verwerken van wachtwoorden/PINs</string>
+ <string name="section_proxy_settings">Proxy-instellingen</string>
+ <string name="section_gui">Interface</string>
<string name="section_certify">Bevestigen</string>
<string name="section_actions">Acties</string>
<string name="section_share_key">Sleutel</string>
<string name="section_key_server">Sleutelserver</string>
<string name="section_fingerprint">Vingerafdruk</string>
<string name="section_encrypt">Versleutelen</string>
- <string name="section_decrypt">Ontsleutelen</string>
+ <string name="section_decrypt">Ontsleutelen / verifiëren</string>
<string name="section_current_expiry">Huidige verloopdatum</string>
<string name="section_new_expiry">Nieuwe verloopdatum</string>
<!--button-->
- <string name="btn_decrypt_verify_file">Decodeer, verifiëer en sla bestand op</string>
+ <string name="btn_decrypt_verify_file">Ontsleutel, verifieer en sla bestand op</string>
<string name="btn_encrypt_share_file">Bestand versleutelen en delen</string>
<string name="btn_encrypt_save_file">Bestand versleutelen en opslaan</string>
- <string name="btn_save">Opslaan</string>
+ <string name="btn_save_file">Bestand opslaan</string>
+ <string name="btn_save">Opslaa</string>
+ <string name="btn_view_log">Bekijk log</string>
<string name="btn_do_not_save">Annuleren</string>
<string name="btn_delete">Verwijderen</string>
<string name="btn_no_date">Geen einddatum</string>
@@ -71,17 +73,20 @@
<string name="btn_view_cert_key">Toon certificatiesleutel</string>
<string name="btn_create_key">Sleutel aanmaken</string>
<string name="btn_add_files">Bestand(en) toevoegen</string>
- <string name="btn_add_share_decrypted_text">Ontcijferde tekst delen</string>
- <string name="btn_decrypt_clipboard">Tekst van klembord ontsleutelen</string>
- <string name="btn_decrypt_and_verify">en ondertekeningen verifiëren</string>
- <string name="btn_decrypt_files">Bestanden ontcijferen</string>
+ <string name="btn_share_decrypted_text">Ontsleutelde tekst delen</string>
+ <string name="btn_copy_decrypted_text">Ontsleutelde tekst kopiëren</string>
+ <string name="btn_decrypt_clipboard">Lezen van klembord</string>
+ <string name="btn_decrypt_files">Kies invoerbestand</string>
<string name="btn_encrypt_files">Bestanden versleutelen</string>
<string name="btn_encrypt_text">Tekst versleutelen</string>
<string name="btn_add_email">Bijkomstig e-mailadres toevoegen</string>
+ <string name="btn_unlock">Ontgrendelen</string>
+ <string name="btn_add_keyserver">Toevoegen</string>
+ <string name="btn_save_default">Opslaan als standaard</string>
+ <string name="btn_saved">Opgeslagen!</string>
<!--menu-->
<string name="menu_preferences">Instellingen</string>
<string name="menu_help">Help</string>
- <string name="menu_export_key">Exporteren naar bestand</string>
<string name="menu_delete_key">Sleutel verwijderen</string>
<string name="menu_manage_keys">Beheer mijn sleutels</string>
<string name="menu_search">Zoeken</string>
@@ -91,9 +96,10 @@
<string name="menu_select_all">Alles selecteren</string>
<string name="menu_export_all_keys">Alle sleutels exporteren</string>
<string name="menu_update_all_keys">Alle sleutels bijwerken</string>
- <string name="menu_advanced">Geavanceerde info tonen</string>
+ <string name="menu_advanced">Uitgebreide informatie</string>
<string name="menu_certify_fingerprint">Bevestigen door vingerafdrukken te vergelijken</string>
<string name="menu_export_log">Log exporteren</string>
+ <string name="menu_keyserver_add">Toevoegen</string>
<!--label-->
<string name="label_message">Tekst</string>
<string name="label_file">Bestand</string>
@@ -112,19 +118,20 @@
<string name="label_use_default_yubikey_pin">Gebruik standaard YubiKey PIN</string>
<string name="label_use_num_keypad_for_yubikey_pin">Gebruik numeriek toetsenbord voor YubiKey PIN</string>
<string name="label_label_use_default_yubikey_pin_summary">Gebruikt standaard PIN (123456) om YubiKeys over NFC te bereiken</string>
- <string name="label_asymmetric_from">Ondertekend door:</string>
+ <string name="label_asymmetric_from">Ondertekenen met:</string>
<string name="label_to">Versleutelen naar:</string>
<string name="label_delete_after_encryption">Verwijder bestanden na versleuteling</string>
- <string name="label_delete_after_decryption">Verwijder na ontcijfering</string>
+ <string name="label_delete_after_decryption">Verwijderen na ontsleuteling</string>
<string name="label_encryption_algorithm">Versleutelingsalgoritme</string>
<string name="label_hash_algorithm">Hashalgoritme</string>
<string name="label_symmetric">Versleutelen met wachtwoord</string>
- <string name="label_passphrase_cache_ttl">Cachetijd</string>
- <string name="label_passphrase_cache_subs">Cache wachtwoorden per subsleutel</string>
+ <string name="label_passphrase_cache_ttl">Onthou tijd</string>
+ <string name="label_passphrase_cache_subs">Onthou wachtwoorden per subsleutel</string>
<string name="label_message_compression">Tekstcompressie</string>
<string name="label_file_compression">Bestandscompressie</string>
- <string name="label_keyservers">Sleutelservers</string>
+ <string name="label_keyservers">Kies OpenPGP-sleutelservers</string>
<string name="label_key_id">Sleutel-id</string>
+ <string name="label_key_created">Sleutel aangemaakt %s</string>
<string name="label_creation">Aanmaak</string>
<string name="label_expiry">Verlopen</string>
<string name="label_usage">Gebruik</string>
@@ -137,15 +144,49 @@
<string name="label_send_key">Synchroniseren met de cloud</string>
<string name="label_fingerprint">Vingerafdruk</string>
<string name="expiry_date_dialog_title">Bepaal verloopdatum</string>
- <string name="label_first_keyserver_is_used">(Voorkeur gaat uit naar de eerste keyserver in de lijst)</string>
+ <string name="label_keyservers_title">Sleutelservers</string>
+ <string name="label_keyserver_settings_hint">Sleep om de volgorde te wijzigen, tik om te bewerken/verwijderen</string>
+ <string name="label_selected_keyserver_title">Gekozen sleutelserver</string>
<string name="label_preferred">voorkeur</string>
<string name="label_enable_compression">Compressie aanzetten</string>
<string name="label_encrypt_filenames">Versleutel bestandsnamen</string>
<string name="label_hidden_recipients">Verberg ontvangers</string>
- <string name="pref_keyserver">Zoeken op sleutelserver</string>
- <string name="pref_keyserver_summary">Zoeken op HKP sleutelserver</string>
- <string name="pref_keybase">Zoeken op Keybase.io</string>
- <string name="pref_keybase_summary">Zoeken in Keybase.io-index</string>
+ <string name="label_verify_keyserver">Sleutelserver verifiëren</string>
+ <string name="label_enter_keyserver_url">Voer sleutelserver-URL in</string>
+ <string name="label_keyserver_dialog_delete">Sleutelserver verwijderen</string>
+ <string name="label_theme">Thema</string>
+ <string name="pref_keyserver">OpenPGP-sleutelservers</string>
+ <string name="pref_keyserver_summary">Zoek sleutels op geselecteerde OpenPGP-sleutelservers (HKP-protocol)</string>
+ <string name="pref_keybase">keybase.io</string>
+ <string name="pref_keybase_summary">Zoek sleutels op keybase.io</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <string name="pref_proxy_tor_title">Tor aanzetten</string>
+ <string name="pref_proxy_tor_summary">Vereist dat Orbot geïnstalleerd is</string>
+ <string name="pref_proxy_normal_title">Andere proxy aanzetten</string>
+ <string name="pref_proxy_host_title">Proxy-host</string>
+ <string name="pref_proxy_host_err_invalid">Proxy-host kan niet leeg zijn</string>
+ <string name="pref_proxy_port_title">Proxy-poort</string>
+ <string name="pref_proxy_port_err_invalid">Ongeldig poortnummer opgegeven</string>
+ <string name="pref_proxy_type_title">Proxy-type</string>
+ <!--proxy type choices and values-->
+ <string name="pref_proxy_type_choice_http">HTTP</string>
+ <string name="pref_proxy_type_choice_socks">SOCKS</string>
+ <!--OrbotHelper strings-->
+ <string name="orbot_ignore_tor">Gebruik Tor niet</string>
+ <!--InstallDialogFragment strings-->
+ <string name="orbot_install_dialog_title">Orbot installeren om Tor te gebruiken?</string>
+ <string name="orbot_install_dialog_install">Installeren</string>
+ <string name="orbot_install_dialog_content">Orbot moet geïnstalleerd en ingesteld zijn om verkeer erdoor te proxyen. Wil je het installeren?</string>
+ <string name="orbot_install_dialog_cancel">Annuleren?</string>
+ <string name="orbot_install_dialog_ignore_tor">Gebruik Tor niet</string>
+ <!--StartOrbotDialogFragment strings-->
+ <string name="orbot_start_dialog_title">Orbot starten?</string>
+ <string name="orbot_start_dialog_content">Orbot wordt niet uitgevoerd. Wil je het opstarten en verbinden met Tor?</string>
+ <string name="orbot_start_btn">Orbot starten</string>
+ <string name="orbot_start_dialog_start">Orbot starten</string>
+ <string name="orbot_start_dialog_cancel">Annuleren</string>
+ <string name="orbot_start_dialog_ignore_tor">Gebruik Tor niet</string>
<string name="user_id_no_name">&lt;no naam&gt;</string>
<string name="none">&lt;geen&gt;</string>
<plurals name="n_keys">
@@ -188,23 +229,25 @@
<string name="wrong_passphrase">Wachtwoord verkeerd.</string>
<string name="no_filemanager_installed">Geen compatibele bestandsbeheerder geïnstalleerd.</string>
<string name="passphrases_do_not_match">De wachtwoorden komen niet overeen.</string>
- <string name="passphrase_must_not_be_empty">Vul een wachtwoord in.</string>
+ <string name="passphrase_must_not_be_empty">Geef een wachtwoord in.</string>
<string name="passphrase_for_symmetric_encryption">Symmetrische versleuteling.</string>
<string name="passphrase_for">Voer het wachtwoord in voor \'%s\'</string>
<string name="pin_for">Voer PIN in voor \'%s\'</string>
<string name="yubikey_pin_for">Voer PIN in om toegang te verkrijgen tot YubiKey voor \'%s\'</string>
- <string name="nfc_text">Hou YubiKey tegen de achterkant van je toestel</string>
+ <string name="nfc_text">Hou de YubiKey tegen de NFC-aanduiding aan de achterkant van je toestel.</string>
+ <string name="nfc_wait">Hou de YubiKey tegen de achterkant!</string>
+ <string name="nfc_finished">Neem de YubiKey nu weg.</string>
+ <string name="nfc_try_again_text">Neem de YubiKey nu weg en druk op Opnieuw proberen.</string>
<string name="file_delete_confirmation_title">Oorspronkelijke bestanden verwijderen?</string>
<string name="file_delete_confirmation">De volgende bestanden zullen worden verwijderd:%s</string>
<string name="file_delete_successful">%1$d van %2$d bestanden zijn verwijderd.%3$s</string>
- <string name="no_file_selected">Selecteer eerst een bestand.</string>
+ <string name="no_file_selected">Geen bestand geselecteerd.</string>
<string name="encrypt_sign_successful">Succesvol gesigneerd en/of gecodeerd.</string>
<string name="encrypt_sign_clipboard_successful">Succesvol gesigneerd en/of gecodeerd naar klembord.</string>
<string name="select_encryption_key">Selecteer ten minste één versleutelingssleutel.</string>
- <string name="select_encryption_or_signature_key">Selecter ten minste één versleutelings-/ondertekeningssleutel.</string>
+ <string name="error_no_encryption_or_signature_key">Kies ten minste één versleutelingssleutel of een ondertekeningssleutel.</string>
<string name="specify_file_to_encrypt_to">Gelieve aan te geven naar welk bestand versleuteld moet worden.\nWAARSCHUWING: Als het bestand al bestaat, zal het overschreven worden!</string>
- <string name="specify_file_to_decrypt_to">Gelieve aan te geven naar welk bestand ontcijferd moet worden.\nWAARSCHUWING: Als het bestand al bestaat, zal het overschreven worden!</string>
- <string name="specify_file_to_export_to">Gelieve aan te geven naar welk bestand geëxporteerd moet worden.\nWAARSCHUWING: Als het bestand al bestaat, zal het overschreven worden!</string>
+ <string name="specify_file_to_decrypt_to">Gelieve aan te geven naar welk bestand ontsleuteld moet worden.\nWAARSCHUWING: Als het bestand al bestaat, zal het overschreven worden!</string>
<string name="key_deletion_confirmation_multi">Ben je zeker dat je alle geselecteerde sleutels wil verwijderen?</string>
<string name="secret_key_deletion_confirmation">Na verwijderen zal je niet langer berichten versleuteld met deze sleutel kunnen lezen, en alle sleutelbevestigingen die ermee gedaan zijn verliezen!</string>
<string name="public_key_deletetion_confirmation">Sleutel \'%s\' verwijderen?</string>
@@ -237,7 +280,7 @@
<string name="error_external_storage_not_ready">externe opslag niet gereed</string>
<string name="error_key_size_minimum512bit">sleutelgrootte moet minstens 512-bits zijn</string>
<string name="error_unknown_algorithm_choice">onbekende algoritmekeuze</string>
- <string name="error_user_id_no_email">geen e-mail gevonden</string>
+ <string name="error_user_id_no_email">geen e-mailadres gevonden</string>
<string name="error_key_needs_a_user_id">minstens een identiteit vereist</string>
<string name="error_no_signature_passphrase">geen wachtwoord opgegeven</string>
<string name="error_no_signature_key">geen ondertekeningssleutel opgegeven</string>
@@ -250,16 +293,18 @@
<string name="error_nfc_needed">NFC moet aangezet zijn!</string>
<string name="error_beam_needed">Beam moet aangezet zijn!</string>
<string name="error_nothing_import">Geen sleutels gevonden!</string>
+ <string name="error_nothing_import_selected">Geen sleutels geselecteerd voor importeren!</string>
<string name="error_contacts_key_id_missing">Sleutel-ID van contacten ophalen mislukt!</string>
<string name="error_generic_report_bug">Een algemene fout is opgetreden, gelieve een nieuw bug-verslag te maken voor OpenKeychain.</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">Niet ondertekend</string>
<string name="decrypt_result_invalid_signature">Ongeldige handtekening!</string>
- <string name="decrypt_result_signature_uncertified">Ondertekend door (niet gecertificeerd!)</string>
- <string name="decrypt_result_signature_certified">Ondertekend door</string>
- <string name="decrypt_result_signature_expired_key">Sleutel is verlopen!</string>
- <string name="decrypt_result_signature_revoked_key">Sleutel is ingetrokken!</string>
- <string name="decrypt_result_signature_missing_key">Onbekende publieke sleutel</string>
+ <string name="decrypt_result_signature_uncertified">Ondertekend door <b>niet bevestigde</b> sleutel</string>
+ <string name="decrypt_result_signature_secret">Ondertekend door jouw sleutel</string>
+ <string name="decrypt_result_signature_certified">Ondertekend door bevestigde sleutel</string>
+ <string name="decrypt_result_signature_expired_key">Ondertekend door <b>verlopen</b> sleutel!</string>
+ <string name="decrypt_result_signature_revoked_key">Ondertekend door <b>ingetrokken</b> sleutel!</string>
+ <string name="decrypt_result_signature_missing_key">Ondertekend door <b>onbekende publieke sleutel</b></string>
<string name="decrypt_result_encrypted">Versleuteld</string>
<string name="decrypt_result_not_encrypted">Niet versleuteld</string>
<string name="decrypt_result_action_show">Toon</string>
@@ -295,10 +340,13 @@
<string name="progress_modify_subkeystrip">bezig met strippen van subsleutels…</string>
<string name="progress_modify_subkeyadd">bezig met toevoegen van subsleutels…</string>
<string name="progress_modify_passphrase">bezig met veranderen van wachtwoord…</string>
+ <string name="progress_modify_pin">bezig met veranderen van PIN…</string>
+ <string name="progress_modify_admin_pin">bezig met veranderen van administrator-PIN…</string>
<plurals name="progress_exporting_key">
<item quantity="one">sleutel exporteren…</item>
<item quantity="other">sleutels exporteren…</item>
</plurals>
+ <string name="progress_start">bewerking voorbereiden…</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>
@@ -318,6 +366,7 @@
<string name="progress_deleting">bezig met verwijderen van sleutels…</string>
<string name="progress_con_saving">consolidatie: bezig met opslaan naar cache…</string>
<string name="progress_con_reimport">consolidatie: bezig met opnieuw importeren…</string>
+ <string name="progress_verifying_keyserver_url">bezig met verifiëren van sleutelserver…</string>
<!--action strings-->
<string name="hint_cloud_search_hint">Zoeken via naam, e-mail, ...</string>
<!--key bit length selections-->
@@ -359,11 +408,14 @@
<string name="import_tab_qr_code">QR code/NFC</string>
<string name="import_import">Geselecteerde sleutels importeren</string>
<string name="import_qr_code_wrong">QR-code ongeldig. Probeer het opnieuw</string>
- <string name="import_qr_code_too_short_fingerprint">Vingerafdruk is te kort (&lt; 16 tekens)</string>
+ <string name="import_qr_code_fp">Vingerafdruk is verkeerd of te kort!</string>
+ <string name="import_qr_code_too_short_fingerprint">Vingerafdruk is te kort!</string>
<string name="import_qr_code_button">QR code scannen</string>
<string name="import_qr_code_text">Plaats je camera voor de QR-code!</string>
+ <!--Import from URL-->
+ <string name="import_url_warn_no_search_parameter">Geen zoekopdracht gedefinieerd. Je kan de sleutelserver handmatig doorzoeken.</string>
<!--Generic result toast-->
- <string name="view_log">Details</string>
+ <string name="snackbar_details">Details</string>
<string name="with_warnings">, met waarschuwingen</string>
<string name="with_cancelled">, tot annulatie</string>
<!--Import result toast-->
@@ -412,6 +464,7 @@
</plurals>
<string name="delete_nothing">Niets te verwijderen.</string>
<string name="delete_cancelled">Verwijderen geannuleerd.</string>
+ <!--Revoke result toast (snackbar)-->
<!--Certify result toast-->
<plurals name="certify_keys_ok">
<item quantity="one">Sleutel%2$s succesvol gecertificeerd.</item>
@@ -426,15 +479,15 @@
<item quantity="other">Certificatie van %d sleutels mislukt!</item>
</plurals>
<!--Intent labels-->
- <string name="intent_decrypt_file">Decodeer bestand met OpenKeychain</string>
+ <string name="intent_decrypt_file">Bestand ontsleutelen met OpenKeychain</string>
<string name="intent_import_key">Importeer Sleutel met OpenKeychain</string>
<string name="intent_send_encrypt">Codeer met OpenKeychain</string>
- <string name="intent_send_decrypt">Decodeer met OpenKeychain</string>
+ <string name="intent_send_decrypt">Ontsleutelen met OpenKeychain</string>
<!--Remote API-->
- <string name="api_settings_show_info">Toon geavanceerde informatie</string>
- <string name="api_settings_hide_info">Verberg geavanceerde informatie</string>
- <string name="api_settings_show_advanced">Toon geavanceerde instellingen</string>
- <string name="api_settings_hide_advanced">Verberg geavanceerde instellingen</string>
+ <string name="api_settings_show_info">Uitgebreide informatie weergeven</string>
+ <string name="api_settings_hide_info">Uitgebreide informatie verbergen</string>
+ <string name="api_settings_show_advanced">Uitgebreide instellingen weergeven</string>
+ <string name="api_settings_hide_advanced">Uitgebreide instellingen verbergen</string>
<string name="api_settings_no_key">Geen sleutel geselecteerd</string>
<string name="api_settings_select_key">Sleutel selecteren</string>
<string name="api_settings_create_key">Nieuwe sleutel aanmaken</string>
@@ -445,28 +498,32 @@
<string name="api_settings_start">Start applicatie</string>
<string name="api_settings_delete_account">Verwijder account</string>
<string name="api_settings_package_name">Pakketnaam</string>
- <string name="api_settings_package_signature">SHA-256 van Pakkethandtekening</string>
- <string name="api_settings_accounts">Accounts (verouderde API)</string>
- <string name="api_settings_advanced">Geavanceerde informatie</string>
+ <string name="api_settings_package_certificate">SHA-256 van Pakketcertificaat</string>
+ <string name="api_settings_accounts">Accounts (oude API)</string>
+ <string name="api_settings_advanced">Uitgebreide informatie</string>
<string name="api_settings_allowed_keys">Toegestane sleutels</string>
<string name="api_settings_settings">Instellingen</string>
<string name="api_settings_key">Accountsleutel:</string>
<string name="api_settings_accounts_empty">Geen accounts verbonden aan deze app.</string>
- <string name="api_create_account_text">Geen sleutel ingesteld voor deze account. Gelieve een van je bestaande sleutels te selecteren, of een nieuwe sleutel aan te maken.\nApps kunnen enkel ontcijferen/ondertekenen met de sleutels die je hier selecteert!</string>
- <string name="api_update_account_text">De sleutel opgeslaan voor deze account is verwijderd. Gelieve een andere te selecteren!\nApps kunnen enkel ontcijferen/ondertekenen met de sleutels die je hier selecteert!</string>
- <string name="api_register_text">De weergegeven app will berichten versleutelen/ontcijferen en ze in jouw naam ondertekenen.\nToegang toestaan?\n\nWAARSCHUWING: als je niet weet waarom dit scherm verscheen, sta dan geen toegang toe! Je kan later toegang weghalen via het \'Apps\' scherm.</string>
+ <string name="api_create_account_text">Geen sleutel ingesteld voor deze account. Gelieve een van je bestaande sleutels te selecteren, of een nieuwe sleutel aan te maken.\nApps kunnen enkel ontsleutelen/ondertekenen met de sleutels die je hier selecteert!</string>
+ <string name="api_update_account_text">De sleutel opgeslaan voor deze account is verwijderd. Gelieve een andere te selecteren!\nApps kunnen enkel ontsleutelen/ondertekenen met de sleutels die je hier selecteert!</string>
+ <string name="api_register_text">De weergegeven app will berichten versleutelen/ontsleutelen en ze in jouw naam ondertekenen.\nToegang toestaan?\n\nWAARSCHUWING: als je niet weet waarom dit scherm verscheen, sta dan geen toegang toe! Je kan later toegang weghalen via het \'Apps\' scherm.</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>
- <string name="api_select_pub_keys_missing_text">Geen sleutels gevonden voor deze identiteiten:</string>
- <string name="api_select_pub_keys_dublicates_text">Meer dan een sleutel bestaat voor deze identiteiten:</string>
+ <string name="api_select_pub_keys_missing_text">Geen sleutels gevonden voor deze e-mailadressen:</string>
+ <string name="api_select_pub_keys_dublicates_text">Er bestaat meer dan een sleutel voor deze e-mailadressen:</string>
<string name="api_select_pub_keys_text">Bekijkt u a.u.b. de ontvangers</string>
<string name="api_select_pub_keys_text_no_user_ids">Gelieve de ontvangers te selecteren!</string>
<string name="api_error_wrong_signature">Handtekening check mislukt! Hebt u deze app van een andere bron geïnstalleerd? Als u zeker weet dat dit geen aanval is, haal dan de registratie van deze app in OpenKeychain weg en registreer de app opnieuw.</string>
<string name="api_select_sign_key_text">Gelieve een van je bestaande sleutels te selecteren of een nieuwe aan te maken.</string>
+ <string name="api_select_keys_text">Geen van de toegestane sleutels kan de inhoud ontsleutelen. Gelieve de toegestane sleutels te selecteren.</string>
<!--Share-->
<string name="share_qr_code_dialog_title">Delen met QR-code</string>
<string name="share_nfc_dialog">Deel met NFC</string>
+ <!--retry upload dialog-->
+ <!--Delete or revoke private key dialog-->
+ <!--Delete Or Revoke Dialog spinner-->
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">1 sleutel geselecteerd.</item>
@@ -509,14 +566,6 @@
<string name="key_trust_results_prefix">Keybase.io geeft “bewijzen” die stellen dat de eigenaar van deze sleutel:</string>
<string name="key_trust_header_text">Let op: Keybase.io-bewijzen zijn een experimentele functie van OpenKeychain. We moedigen je aan QR-codes te scannen of sleutels uit te wisselen via NFC bovenop het bevestigen ervan.</string>
<!--keybase proof stuff-->
- <string name="keybase_narrative_twitter">Post op Twitter als</string>
- <string name="keybase_narrative_github">Bekend is op GitHub als</string>
- <string name="keybase_narrative_dns">De volgende domeinnamen beheert</string>
- <string name="keybase_narrative_web_site">Kan posten op de website(s)</string>
- <string name="keybase_narrative_reddit">Post op Reddit als</string>
- <string name="keybase_narrative_coinbase">Bekend is op Coinbase als</string>
- <string name="keybase_narrative_hackernews">Post op Hacker News als</string>
- <string name="keybase_narrative_unknown">Onbekend bewijstype</string>
<string name="keybase_proof_failure">Helaas kan dit bewijs niet geverifieerd worden.</string>
<string name="keybase_unknown_proof_failure">Niet-herkend probleem met de bewijschecker</string>
<string name="keybase_problem_fetching_evidence">Probleem met bewijs</string>
@@ -555,11 +604,15 @@
<item>Vervaldatum veranderen</item>
<item>Subsleutel intrekken</item>
<item>Subsleutel strippen</item>
+ <item>Subsleutel verplaatsen naar YubiKey / smartcard</item>
</string-array>
<string name="edit_key_new_subkey">nieuwe subsleutel</string>
<string name="edit_key_select_flag">Gelieve minstens een vlag te selecteren!</string>
<string name="edit_key_error_add_identity">Voeg minstens een identiteit toe!</string>
<string name="edit_key_error_add_subkey">Voeg minstens een subsleutel toe!</string>
+ <string name="edit_key_error_bad_nfc_algo">Algoritme wordt niet ondersteund door smartcard!</string>
+ <string name="edit_key_error_bad_nfc_size">Sleutelgrootte wordt niet ondersteund door smartcard!</string>
+ <string name="edit_key_error_bad_nfc_stripped">Kan sleutel niet verplaatsen naar smartcard (ofwel gestript ofwel \'doorschakelen-naar-kaart\')!</string>
<!--Create key-->
<string name="create_key_upload">Synchroniseren met de cloud</string>
<string name="create_key_empty">Dit veld moet ingevuld worden</string>
@@ -575,7 +628,15 @@
<string name="create_key_edit">Sleutelconfiguratie wijzigen</string>
<string name="create_key_add_email">E-mailadres toevoegen</string>
<string name="create_key_add_email_text">Bijkomstige e-mailadressen zijn ook verbonden met deze sleutel en kunnen gebruikt worden voor veilige communicatie.</string>
- <string name="create_key_email_already_exists_text">E-mail is al toegevoegd</string>
+ <string name="create_key_email_already_exists_text">E-mailadres is al toegevoegd</string>
+ <string name="create_key_email_invalid_email">E-mailadresformaat is ongeldig</string>
+ <string name="create_key_yubi_key_pin_text">Onthou deze PIN, ze is nodig om je YubiKey later te gebruiken. Schrijf indien mogelijk de administrator-PIN ergens op en bewaar deze op een veilige plek.</string>
+ <string name="create_key_yubi_key_pin">PIN</string>
+ <string name="create_key_yubi_key_admin_pin">Administrator-PIN</string>
+ <string name="create_key_yubi_key_pin_repeat_text">Voer de PIN en administrator-PIN in om door te gaan.</string>
+ <string name="create_key_yubi_key_pin_repeat">Herhaal PIN</string>
+ <string name="create_key_yubi_key_admin_pin_repeat">Herhaal administrator-PIN</string>
+ <string name="create_key_yubi_key_pin_not_correct">PIN is niet correct!</string>
<!--View key-->
<string name="view_key_revoked">Ingetrokken: sleutel mag niet meer gebruikt worden!</string>
<string name="view_key_expired">Verlopen: het contact moet de geldigheid van de sleutel verlengen!</string>
@@ -584,6 +645,15 @@
<string name="view_key_verified">Bevestigde sleutel</string>
<string name="view_key_unverified">Niet bevestigd: scan QR-code om de sleutel te bevestigen!</string>
<string name="view_key_fragment_no_system_contact">&lt;geen&gt;</string>
+ <!--Add/Edit keyserver-->
+ <string name="add_keyserver_dialog_title">Sleutelserver toevoegen</string>
+ <string name="edit_keyserver_dialog_title">Sleutelserver bewerken</string>
+ <string name="add_keyserver_verified">Sleutelserver geverifieerd!</string>
+ <string name="add_keyserver_without_verification">Sleutelserver toegevoegd zonder verificatie.</string>
+ <string name="add_keyserver_invalid_url">Ongeldige URL!</string>
+ <string name="add_keyserver_connection_failed">Kon niet verbinden met sleutelserver. Controleer de URL en je internetverbinding.</string>
+ <string name="keyserver_preference_deleted">%s verwijderd</string>
+ <string name="keyserver_preference_cannot_delete_last">Kan laatste sleutelserver niet verwijderen, er is er minstens één nodig!</string>
<!--Navigation Drawer-->
<string name="nav_keys">Sleutels</string>
<string name="nav_encrypt_decrypt">Versleutelen/Ontsleutelen</string>
@@ -714,7 +784,7 @@
<string name="msg_is_subkey_empty">Geheime subsleutel %s gemarkeerd als beschikbaar, met leeg wachtwoord</string>
<string name="msg_is_subkey_pin">Geheime subsleutel %s gemarkeerd als beschikbaar, met PIN</string>
<string name="msg_is_subkey_stripped">Geheime subsleutel %s gemarkeerd als gestript</string>
- <string name="msg_is_subkey_divert">Geheime subsleutel %s gemarkeerd als \'doorschakelen naar smartcard/NFC\'</string>
+ <string name="msg_is_subkey_divert">Geheime subsleutel %s gemarkeerd als \'doorschakelen-naar-kaart\'</string>
<string name="msg_is_success_identical">Sleutelbos bevat geen nieuwe gegevens, niets te doen</string>
<string name="msg_is_success">Geheime sleutelbos succesvol geïmporteerd</string>
<!--Keyring Canonicalization log entries-->
@@ -741,6 +811,7 @@
<string name="msg_kc_sub_bad_local">Bezig met verwijderen van subsleutelbindend certificaat met vlag \'lokaal\'</string>
<string name="msg_kc_sub_bad_keyid">Subsleutelbindende uitgevers-ID komt niet overeen</string>
<string name="msg_kc_sub_bad_time">Bezig met verwijderen van subsleutelbindend certificaat met toekomstige tijdstempel</string>
+ <string name="msg_kc_sub_bad_time_early">Subsleutelbindend certificaat heeft vroegere tijdstempel dan sleutel!</string>
<string name="msg_kc_sub_bad_type">Onbekend subsleutelcertificaattype: %s</string>
<string name="msg_kc_sub_dup">Bezig met verwijderen van overbodig subsleutelbindend certificaat</string>
<string name="msg_kc_sub_primary_bad">Bezig met verwijderen van subsleutelbindend certificaat wegens ongeldig primair bindend certificaat</string>
@@ -817,7 +888,8 @@
<string name="msg_cr_error_flags_ecdh">Slechte sleutelvlaggen geselecteerd, ECDH kan niet gebruikt worden voor versleuteling!</string>
<!--modifySecretKeyRing-->
<string name="msg_mr">Bezig met wijzigen van sleutelbos %s</string>
- <string name="msg_mf_error_divert_serial">Het serienummer van een doorschakelen-naar-kaart sleutel moet 16 bytes zijn! Dit is een programmeerfout, gelieve een verslag in te dienen!</string>
+ <string name="msg_mf_divert">Zal doorschakelen naar smartcard voor versleutelingsbewerkingen</string>
+ <string name="msg_mf_error_divert_serial">Het serienummer van een \'doorschakelen-naar-kaart\'-sleutel moet 16 bytes zijn! Dit is een programmeerfout, gelieve een verslag in te dienen!</string>
<string name="msg_mf_error_encode">Codeeruitzondering!</string>
<string name="msg_mf_error_fingerprint">Eigenlijke vingerafdruk van sleutel komt niet overeen met verwachte vingerafdruk!</string>
<string name="msg_mf_error_keyid">Geen sleutel-ID. Dit is een bug, gelieve een verslag in te dienen!</string>
@@ -828,25 +900,37 @@
<string name="msg_mf_error_restricted">Geprobeerd een beperkte bewerking uit te voeren zonder wachtwoord! Dit is een programmeerfout, gelieve een verslag in te dienen!</string>
<string name="msg_mf_error_revoked_primary">Ingetrokken gebruikers-ID\'s kunnen niet primair zijn!</string>
<string name="msg_mf_error_null_expiry">Verloopdatum kan niet hetzelfde als voordien zijn bij aanmaken van een subsleutel. Dit is een bug, gelieve een verslag in te dienen!</string>
- <string name="msg_mf_error_passphrase_master">Fatale fout bij ontcijferen van hoofdsleutel. Dit is waarschijnlijk een bug, gelieve een verslag in te dienen!</string>
+ <string name="msg_mf_error_noop">Niets om te doen!</string>
+ <string name="msg_mf_error_passphrase_master">Fatale fout bij ontsleutelen van hoofdsleutel. Dit is waarschijnlijk een bug, gelieve een verslag in te dienen!</string>
<string name="msg_mf_error_pgp">Interne OpenPGP-fout!</string>
<string name="msg_mf_error_sig">Ondertekeningsuitzondering!</string>
+ <string name="msg_mf_error_sub_stripped">Kan gestripte subsleutel %s niet wijzigen!</string>
+ <string name="msg_mf_error_subkey_missing">Geprobeerd om bewerking uit te voeren op ontbrekende subsleutel %s!</string>
+ <string name="msg_mf_error_conflicting_nfc_commands">Kan sleutel niet tegelijk verplaatsen naar smartcard en een on-card-ondertekening aanmaken.</string>
+ <string name="msg_mf_error_duplicate_keytocard_for_slot">Smartcard ondersteunt slechts een slot per sleuteltype</string>
+ <string name="msg_mf_error_invalid_flags_for_keytocard">Ongeldige sleutelvlaggen voor smartcardsleutel</string>
<string name="msg_mf_master">Bezig met wijzigen van hoofdcertificaten</string>
<string name="msg_mf_notation_empty">Bezig met toevoegen van leeg notatiepakket</string>
<string name="msg_mf_notation_pin">Bezig met toevoegen van PIN-notatiepakket</string>
<string name="msg_mf_passphrase">Bezig met wijzigen van wachtwoord voor sleutelbos</string>
+ <string name="msg_mf_pin">Bezig met veranderen van PIN op kaart</string>
+ <string name="msg_mf_admin_pin">Bezig met veranderen van administrator-PIN op kaart</string>
<string name="msg_mf_passphrase_key">Bezig met opnieuw versleutelen van subsleutel %s met nieuw wachtwoord</string>
<string name="msg_mf_passphrase_empty_retry">Instellen van nieuw wachtwoord mislukt, opnieuw proberen met leeg oud wachtwoord</string>
<string name="msg_mf_passphrase_fail">Wachtwoord voor subsleutel kon niet gewijzigd worden! (Heeft het een ander wachtwoord dan de andere sleutels?)</string>
<string name="msg_mf_primary_replace_old">Bezig met vervangen van certificaat van vorige primaire gebruikers-ID</string>
<string name="msg_mf_primary_new">Bezig met aanmaken van nieuw certificaat voor nieuwe primaire gebruikers-ID</string>
+ <string name="msg_mf_restricted_mode">Veranderen naar beperkte bewerkingsmodus</string>
<string name="msg_mf_subkey_change">Bezig met wijzigen van subsleutel %s</string>
- <string name="msg_mf_error_subkey_missing">Geprobeerd om bewerking uit te voeren op ontbrekende subsleutel %s!</string>
+ <string name="msg_mf_require_divert">Doorschakelen naar smartcard voor versleutelingsbewerkingen</string>
+ <string name="msg_mf_require_passphrase">Wachtwoord vereist voor bewerkingen</string>
<string name="msg_mf_subkey_new">Bezig met toevoegen van nieuwe subsleutel of type %s</string>
<string name="msg_mf_subkey_new_id">Nieuwe subsleutel-ID: %s</string>
<string name="msg_mf_error_past_expiry">Verloopdatum kan niet in het verleden zijn!</string>
<string name="msg_mf_subkey_revoke">Bezig met intrekken van subsleutel %s</string>
<string name="msg_mf_subkey_strip">Bezig met strippen van subsleutel %s</string>
+ <string name="msg_mf_keytocard_start">Subsleutel %s wordt verplaatst naar smartcard</string>
+ <string name="msg_mf_keytocard_finish">%1$s verplaatst naar smartcard %2$s</string>
<string name="msg_mf_success">Sleutelbos succesvol gewijzigd</string>
<string name="msg_mf_uid_add">Bezig met toevoegen van gebruikers-ID %s</string>
<string name="msg_mf_uid_primary">Bezig met wijzigen van primaire gebruikers-ID naar %s</string>
@@ -898,12 +982,13 @@
<string name="msg_ed_success">Sleutelbewerking geslaagd</string>
<!--Promote key-->
<string name="msg_pr">Bezig met promoveren van publieke sleutel naar geheime sleutel</string>
- <string name="msg_pr_error_already_secret">Sleutel is al een geheime sleutel!</string>
+ <string name="msg_pr_all">Bezig met promoveren van alle subsleutels</string>
<string name="msg_pr_error_key_not_found">Sleutel niet gevonden!</string>
<string name="msg_pr_fetching">Bezig met ophalen van de te wijzigen sleutel (%s)</string>
+ <string name="msg_pr_subkey_match">Bezig met promoveren van subsleutel: %s</string>
+ <string name="msg_pr_subkey_nomatch">Subsleutel niet op YubiKey: %s</string>
<string name="msg_pr_success">Sleutel succesvol gepromoveerd!</string>
<!--Other messages used in OperationLogs-->
- <string name="msg_ek_error_divert">Bewerken van NFC-sleutels wordt (nog) niet ondersteund!</string>
<string name="msg_ek_error_dummy">Kan sleutelbos niet bewerken met gestripte hoofdsleutel!</string>
<string name="msg_ek_error_not_found">Sleutel niet gevonden!</string>
<!--Messages for DecryptVerify operation-->
@@ -919,35 +1004,35 @@
<string name="msg_dc_clear_meta_size_unknown">Bestandsgrootte onbekend</string>
<string name="msg_dc_clear_meta_time">Wijzigingstijd: %s</string>
<string name="msg_dc_clear_signature_bad">Ondertekeningscontrole NIET OKÉ!</string>
- <string name="msg_dc_error_unsupported_hash_algo">Niet ondersteund en mogelijk onveilig hash-algoritme!</string>
<string name="msg_dc_clear_signature_check">Bezig met verifiëren van ondertekeningsgegevens</string>
<string name="msg_dc_clear_signature_ok">Ondertekeningscontrole OKÉ</string>
<string name="msg_dc_clear_signature">Bezig met opslaan van ondertekeningsgegevens voor later</string>
<string name="msg_dc_clear">Bezig met verwerken van platte tekst-gegevens</string>
- <string name="msg_dc_error_bad_passphrase">Fout bij ontcijferen van sleutel, slecht wachtwoord!</string>
+ <string name="msg_dc_error_bad_passphrase">Fout bij ontgrendelen van sleutel, verkeerd wachtwoord!</string>
+ <string name="msg_dc_error_sym_passphrase">Fout bij ontsleutelen van gegevens! (Verkeerd wachtwoord?)</string>
+ <string name="msg_dc_error_corrupt_data">Gegevens zijn corrupt!</string>
<string name="msg_dc_error_extract_key">Onbekende fout bij ontgrendelen van sleutel!</string>
<string name="msg_dc_error_integrity_check">Fout bij integriteitscontrole!</string>
- <string name="msg_dc_error_integrity_missing">Integriteitscheck ontbreekt! Dit kan gebeuren omdat de versleutelingsapplicatie verouderd is, of door een downgrade-aanval.</string>
- <string name="msg_dc_error_invalid_siglist">Geen geldige ondertekeningsgegevens gevonden!</string>
- <string name="msg_dc_error_io">I/O-uitzondering tegengekomen tijdens bewerking!</string>
+ <string name="msg_dc_error_invalid_data">Geen geldige OpenPGP-versleutelde of ondertekende inhoud gevonden!</string>
+ <string name="msg_dc_error_io">Fout bij lezen van invoergegevens!</string>
+ <string name="msg_dc_error_input">Fout bij openen van invoergegevensstream!</string>
<string name="msg_dc_error_no_data">Geen versleutelde gegevens gevonden!</string>
<string name="msg_dc_error_no_key">Geen versleutelde gegevens met bekende geheime sleutel gevonden!</string>
<string name="msg_dc_error_pgp_exception">OpenPGP-uitzondering tegengekomen tijdens bewerking!</string>
<string name="msg_dc_integrity_check_ok">Integriteitscontrole OKÉ!</string>
- <string name="msg_dc_ok_meta_only">Enkel metadata opgevraagd, ontcijferen wordt overgeslaan</string>
+ <string name="msg_dc_ok_meta_only">Enkel metadata opgevraagd, ontsleutelen wordt overgeslagen</string>
<string name="msg_dc_ok">Ontsleuteling/verificatie voltooid</string>
<string name="msg_dc_pass_cached">Wachtwoord wordt uit cache gebruikt</string>
<string name="msg_dc_pending_nfc">NFC token vereist, gebruikersinput wordt gevraagd…</string>
<string name="msg_dc_pending_passphrase">Wachtwoord vereist, gebruikersinput wordt gevraagd…</string>
- <string name="msg_dc_prep_streams">Bezig met voorbereiden van streams voor ontcijfering</string>
- <string name="msg_dc">Ontcijfering wordt gestart…</string>
+ <string name="msg_dc_prep_streams">Bezig met voorbereiden van streams voor ontsleuteling</string>
+ <string name="msg_dc">Ontsleuteling wordt gestart…</string>
<string name="msg_dc_sym_skip">Symmetrische gegevens niet toegestaan, bewerking wordt overgeslaan…</string>
<string name="msg_dc_sym">Blok met symmetrisch versleutelde gegevens gevonden</string>
<string name="msg_dc_trail_asym">Achterlopende, asymmetrisch versleutelde gegevens tegengekomen voor sleutel %s</string>
<string name="msg_dc_trail_sym">Achterlopende, symmetrisch versleutelde gegevens tegengekomen</string>
<string name="msg_dc_trail_unknown">Achterlopende gegevens van onbekend type tegengekomen</string>
<string name="msg_dc_unlocking">Bezig met ontgrendelen van geheime sleutel</string>
- <string name="msg_dc_old_symmetric_encryption_algo">Mogelijk onveilig versleutelingsalgoritme gebruikt!</string>
<!--Messages for VerifySignedLiteralData operation-->
<string name="msg_vl">Ondertekeningscontrole wordt gestart</string>
<string name="msg_vl_error_no_siglist">Geen ondertekeningslijst in ondertekende letterlijke gegevens</string>
@@ -968,20 +1053,18 @@
<string name="msg_se_error_input_uri_not_found">Fout bij openen van URI voor lezen!</string>
<string name="msg_se_error_output_uri_not_found">Fout bij openen van URI voor schrijven!</string>
<string name="msg_se_error_too_many_inputs">Meer inputs dan outputs opgegeven! Dit is waarschijnlijk een programmeerfout, gelieve dit te melden!</string>
- <string name="msg_se_warn_output_left">Er blijven outputs over maar geen inputs. Dit is waarschijnlijk een programmeerfout, gelieve dit te melden!</string>
<string name="msg_se_success">Ondertekening/versleuteling geslaagd</string>
<!--Messages for PgpSignEncrypt operation-->
<string name="msg_pse_asymmetric">Bezig met publieke sleutels voorbereiden voor versleuteling</string>
<string name="msg_pse_clearsign_only">Ondertekenen van platte tekst-input wordt niet ondersteund!</string>
<string name="msg_pse_compressing">Bezig met voorbereiden van comprimeren</string>
<string name="msg_pse_encrypting">Bezig met versleutelen van gegevens</string>
- <string name="msg_pse_error_bad_passphrase">Slecht wachtwoord!</string>
- <string name="msg_pse_error_hash_algo">Het gevraagde hashing-algoritme wordt niet ondersteund door deze sleutel!</string>
+ <string name="msg_pse_error_bad_passphrase">Wachtwoord verkeerd!</string>
<string name="msg_pse_error_io">I/O-uitzondering tegengekomen tijdens bewerking!</string>
<string name="msg_pse_error_key_sign">Geselecteerde sleutel kan geen gegevens ondertekenen!</string>
<string name="msg_pse_error_sign_key">Fout bij ophalen van ondertekeningssleutel!</string>
<string name="msg_pse_error_nfc">NFC-gegevensfout!</string>
- <string name="msg_pse_error_no_passphrase">Geen wachtwoord gegeven!</string>
+ <string name="msg_pse_error_no_passphrase">Geen wachtwoord opgegeven!</string>
<string name="msg_pse_error_pgp">Interne OpenPGP-fout!</string>
<string name="msg_pse_error_sig">OpenPGP-ondertekeningsuitzondering tegengekomen!</string>
<string name="msg_pse_error_unlock">Onbekende fout bij ontgrendelen van sleutel!</string>
@@ -990,7 +1073,7 @@
<string name="msg_pse_key_warn">Slechte sleutel voor versleuteling: %s</string>
<string name="msg_pse_ok">Ondertekening/versleuteling geslaagd!</string>
<string name="msg_pse_pending_nfc">NFC token vereist, gebruikersinput gevraagd…</string>
- <string name="msg_pse_pending_passphrase">Wachtwoord vereist, gebruikersinput gevraagd…</string>
+ <string name="msg_pse_pending_passphrase">Wachtwoord vereist, gebruikersinput wordt gevraagd…</string>
<string name="msg_pse_signing">Bezig met ondertekenen van gegevens (zonder versleuteling)</string>
<string name="msg_pse_signing_cleartext">Bezig met aanmaken van platte tekst-ondertekening</string>
<string name="msg_pse_signing_detached">Bezig met aanmaken van vrijstaande ondertekening</string>
@@ -1010,9 +1093,9 @@
<string name="msg_crt_error_master_not_found">Hoofdsleutel niet gevonden!</string>
<string name="msg_crt_error_nothing">Geen sleutels gecertificeerd!</string>
<string name="msg_crt_error_unlock">Fout bij ontgrendelen van hoofdsleutel!</string>
- <string name="msg_crt_error_divert">Certificatie met NFC wordt (nog) niet ondersteund!</string>
<string name="msg_crt">Bezig met certificeren van sleutelbossen</string>
<string name="msg_crt_master_fetch">Bezig met ophalen van gecertificeerde hoofdsleutel</string>
+ <string name="msg_crt_nfc_return">Terugkeren naar NFC-scherm</string>
<string name="msg_crt_save">Bezig met opslaan van gecertificeerde sleutel %s</string>
<string name="msg_crt_saving">Bezig met opslaan van sleutelbossen</string>
<string name="msg_crt_unlock">Bezig met ontgrendelen van hoofdsleutel</string>
@@ -1020,6 +1103,7 @@
<string name="msg_crt_warn_not_found">Sleutel niet gevonden!</string>
<string name="msg_crt_warn_cert_failed">Aanmaken van certificaat mislukt!</string>
<string name="msg_crt_warn_save_failed">Opslaan mislukt!</string>
+ <string name="msg_crt_warn_upload_failed">Uploaden mislukt!</string>
<string name="msg_crt_upload_success">Sleutel succesvol geüpload naar server</string>
<plurals name="msg_import">
<item quantity="one">Bezig met importeren van sleutel</item>
@@ -1028,13 +1112,14 @@
<string name="msg_import_fetch_error_decode">Fout bij decoderen van opgehaalde sleutelbos!</string>
<string name="msg_import_fetch_error">Sleutel kon niet opgehaald worden! (Netwerkproblemen?)</string>
<string name="msg_import_fetch_keybase">Bezig met ophalen van keybase.io: %s</string>
- <string name="msg_import_fetch_keyserver_error">Kon sleutel niet ophalen!</string>
+ <string name="msg_import_fetch_keyserver_error">Kon sleutel niet ophalen van sleutelservers: %s</string>
<string name="msg_import_fetch_keyserver">Bezig met ophalen van sleutelserver: %s</string>
<string name="msg_import_fetch_keyserver_ok">Ophalen van sleutel geslaagd!</string>
<string name="msg_import_keyserver">Sleutelserver %s wordt gebruikt</string>
<string name="msg_import_fingerprint_error">Vingerafdruk van opgehaalde sleutel komt niet overeen met verwachte vingerafdruk!</string>
<string name="msg_import_fingerprint_ok">Vingerafdrukcontrole OKÉ</string>
<string name="msg_import_merge">Bezig met samenvoegen van opgehaalde gegevens</string>
+ <string name="msg_import_merge_error">Fout bij samenvoegen van opgehaalde gegevens!</string>
<string name="msg_import_error">Importeren mislukt!</string>
<string name="msg_import_error_io">Importeren mislukt door i/o-fout!</string>
<string name="msg_import_partial">Importeren geslaagd, met fouten!</string>
@@ -1045,6 +1130,7 @@
</plurals>
<string name="msg_export_all">Bezig met exporteren van alle sleutels</string>
<string name="msg_export_public">Bezig met exporteren van publieke sleutel %s</string>
+ <string name="msg_export_upload_public">Bezig met uploaden van publieke sleutel %s</string>
<string name="msg_export_secret">Bezig met exporteren van geheime sleutel %s</string>
<string name="msg_export_error_no_file">Geen bestandsnaam opgegeven!</string>
<string name="msg_export_error_fopen">Fout bij openen van bestand!</string>
@@ -1054,7 +1140,9 @@
<string name="msg_export_error_db">Databasefout!</string>
<string name="msg_export_error_io">Input/output-fout!</string>
<string name="msg_export_error_key">Fout bij voorwerken van sleutelgegevens!</string>
+ <string name="msg_export_error_upload">Fout bij uploaden van sleutel naar server! Controleer je internetverbinding</string>
<string name="msg_export_success">Exporteren geslaagd</string>
+ <string name="msg_export_upload_success">Uploaden naar sleutelserver geslaagd</string>
<string name="msg_del_error_empty">Niets om te verwijderen!</string>
<string name="msg_del_error_multi_secret">Geheime sleutels kunnen enkel individueel verwijderd worden!</string>
<plurals name="msg_del">
@@ -1084,6 +1172,14 @@
<string name="msg_download_too_many_responses">Zoekopdracht gaf te veel kandidaten. Gelieve je zoekopdracht te verfijnen!</string>
<string name="msg_download_query_too_short_or_too_many_responses">Geen of te veel sleutels werden gevonden. Gelieve je zoekopdracht te verfijnen!</string>
<string name="msg_download_query_failed">Er trad een fout op bij het zoeken naar sleutels.</string>
+ <!--Messages for Keybase Verification operation-->
+ <string name="msg_keybase_verification">Keybaseverificatie voor %s wordt geprobeerd</string>
+ <string name="msg_keybase_error_no_prover">Geen bewijschecker gevonden voor %s</string>
+ <string name="msg_keybase_error_fetching_evidence">Probleem bij ophalen van bewijs</string>
+ <string name="msg_keybase_error_key_mismatch">Sleutelvingerafdruk komt niet overeen met die in bewijspost</string>
+ <string name="msg_keybase_error_dns_fail">DNS TXT record ophalen mislukt</string>
+ <string name="msg_keybase_error_specific">%s</string>
+ <string name="msg_keybase_error_msg_payload_mismatch">Ontsleuteld bewijs komt niet overeen met verwachte waarde</string>
<!--Messages for Export Log operation-->
<string name="msg_export_log_start">Bezig met exporteren van log</string>
<string name="msg_export_log_error_fopen">Fout bij openen van bestand</string>
@@ -1091,17 +1187,25 @@
<string name="msg_export_log_error_writing">I/O-fout bij schrijven naar bestand!</string>
<string name="msg_export_log_success">Log succesvol geëxporteerd!</string>
<!--PassphraseCache-->
- <string name="passp_cache_notif_click_to_clear">Klik om de gecachete wachtwoorden te wissen</string>
- <string name="passp_cache_notif_n_keys">OpenKeychain heeft %d wachtwoorden gecachet</string>
- <string name="passp_cache_notif_keys">Gecachete wachtwoorden:</string>
- <string name="passp_cache_notif_clear">Wis cache</string>
+ <string name="passp_cache_notif_click_to_clear">Tik om wachtwoorden te wissen.</string>
+ <plurals name="passp_cache_notif_n_keys">
+ <item quantity="one">%d wachtwoord onthouden</item>
+ <item quantity="other">%d wachtwoorden onthouden</item>
+ </plurals>
+ <string name="passp_cache_notif_keys">Onthouden wachtwoorden</string>
+ <string name="passp_cache_notif_clear">Wachtwoorden wissen</string>
<string name="passp_cache_notif_pwd">Wachtwoord</string>
+ <!--Keyserver sync-->
<!--First Time-->
<string name="first_time_text1">Neem je privacy terug met OpenKeychain!</string>
<string name="first_time_create_key">Mijn sleutel aanmaken</string>
<string name="first_time_import_key">Sleutel importeren uit bestand</string>
<string name="first_time_yubikey">Gebruik YubiKey NEO</string>
<string name="first_time_skip">Setup overslaan</string>
+ <string name="first_time_blank_yubikey">Wil je deze lege YubiKey NEO gebruiken met OpenKeychain?\n\nNeem de YubiKey nu weg, je zal gevraagd worden deze terug boven te halen wanneer nodig!</string>
+ <string name="first_time_blank_yubikey_yes">Gebruik deze YubiKey</string>
+ <string name="backup_all">Alle sleutels + je eigen sleutels</string>
+ <string name="backup_public_keys">Alle sleutels</string>
<!--unsorted-->
<string name="section_certifier_id">Certificeer</string>
<string name="section_cert">Certificaat Details</string>
@@ -1118,7 +1222,7 @@
<string name="error_key_not_found">Sleutel niet gevonden!</string>
<string name="error_key_processing">Fout bij verwerken sleutel!</string>
<string name="key_stripped">gestript</string>
- <string name="key_divert">doorschakelen naar smartcard/NFC</string>
+ <string name="key_divert">doorschakelen naar smartcard</string>
<string name="key_no_passphrase">geen wachtwoord</string>
<string name="key_unavailable">niet beschikbaar</string>
<string name="secret_cannot_multiple">Je eigen sleutels kunnen enkel individueel verwijderd worden!</string>
@@ -1126,27 +1230,31 @@
<string name="unknown_algorithm">onbekend</string>
<string name="can_sign_not">kan niet ondertekenen</string>
<string name="error_no_encrypt_subkey">Geen codeer-subsleutel beschikbaar!</string>
- <string name="info_no_manual_account_creation">Maak OpenKeychain-accounts niet handmatig aan.\nVoor meer informatie, zie Help.</string>
<string name="contact_show_key">Toon sleutel (%s)</string>
<string name="swipe_to_update">Veeg naar beneden om van sleutelserver te updaten</string>
<string name="error_no_file_selected">Selecteer minstens een bestand om te versleutelen!</string>
- <string name="error_multi_not_supported">Opslaan van meerdere bestanden wordt niet ondersteund. Dit is een beperking van Android.</string>
+ <string name="error_multi_files">Opslaan van meerdere bestanden wordt niet ondersteund. Dit is een beperking van Android.</string>
+ <string name="error_multi_clipboard">Versleuteling van meerdere bestanden naar klembord wordt niet ondersteund.</string>
+ <string name="error_detached_signature">Alleen-ondertekenen-bewerking van binaire bestanden wordt niet ondersteund, kies ten minste één versleutelingssleutel.</string>
+ <string name="error_empty_text">Voer tekst in om ze te versleutelen!</string>
<string name="key_colon">Sleutel:</string>
<string name="exchange_description">Selecteer om een sleuteluitwisseling te starten het aantal deelnemers aan de rechterkant, en klik vervolgens op de knop \'Start uitwisseling\'.\n\nJe zal twee vragne gesteld worden om zeker te zijn dat enkel de juiste deelnemers zich in de uitwisseling bevinden en dat hun vingerafdrukken correct zijn.</string>
<string name="btn_start_exchange">Uitwisseling starten</string>
<string name="user_id_none"><![CDATA[<none>]]></string>
+ <!--Android Account-->
+ <string name="account_privacy_title">Privacy</string>
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
<string name="title_unlock_method">Kies een ontgrendelingsmethode</string>
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<string name="enter_passphrase">Voer wachtwoord in</string>
<string name="passphrase">Wachtwoord</string>
<string name="noPassphrase">Geen wachtwoord</string>
<string name="no_passphrase_set">Geen wachtwoord ingesteld</string>
- <string name="passphrases_match">Wachtwoorden komen niet overeen</string>
+ <string name="passphrases_match">Wachtwoorden komen overeen</string>
<string name="passphrase_saved">Wachtwoord opgeslagen</string>
<string name="passphrase_invalid">Wachtwoord ongeldig</string>
- <string name="missing_passphrase">Ontbrekend wachtwoord</string>
+ <string name="missing_passphrase">Wachtwoord ontbreekt</string>
<string name="passphrase_again">Opnieuw</string>
<string name="lockpattern">Vergrendelingspatroon</string>
<string name="lockpatternNFC">NFC + vergrendelingspatroon</string>
@@ -1161,4 +1269,58 @@
<string name="nfc_write_succesful">Succesvol geschreven op NFC-tag</string>
<string name="unlocked">Ontgrendeld</string>
<string name="nfc_settings">Instellingen</string>
+ <string name="snack_yubikey_view">Bekijken</string>
+ <string name="snack_yubikey_import">Importeren</string>
+ <string name="button_bind_key">Sleutel binden</string>
+ <string name="yubikey_serno">Serienummer: %s</string>
+ <string name="yubikey_key_holder">Sleutelhouder:</string>
+ <string name="yubikey_key_holder_not_set">Sleutelhouder: &lt;niet ingesteld&gt;</string>
+ <string name="yubikey_status_bound">YubiKey komt overeen en is gebonden aan sleutel</string>
+ <string name="yubikey_status_unbound">YubiKey komt overeen en kan worden gebonden aan sleutel</string>
+ <string name="yubikey_status_partly">YubiKey komt overeen en is gedeeltelijk gebonden aan sleutel</string>
+ <string name="yubikey_create">Hou de YubiKey tegen de achterkant van je toestel.</string>
+ <string name="btn_import">Importeren</string>
+ <string name="snack_yubi_other">Andere sleutel opgeslagen op YubiKey!</string>
+ <string name="error_nfc">NFC-fout: %s</string>
+ <plurals name="error_pin">
+ <item quantity="one">Ongeldige PIN!\n%d resterende poging.</item>
+ <item quantity="other">Ongeldige PIN!\n%d resterende pogingen.</item>
+ </plurals>
+ <string name="error_nfc_terminated">YubiKey in beëindigingsstaat.</string>
+ <string name="error_nfc_wrong_length">Ingevoerde PIN is te kort. PIN\'s zijn minstens 6 tekens lang.</string>
+ <string name="error_nfc_conditions_not_satisfied">Gebruiksvoorwaarden niet voldaan.</string>
+ <string name="error_nfc_security_not_satisfied">Veiligheidsstatus niet voldaan.</string>
+ <string name="error_nfc_authentication_blocked">PIN geblokkeerd na te veel pogingen.</string>
+ <string name="error_nfc_data_not_found">Sleutel of object niet gevonden.</string>
+ <string name="error_nfc_unknown">Onbekende fout</string>
+ <string name="error_nfc_try_again">Opnieuw proberen</string>
+ <string name="error_pin_nodefault">Standaard-PIN geweigerd!</string>
+ <string name="error_temp_file">Fout bij aanmaken van tijdelijk bestand.</string>
+ <string name="btn_delete_original">Oorspronkelijk bestand verwijderen</string>
+ <string name="snack_encrypt_filenames_on">Bestandsnamen <b>zijn</b> versleuteld.</string>
+ <string name="snack_encrypt_filenames_off">Bestandsnamen <b>zijn niet</b> versleuteld.</string>
+ <string name="snack_armor_on">Uitvoer geëncodeerd als tekst.</string>
+ <string name="snack_armor_off">Uitvoer geëncodeerd als binair.</string>
+ <string name="snack_compression_on">Compressie <b>ingeschakeld</b>.</string>
+ <string name="snack_compression_off">Compressie <b>uitgeschakeld</b>.</string>
+ <string name="error_loading_keys">Fout bij laden van sleutels!</string>
+ <string name="error_empty_log">(fout, leeg log)</string>
+ <string name="error_reading_text">Kon invoer niet lezen om te ontsleutelen!</string>
+ <string name="filename_unknown">&lt;geen bestandsnaam&gt;</string>
+ <string name="filename_unknown_text">&lt;platte tekstgegevens&gt;</string>
+ <string name="intent_show">Toon ondertekende/versleutelde inhoud</string>
+ <string name="view_internal">Bekijken in OpenKeychain</string>
+ <string name="error_preparing_data">Fout bij voorbereiden van gegevens!</string>
+ <string name="label_clip_title">Versleutelde gegevens</string>
+ <string name="progress_processing">Bezig met verwerken…</string>
+ <string name="error_saving_file">Fout bij opslaan van bestand!</string>
+ <string name="file_saved">Bestand opgeslagen!</string>
+ <string name="file_delete_ok">Oorspronkelijk bestand verwijderd.</string>
+ <string name="file_delete_none">Geen bestand verwijderd! (Misschien was het al verwijderd?)</string>
+ <string name="file_delete_exception">Oorspronkelijk bestand kon niet worden verwijderd!</string>
+ <string name="error_clipboard_empty">Klembord is leeg!</string>
+ <string name="error_clipboard_copy">Fout bij kopiëren van gegevens naar klembord!</string>
+ <string name="error_scan_fp">Fout bij scannen van vingerafdruk!</string>
+ <string name="error_scan_match">Vingerafdrukken komen niet overeen!</string>
+ <string name="error_expiry_past">Verloopdatum is in het verleden!</string>
</resources>
diff --git a/OpenKeychain/src/main/res/values-pl/strings.xml b/OpenKeychain/src/main/res/values-pl/strings.xml
index b0cf1abaa..bf46e5c42 100644
--- a/OpenKeychain/src/main/res/values-pl/strings.xml
+++ b/OpenKeychain/src/main/res/values-pl/strings.xml
@@ -4,44 +4,42 @@
http://developer.android.com/guide/topics/resources/string-resource.html (scroll down to "Escaping apostrophes and quotes").-->
<string name="app_name">OpenKeychain</string>
<!--title-->
+ <string name="title_encrypt_text">Szyfruj</string>
+ <string name="title_encrypt_files">Szyfruj</string>
<string name="title_decrypt">Odszyfruj</string>
- <string name="title_unlock">Odblokuj klucz</string>
<string name="title_add_subkey">Dodaj pod-klucz</string>
<string name="title_edit_key">Edytuj klucz</string>
<string name="title_preferences">Ustawienia</string>
<string name="title_api_registered_apps">Aplikacje</string>
- <string name="title_key_server_preference">Serwery kluczy</string>
+ <string name="title_key_server_preference">Serwery kluczy OpenPGP</string>
<string name="title_change_passphrase">Zmień hasło</string>
- <string name="title_share_fingerprint_with">Podziel się odciskiem klucza przez...</string>
- <string name="title_share_key">Podziel się kluczem przez...</string>
- <string name="title_share_file">Podziel się plikiem przez...</string>
+ <string name="title_share_fingerprint_with">Wyślij odcisk klucza przez...</string>
+ <string name="title_share_key">Wyślij klucz przez...</string>
+ <string name="title_share_file">Wyślij plik przez...</string>
+ <string name="title_share_message">Wyślij tekst przez...</string>
<string name="title_encrypt_to_file">Zaszyfruj do pliku</string>
<string name="title_decrypt_to_file">Odszyfruj do pliku</string>
<string name="title_import_keys">Importuj klucze</string>
- <string name="title_export_key">Eksportuj klucz</string>
- <string name="title_export_keys">Eksportuj klucze</string>
<string name="title_key_not_found">Nie znaleziono klucza</string>
<string name="title_send_key">Wyślij do serwera kluczy</string>
<string name="title_key_details">Szczegóły klucza</string>
<string name="title_help">Pomoc</string>
<string name="title_log_display">Logi</string>
<string name="title_exchange_keys">Wymień się kluczami</string>
- <string name="title_advanced_key_info">Zaawansowane informacje o kluczu</string>
+ <string name="title_manage_my_keys">Zarządzaj kluczami</string>
<!--section-->
<string name="section_user_ids">Tożsamości</string>
<string name="section_keys">Pod-klucze</string>
<string name="section_cloud_search">Szukanie w Chmurze</string>
- <string name="section_passphrase_cache">Pamięć cache hasła</string>
+ <string name="section_certify">Zatwierdź</string>
<string name="section_actions">Działania</string>
<string name="section_share_key">Klucz</string>
<string name="section_key_server">Serwer kluczy</string>
<string name="section_fingerprint">Odcisk klucza</string>
<string name="section_encrypt">Zaszyfruj</string>
- <string name="section_decrypt">Odszyfruj</string>
<!--button-->
<string name="btn_decrypt_verify_file">Odszyfruj, weryfikuj i zapisz plik</string>
<string name="btn_encrypt_share_file">Szyfruj i podziel się plikiem</string>
- <string name="btn_save">Zapisz</string>
<string name="btn_do_not_save">Anuluj</string>
<string name="btn_delete">Usuń</string>
<string name="btn_no_date">Bez wygaśnięcia</string>
@@ -52,31 +50,22 @@
<string name="btn_view_cert_key">Wyświetl klucz certyfikacji</string>
<string name="btn_create_key">Utwórz klucz</string>
<string name="btn_add_files">Dodaj plik(i)</string>
- <string name="btn_add_share_decrypted_text">Podziel się odszyfrowanym tekstem</string>
- <string name="btn_decrypt_clipboard">Odszyfruj ze schowka</string>
- <string name="btn_decrypt_and_verify">oraz zweryfikuj podpisy</string>
- <string name="btn_decrypt_files">Odszyfruj pliki</string>
<string name="btn_encrypt_files">Zaszyfruj pliki</string>
<string name="btn_encrypt_text">Zaszyfruj tekst</string>
<!--menu-->
<string name="menu_preferences">Ustawienia</string>
<string name="menu_help">Pomoc</string>
- <string name="menu_export_key">Eksportuj do pliku</string>
<string name="menu_delete_key">Usuń klucz</string>
<string name="menu_search">Szukaj</string>
<string name="menu_beam_preferences">Ustawienia Beam</string>
<string name="menu_encrypt_to">Zaszyfruj do...</string>
<string name="menu_select_all">Wybierz wszystko</string>
<string name="menu_export_all_keys">Eksportuj wszystkie klucze</string>
- <string name="menu_advanced">Pokaż zaawansowane informacje</string>
<!--label-->
<string name="label_file">Plik</string>
<string name="label_files">Plik(i)</string>
<string name="label_file_colon">Plik:</string>
- <string name="label_no_passphrase">Brak hasła</string>
- <string name="label_passphrase">Hasło</string>
<string name="label_unlock">Odblokowywanie...</string>
- <string name="label_passphrase_again">Powtórz hasło</string>
<string name="label_algorithm">Algorytm</string>
<string name="label_ascii_armor">Plik ASCII Armor</string>
<string name="label_file_ascii_armor">Włącz ASCII Armor</string>
@@ -85,16 +74,11 @@
<string name="label_use_default_yubikey_pin">Użyj domyślnego PIN-u YubiKey</string>
<string name="label_use_num_keypad_for_yubikey_pin">Użyj klawiatury numerycznej dla PIN-u YubiKey</string>
<string name="label_label_use_default_yubikey_pin_summary">Używa domyślnego PIN-u (123456) do dostępu do YubiKeys przez NFC</string>
- <string name="label_asymmetric_from">Podpisane przez:</string>
<string name="label_to">Szyfruj do:</string>
<string name="label_delete_after_decryption">Usuń po odszyfrowaniu</string>
<string name="label_encryption_algorithm">Algorytm szyfrowania</string>
<string name="label_hash_algorithm">Funkcja skrótu</string>
- <string name="label_symmetric">Szyfruj z hasłem</string>
- <string name="label_passphrase_cache_ttl">Czas pamięci cache</string>
- <string name="label_passphrase_cache_subs">Pamiętaj hasła względem pod-klucza:</string>
<string name="label_file_compression">Kompresja pliku</string>
- <string name="label_keyservers">Serwery kluczy</string>
<string name="label_key_id">Identyfikator klucza</string>
<string name="label_creation">Utworzenia</string>
<string name="label_expiry">Wygaśnięcia</string>
@@ -108,8 +92,13 @@
<string name="label_send_key">Synchronizuj z chmurą</string>
<string name="label_fingerprint">Odcisk</string>
<string name="expiry_date_dialog_title">Ustaw datę wygaśnięcia</string>
- <string name="label_first_keyserver_is_used">(Pierwszy serwer kluczy jest preferowany)</string>
<string name="label_preferred">preferowany</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <!--proxy type choices and values-->
+ <!--OrbotHelper strings-->
+ <!--InstallDialogFragment strings-->
+ <!--StartOrbotDialogFragment strings-->
<string name="user_id_no_name">&lt;bez nazwy&gt;</string>
<string name="none">&lt;żaden&gt;</string>
<plurals name="n_keys">
@@ -151,19 +140,12 @@
<string name="flag_encrypt">Szyfruj</string>
<string name="flag_authenticate">Uwierzytelniaj</string>
<!--sentences-->
- <string name="wrong_passphrase">Nieprawidłowe hasło.</string>
<string name="no_filemanager_installed">Nie zainstalowano żadnego kompatybilnego menadżera plików.</string>
- <string name="passphrases_do_not_match">Hasła nie pasują do siebie</string>
- <string name="passphrase_must_not_be_empty">Podaj hasło.</string>
<string name="passphrase_for_symmetric_encryption">Szyfrowanie symetryczne.</string>
- <string name="passphrase_for">Podaj hasło dla \'%s\'</string>
<string name="pin_for">Wpisz PIN dla \'%s\'</string>
- <string name="nfc_text">Trzymaj YubiKey z tyłu Twojego urządzenia.</string>
- <string name="no_file_selected">Najpierw wskaż plik.</string>
<string name="encrypt_sign_successful">Pomyślnie podpisano i/lub zaszyfrowano.</string>
<string name="encrypt_sign_clipboard_successful">Pomyslnie podpisano i/lub zaszyfrowano do schowka.</string>
<string name="select_encryption_key">Wybierz co najmniej jeden klucz szyfrujący.</string>
- <string name="select_encryption_or_signature_key">Wybierz co najmniej jeden klucz szyfrujący lub klucz podpisujący.</string>
<string name="also_export_secret_keys">Także eksportuj tajne klucze</string>
<string name="reinstall_openkeychain">Napotkałeś się na znany błąd w Androidzie. Proszę ponownie zainstalować OpenKeychain jeśli chcesz połączyć kontakty z kluczami.</string>
<string name="key_exported">Pomyślnie wyeksportowano 1 klucz.</string>
@@ -190,13 +172,10 @@
<string name="error_external_storage_not_ready">zewnętrzne urządzenie jest niegotowe</string>
<string name="error_key_size_minimum512bit">klucz musi mieć rozmiar co najmniej 512 bitów</string>
<string name="error_unknown_algorithm_choice">wybrano nieznany algorytm</string>
- <string name="error_user_id_no_email">nie znaleziono adresu email</string>
<string name="error_key_needs_a_user_id">potrzeba przynajmniej jeden tożsamości</string>
- <string name="error_no_signature_passphrase">nie podano hasła</string>
<string name="error_no_signature_key">nie podano klucza podpisu</string>
<string name="error_invalid_data">Brak treści szyfrowanej lub podpisanej OpenPGP!</string>
<string name="error_integrity_check_failed">Sprawdzanie spójności zakończone niepowodzeniem! Dane były modyfikowane!</string>
- <string name="error_wrong_passphrase">nieprawidłowe hasło</string>
<string name="error_could_not_extract_private_key">nie można wyodrębnić klucza prywatnego</string>
<!--errors without preceeding Error:-->
<string name="error_jelly_bean_needed">Potrzebujesz Androida 4.1 aby korzystać z Android NFC Beam</string>
@@ -206,11 +185,6 @@
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">Nie podpisane</string>
<string name="decrypt_result_invalid_signature">Nieprawidłowy podpis!</string>
- <string name="decrypt_result_signature_uncertified">Podpisane przez (bez certyfikacji)</string>
- <string name="decrypt_result_signature_certified">Podpisany przez</string>
- <string name="decrypt_result_signature_expired_key">Klucz już wygasł!</string>
- <string name="decrypt_result_signature_revoked_key">Klucz został unieważniony!</string>
- <string name="decrypt_result_signature_missing_key">Nieznany klucz publiczny</string>
<string name="decrypt_result_encrypted">Szyfrowane</string>
<string name="decrypt_result_not_encrypted">Nie szyfrowane</string>
<string name="decrypt_result_action_show">Pokaż</string>
@@ -242,7 +216,6 @@
<string name="progress_modify_subkeyrevoke">unieważnianie pod-kluczy...</string>
<string name="progress_modify_subkeystrip">usuwanie podkluczy...</string>
<string name="progress_modify_subkeyadd">dodawanie pod-kluczy...</string>
- <string name="progress_modify_passphrase">zmienianie hasła...</string>
<plurals name="progress_exporting_key">
<item quantity="one">eksportowanie klucza...</item>
<item quantity="few">eksportowanie kluczy...</item>
@@ -304,11 +277,10 @@
<string name="import_tab_qr_code">Kod QR/NFC</string>
<string name="import_import">Zaimportuj wybrane klucze</string>
<string name="import_qr_code_wrong">Kod QR zniekształcony! Spróbuj jeszcze raz!</string>
- <string name="import_qr_code_too_short_fingerprint">Odcisk klucza jest za krótki (&lt; 16 znaków)</string>
<string name="import_qr_code_button">Skanuj kod QR</string>
<string name="import_qr_code_text">Umieść kod QR przed kamerą!</string>
+ <!--Import from URL-->
<!--Generic result toast-->
- <string name="view_log">Szczegóły</string>
<string name="with_warnings">, z ostrzeżeniami</string>
<string name="with_cancelled">, aż anulowano</string>
<!--Import result toast-->
@@ -367,6 +339,7 @@
</plurals>
<string name="delete_nothing">Nie ma nic do usunięcia.</string>
<string name="delete_cancelled">Operacja usuwania anulowana.</string>
+ <!--Revoke result toast (snackbar)-->
<!--Certify result toast-->
<plurals name="certify_keys_ok">
<item quantity="one">Pomyślnie certyfikowano klucz%2$s.</item>
@@ -389,10 +362,6 @@
<string name="intent_send_encrypt">Zaszyfruj korzystając z OpenKeychain</string>
<string name="intent_send_decrypt">Deszyfruj korzystając z OpenKeychain</string>
<!--Remote API-->
- <string name="api_settings_show_info">Pokaż zaawansowane informacje</string>
- <string name="api_settings_hide_info">Ukryj zaawansowane informacje</string>
- <string name="api_settings_show_advanced">Pokaż zaawanowane ustawienia</string>
- <string name="api_settings_hide_advanced">Ukryj zaawansowane ustawienia</string>
<string name="api_settings_no_key">Nie wybrano klucza</string>
<string name="api_settings_select_key">Wybierz klucz</string>
<string name="api_settings_save">Zapisz</string>
@@ -402,8 +371,6 @@
<string name="api_settings_start">Uruchom aplikację</string>
<string name="api_settings_delete_account">Usuń konto</string>
<string name="api_settings_package_name">Nazwa paczki</string>
- <string name="api_settings_package_signature">Skrót SHA-256 podpisu paczki</string>
- <string name="api_settings_accounts">Konta (przestarzłe API)</string>
<string name="api_settings_settings">Ustawienia</string>
<string name="api_settings_key">Klucz konta:</string>
<string name="api_settings_accounts_empty">Brak kont połączonych z tą aplikacją.</string>
@@ -415,14 +382,15 @@ OSTRZEŻENIE: Jeżeli nie wiesz, czemu wyświetlił się ten komunikat, nie zezw
<string name="api_register_allow">Zezwól na dostęp</string>
<string name="api_register_disallow">Odmów dostępu</string>
<string name="api_register_error_select_key">Wybierz klucz!</string>
- <string name="api_select_pub_keys_missing_text">Nie znaleziono kluczy dla następujących tożsamości:</string>
- <string name="api_select_pub_keys_dublicates_text">Więcej niż jeden klucz istnieje dla następujących tożsamości:</string>
<string name="api_select_pub_keys_text">Proszę przejrzeć listę adresatów!</string>
<string name="api_select_pub_keys_text_no_user_ids">Prosimy o wybranie odbiorców!</string>
<string name="api_error_wrong_signature">Sprawdzanie podpisu zakończone niepowodzeniem! Czy zainstalowałeś tę aplikację z innego źródła? Jeżeli jesteś pewien, że nie jest to atak, odwołaj rejestrację teg aplikacji w OpenKeychain, a następnie zarejestruj ją ponownie.</string>
<!--Share-->
<string name="share_qr_code_dialog_title">Udostępnij przez kod QR</string>
<string name="share_nfc_dialog">Udostępnij przez NFC</string>
+ <!--retry upload dialog-->
+ <!--Delete or revoke private key dialog-->
+ <!--Delete Or Revoke Dialog spinner-->
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">1 klucz wybrany.</item>
@@ -455,7 +423,6 @@ OSTRZEŻENIE: Jeżeli nie wiesz, czemu wyświetlił się ten komunikat, nie zezw
<!--Key trust-->
<!--keybase proof stuff-->
<!--Edit key-->
- <string name="edit_key_action_change_passphrase">Zmień hasło</string>
<string name="edit_key_action_add_identity">Dodaj tożsamość</string>
<string name="edit_key_action_add_subkey">Dodaj pod-klucz</string>
<string name="edit_key_edit_user_id_title">Wybierz zadanie!</string>
@@ -468,24 +435,24 @@ OSTRZEŻENIE: Jeżeli nie wiesz, czemu wyświetlił się ten komunikat, nie zezw
</string-array>
<string name="edit_key_edit_user_id_revoked">Ta tożsamość została unieważniona. Tego już nie można cofnąć.</string>
<string name="edit_key_edit_subkey_title">Wybierz zadanie!</string>
- <string-array name="edit_key_edit_subkey">
- <item>Zmień wygaśnięcie</item>
- <item>Unieważnij Pod-klucz</item>
- <item>Usuń Pod-klucz</item>
- </string-array>
<string name="edit_key_new_subkey">nowy pod-klucz</string>
<string name="edit_key_select_flag">Prosimy o wybranie przynajmniej jeden flagi!</string>
<string name="edit_key_error_add_identity">Dodaj przynajmniej jedną tożsamość!</string>
<string name="edit_key_error_add_subkey">Dodaj przynajmniej jeden pod-klucz!</string>
<!--Create key-->
+ <string name="create_key_upload">Synchronizuj z chmurą</string>
<string name="create_key_empty">Dane pole jest wymagane</string>
- <string name="create_key_passphrases_not_equal">Hasła nie zgadzają się</string>
<string name="create_key_final_text">Wpisałeś następującą tożsamość:</string>
<string name="create_key_final_robot_text">Tworzenie klucza może zająć trochę czasu... w międzyczasie idź się napij kawy.</string>
<string name="create_key_rsa">(3 pod-klucze, RSA, 4096 bit)</string>
<string name="create_key_custom">(niestandardowa konfiguracja kluczy)</string>
- <string name="create_key_edit">Zmień klucz konfiguracji</string>
+ <string name="create_key_name_text">Wybierz imię powiązane z tym kluczem. Może to być imię i nazwisko, np: \'Jan Kowalski\', lub nick, np: \'Jaś\'.</string>
+ <string name="create_key_email_text">Wpisz twój główny adres email używany do bezpiecznej komunikacji.</string>
+ <string name="create_key_passphrase_text">Utwórz mocne hasło. W przypadku kradzieży urządzenia będzie chronić twój klucz.</string>
+ <string name="create_key_hint_full_name">Pełne imię lub przezwisko</string>
+ <string name="create_key_edit">Zmień ustawienia klucza</string>
<!--View key-->
+ <!--Add/Edit keyserver-->
<!--Navigation Drawer-->
<string name="nav_keys">Klucze</string>
<string name="nav_encrypt_decrypt">Zaszyfruj/Odszyfruj</string>
@@ -527,7 +494,6 @@ OSTRZEŻENIE: Jeżeli nie wiesz, czemu wyświetlił się ten komunikat, nie zezw
<string name="msg_cr_error_internal_pgp">Błąd wewnętrzny PGP!</string>
<!--modifySecretKeyRing-->
<string name="msg_mf_error_pgp">Błąd wewnętrzny PGP!</string>
- <string name="msg_mf_passphrase">Zmiana hasła dla pęku kluczy</string>
<string name="msg_mf_error_past_expiry">Data ważności nie może być przeszła!</string>
<string name="msg_mf_subkey_revoke">Unieważnianie pod-klucza %s</string>
<string name="msg_mf_uid_error_empty">ID użytkownika nie może być pusty!</string>
@@ -549,8 +515,6 @@ OSTRZEŻENIE: Jeżeli nie wiesz, czemu wyświetlił się ten komunikat, nie zezw
<string name="msg_dc_clear_signature_check">Weryfikowanie danych podpisu...</string>
<string name="msg_dc_clear_signature_ok">Sprawdzanie podpisu jest OK</string>
<string name="msg_dc_ok_meta_only">Tylko metadane były wymagane, pomijanie odszyfrowania</string>
- <string name="msg_dc_pass_cached">Użycie hasła z pamięci cache</string>
- <string name="msg_dc_pending_passphrase">Hasło wymagane, wymagana reakcja użytkownika...</string>
<string name="msg_dc">Rozpoczynanie operacji odszyfrowania...</string>
<!--Messages for VerifySignedLiteralData operation-->
<!--Messages for SignEncrypt operation-->
@@ -597,13 +561,10 @@ OSTRZEŻENIE: Jeżeli nie wiesz, czemu wyświetlił się ten komunikat, nie zezw
<item quantity="few">Część wczytanego pliku to poprawne obiekty OpenPGP, ale nie są kluczami OpenPGP</item>
<item quantity="other">Część wczytanego pliku to poprawne obiekty OpenPGP, ale nie są kluczami OpenPGP</item>
</plurals>
+ <!--Messages for Keybase Verification operation-->
<!--Messages for Export Log operation-->
<!--PassphraseCache-->
- <string name="passp_cache_notif_click_to_clear">Kliknij, aby wyczyścić zapamiętane hasła</string>
- <string name="passp_cache_notif_n_keys">OpenKeychain zapamiętał %d haseł</string>
- <string name="passp_cache_notif_keys">Zapamiętane hasła:</string>
- <string name="passp_cache_notif_clear">Wyczyść pamięć cache</string>
- <string name="passp_cache_notif_pwd">Hasło</string>
+ <!--Keyserver sync-->
<!--First Time-->
<string name="first_time_text1">Weź prywatność w swoje ręce używając OpenKeychain!</string>
<string name="first_time_skip">Pomiń ustawienia</string>
@@ -619,7 +580,6 @@ OSTRZEŻENIE: Jeżeli nie wiesz, czemu wyświetlił się ten komunikat, nie zezw
<string name="error_key_not_found">Nie znaleziono klucza!</string>
<string name="error_key_processing">Błąd przy przetwarzaniu klucza!</string>
<string name="key_stripped">zredukowany</string>
- <string name="key_no_passphrase">brak hasła</string>
<string name="key_unavailable">niedostepne</string>
<string name="secret_cannot_multiple">Twoje klucze mogą być usuwane tylko pojedynczo!</string>
<string name="title_view_cert">Zweryfikuj szczegóły certyfikatu</string>
@@ -631,8 +591,9 @@ OSTRZEŻENIE: Jeżeli nie wiesz, czemu wyświetlił się ten komunikat, nie zezw
<string name="error_no_file_selected">Wybierz przynajmniej jeden plik, aby szyfrować!</string>
<string name="key_colon">Klucz:</string>
<string name="btn_start_exchange">Rozpocznij wymianę</string>
+ <!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
</resources>
diff --git a/OpenKeychain/src/main/res/values-pt/strings.xml b/OpenKeychain/src/main/res/values-pt/strings.xml
deleted file mode 100644
index 518b80f47..000000000
--- a/OpenKeychain/src/main/res/values-pt/strings.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<resources>
- <!--GENERAL: Please put all strings inside quotes as described in example 1 on
- http://developer.android.com/guide/topics/resources/string-resource.html (scroll down to "Escaping apostrophes and quotes").-->
- <!--title-->
- <!--section-->
- <!--button-->
- <!--menu-->
- <!--label-->
- <!--choice-->
- <!--key flags-->
- <!--sentences-->
- <!--errors
- no punctuation, all lowercase,
- they will be put after "error_message", e.g. "Error: file not found"-->
- <!--errors without preceeding Error:-->
- <!--results shown after decryption/verification-->
- <!--Add keys-->
- <!--progress dialogs, usually ending in '…'-->
- <!--action strings-->
- <!--key bit length selections-->
- <!--elliptic curve names-->
- <!--not in for now, see SaveKeyringParcel
- <string name="key_curve_bp_p256">"Brainpool P-256"</string>
- <string name="key_curve_bp_p384">"Brainpool P-384"</string>
- <string name="key_curve_bp_p512">"Brainpool P-512"</string>-->
- <!--compression-->
- <!--Help-->
- <!--Import-->
- <!--Generic result toast-->
- <!--Import result toast-->
- <!--Delete result toast-->
- <!--Certify result toast-->
- <!--Intent labels-->
- <!--Remote API-->
- <!--Share-->
- <!--Key list-->
- <!--Key view-->
- <!--Key trust-->
- <!--keybase proof stuff-->
- <!--Edit key-->
- <!--Create key-->
- <!--View key-->
- <!--Navigation Drawer-->
- <!--hints-->
- <!--certs-->
- <!--LogType log messages. Errors should have _ERROR_ in their name and end with a !-->
- <!--Import Public log entries-->
- <!--Import Secret log entries-->
- <!--Keyring Canonicalization log entries-->
- <!--Keyring merging log entries-->
- <!--createSecretKeyRing-->
- <!--modifySecretKeyRing-->
- <!--Consolidate-->
- <!--Edit Key (higher level than modify)-->
- <!--Promote key-->
- <!--Other messages used in OperationLogs-->
- <!--Messages for DecryptVerify operation-->
- <!--Messages for VerifySignedLiteralData operation-->
- <!--Messages for SignEncrypt operation-->
- <!--Messages for PgpSignEncrypt operation-->
- <!--Messages for Export Log operation-->
- <!--PassphraseCache-->
- <!--First Time-->
- <!--unsorted-->
- <!--Passphrase wizard-->
- <!--TODO: rename all the things!-->
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
- <!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
-</resources>
diff --git a/OpenKeychain/src/main/res/values-ro/strings.xml b/OpenKeychain/src/main/res/values-ro/strings.xml
deleted file mode 100644
index 518b80f47..000000000
--- a/OpenKeychain/src/main/res/values-ro/strings.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<resources>
- <!--GENERAL: Please put all strings inside quotes as described in example 1 on
- http://developer.android.com/guide/topics/resources/string-resource.html (scroll down to "Escaping apostrophes and quotes").-->
- <!--title-->
- <!--section-->
- <!--button-->
- <!--menu-->
- <!--label-->
- <!--choice-->
- <!--key flags-->
- <!--sentences-->
- <!--errors
- no punctuation, all lowercase,
- they will be put after "error_message", e.g. "Error: file not found"-->
- <!--errors without preceeding Error:-->
- <!--results shown after decryption/verification-->
- <!--Add keys-->
- <!--progress dialogs, usually ending in '…'-->
- <!--action strings-->
- <!--key bit length selections-->
- <!--elliptic curve names-->
- <!--not in for now, see SaveKeyringParcel
- <string name="key_curve_bp_p256">"Brainpool P-256"</string>
- <string name="key_curve_bp_p384">"Brainpool P-384"</string>
- <string name="key_curve_bp_p512">"Brainpool P-512"</string>-->
- <!--compression-->
- <!--Help-->
- <!--Import-->
- <!--Generic result toast-->
- <!--Import result toast-->
- <!--Delete result toast-->
- <!--Certify result toast-->
- <!--Intent labels-->
- <!--Remote API-->
- <!--Share-->
- <!--Key list-->
- <!--Key view-->
- <!--Key trust-->
- <!--keybase proof stuff-->
- <!--Edit key-->
- <!--Create key-->
- <!--View key-->
- <!--Navigation Drawer-->
- <!--hints-->
- <!--certs-->
- <!--LogType log messages. Errors should have _ERROR_ in their name and end with a !-->
- <!--Import Public log entries-->
- <!--Import Secret log entries-->
- <!--Keyring Canonicalization log entries-->
- <!--Keyring merging log entries-->
- <!--createSecretKeyRing-->
- <!--modifySecretKeyRing-->
- <!--Consolidate-->
- <!--Edit Key (higher level than modify)-->
- <!--Promote key-->
- <!--Other messages used in OperationLogs-->
- <!--Messages for DecryptVerify operation-->
- <!--Messages for VerifySignedLiteralData operation-->
- <!--Messages for SignEncrypt operation-->
- <!--Messages for PgpSignEncrypt operation-->
- <!--Messages for Export Log operation-->
- <!--PassphraseCache-->
- <!--First Time-->
- <!--unsorted-->
- <!--Passphrase wizard-->
- <!--TODO: rename all the things!-->
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
- <!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
-</resources>
diff --git a/OpenKeychain/src/main/res/values-ru/strings.xml b/OpenKeychain/src/main/res/values-ru/strings.xml
index ce0911af5..55a5afa5e 100644
--- a/OpenKeychain/src/main/res/values-ru/strings.xml
+++ b/OpenKeychain/src/main/res/values-ru/strings.xml
@@ -7,12 +7,11 @@
<string name="title_encrypt_text">Зашифровать текст</string>
<string name="title_encrypt_files">Зашифровать файлы</string>
<string name="title_decrypt">Расшифровать</string>
- <string name="title_unlock">Разблокировать ключ</string>
<string name="title_add_subkey">Добавить подключ</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_key_server_preference">Серверы OpenPGP</string>
<string name="title_change_passphrase">Изменить пароль</string>
<string name="title_share_fingerprint_with">Отправить отпечаток...</string>
<string name="title_share_key">Отправить ключ...</string>
@@ -21,8 +20,8 @@
<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_export_key">Резервный ключ</string>
+ <string name="title_export_keys">Резервные ключи</string>
<string name="title_key_not_found">Ключ не найден</string>
<string name="title_send_key">Загрузить на сервер ключей</string>
<string name="title_certify_key">Сертифицировать ключ</string>
@@ -30,29 +29,37 @@
<string name="title_help">Помощь</string>
<string name="title_log_display">Журнал</string>
<string name="title_exchange_keys">Обмен ключами</string>
- <string name="title_advanced_key_info">Детальная информация о ключе</string>
+ <string name="title_advanced_key_info">Дополнительные сведения</string>
<string name="title_delete_secret_key">Удалить ВАШ ключ \'%s\'?</string>
+ <string name="title_export_log">Экспорт журнала</string>
+ <string name="title_manage_my_keys">Управление ключами</string>
<!--section-->
<string name="section_user_ids">Идентификаторы</string>
+ <string name="section_yubikey">YubiKey</string>
<string name="section_linked_system_contact">Связанные контакты</string>
<string name="section_should_you_trust">Должны ли Вы доверять этому ключу?</string>
<string name="section_proof_details">Подтвердить верификацию</string>
<string name="section_cloud_evidence">Подтвердить из облака</string>
<string name="section_keys">Доп. ключи</string>
<string name="section_cloud_search">Облачный поиск</string>
- <string name="section_passphrase_cache">Кэш пароля</string>
+ <string name="section_proxy_settings">Настройки прокси</string>
+ <string name="section_gui">Интерфейс</string>
+ <string name="section_certify">Подтвердить</string>
<string name="section_actions">Действия</string>
<string name="section_share_key">Ключ</string>
<string name="section_key_server">Сервер ключей</string>
<string name="section_fingerprint">Отпечаток ключа</string>
<string name="section_encrypt">Зашифровать</string>
- <string name="section_decrypt">Расшифровать</string>
+ <string name="section_decrypt">Расшифровка / Подтверждение</string>
<string name="section_current_expiry">Текущий срок годности</string>
<string name="section_new_expiry">Новый срок годности</string>
<!--button-->
<string name="btn_decrypt_verify_file">Расшифровать, проверить и сохранить файл</string>
<string name="btn_encrypt_share_file">Зашифровать и отправить файл</string>
+ <string name="btn_encrypt_save_file">Зашифровать и сохранить файл</string>
+ <string name="btn_save_file">Сохранить файл</string>
<string name="btn_save">Сохранить</string>
+ <string name="btn_view_log">Смотреть журнал</string>
<string name="btn_do_not_save">Отмена</string>
<string name="btn_delete">Удалить</string>
<string name="btn_no_date">Бесконечно</string>
@@ -62,28 +69,41 @@
<string name="btn_back">Назад</string>
<string name="btn_no">Нет</string>
<string name="btn_match">Отпечатки совпадают</string>
+ <string name="btn_share_encrypted_signed">Зашифровать и отправить текст</string>
+ <string name="btn_copy_encrypted_signed">Зашифровать и скопировать текст</string>
<string name="btn_view_cert_key">Просмотр ключа</string>
<string name="btn_create_key">Создать ключ</string>
<string name="btn_add_files">Добавить файл(ы)</string>
- <string name="btn_add_share_decrypted_text">Поделиться расшифрованным текстом</string>
- <string name="btn_decrypt_clipboard">Расшифровать текст из буфера обмена</string>
- <string name="btn_decrypt_and_verify">и проверить подписи</string>
- <string name="btn_decrypt_files">Расшифровать файлы</string>
+ <string name="btn_share_decrypted_text">Отправить расшифрованный текст</string>
+ <string name="btn_copy_decrypted_text">Копировать расшифрованный текст</string>
+ <string name="btn_decrypt_clipboard">Прочитать из буфера</string>
+ <string name="btn_decrypt_files">Выберите входной файл</string>
<string name="btn_encrypt_files">Зашифровать файлы</string>
<string name="btn_encrypt_text">Зашифровать текст</string>
+ <string name="btn_add_email">Добавить дополнительные адреса почты</string>
+ <string name="btn_unlock">Разблокировать</string>
+ <string name="btn_add_keyserver">Добавить</string>
+ <string name="btn_save_default">Сохранить по умолчанию</string>
+ <string name="btn_saved">Сохранено!</string>
<!--menu-->
<string name="menu_preferences">Настройки</string>
<string name="menu_help">Помощь</string>
- <string name="menu_export_key">Экспорт в файл</string>
+ <string name="menu_export_key">Поместить резервную копию в файл</string>
<string name="menu_delete_key">Удалить ключ</string>
+ <string name="menu_manage_keys">Управление ключами</string>
<string name="menu_search">Поиск</string>
<string name="menu_nfc_preferences">Настройки NFC</string>
<string name="menu_beam_preferences">Настройки Beam</string>
<string name="menu_encrypt_to">Зашифровать....</string>
<string name="menu_select_all">Выбрать все</string>
<string name="menu_export_all_keys">Экспорт всех ключей</string>
- <string name="menu_advanced">Подробные данные</string>
+ <string name="menu_update_all_keys">Обновить все ключи</string>
+ <string name="menu_advanced">Дополнительные сведения</string>
+ <string name="menu_certify_fingerprint">Подтвердить по отпечатку</string>
+ <string name="menu_export_log">Экспорт журнала</string>
+ <string name="menu_keyserver_add">Добавить</string>
<!--label-->
+ <string name="label_message">Текст</string>
<string name="label_file">Файл</string>
<string name="label_files">Файл(ы)</string>
<string name="label_file_colon">Файл:</string>
@@ -91,6 +111,7 @@
<string name="label_passphrase">Пароль</string>
<string name="label_unlock">Идет разблокировка...</string>
<string name="label_passphrase_again">Повторите пароль</string>
+ <string name="label_show_passphrase">Показать пароль</string>
<string name="label_algorithm">Алгоритм</string>
<string name="label_ascii_armor">ASCII формат</string>
<string name="label_file_ascii_armor">Использовать ASCII формат</string>
@@ -99,18 +120,19 @@
<string name="label_use_default_yubikey_pin">Использовать YubiKey PIN по умолчанию</string>
<string name="label_use_num_keypad_for_yubikey_pin">Использовать цифровую клавиатуру для YubiKey PIN</string>
<string name="label_label_use_default_yubikey_pin_summary">Использовать PIN по умолчанию (123456) для доступа к YubiKeys через NFC</string>
- <string name="label_asymmetric_from">Подписано:</string>
+ <string name="label_asymmetric_from">Подписать:</string>
<string name="label_to">Зашифровать для:</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_symmetric">Зашифровать паролем</string>
- <string name="label_passphrase_cache_ttl">Время хранения в кэше</string>
- <string name="label_passphrase_cache_subs">Кэшировать пароли по доп. ключу</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_keyservers">Серверы ключей</string>
+ <string name="label_keyservers">Выберите серверы OpenPGP</string>
<string name="label_key_id">ID ключа</string>
+ <string name="label_key_created">Создан ключ %s</string>
<string name="label_creation">Создан</string>
<string name="label_expiry">Годен до</string>
<string name="label_usage">Применение</string>
@@ -123,8 +145,48 @@
<string name="label_send_key">Синхронизировать с облаком</string>
<string name="label_fingerprint">Отпечаток</string>
<string name="expiry_date_dialog_title">Срок годности</string>
- <string name="label_first_keyserver_is_used">(Предпочесть первый сервер ключей)</string>
+ <string name="label_keyservers_title">Серверы ключей</string>
+ <string name="label_keyserver_settings_hint">Переместите для смены приоритета, зажмите чтобы редактировать/удалить</string>
+ <string name="label_selected_keyserver_title">Выбранные серверы ключей</string>
<string name="label_preferred">предпочитаемый</string>
+ <string name="label_enable_compression">Использовать сжатие</string>
+ <string name="label_encrypt_filenames">Шифровать имена файлов</string>
+ <string name="label_hidden_recipients">Скрыть получателей</string>
+ <string name="label_verify_keyserver">Подтвердить сервер ключей</string>
+ <string name="label_enter_keyserver_url">Введите адрес сервера ключей</string>
+ <string name="label_keyserver_dialog_delete">Удалить сервер ключей</string>
+ <string name="label_theme">Тема</string>
+ <string name="pref_keyserver">Серверы OpenPGP</string>
+ <string name="pref_keyserver_summary">Искать ключи на выбранных серверах OpenPGP (протокол HKP)</string>
+ <string name="pref_keybase">keybase.io</string>
+ <string name="pref_keybase_summary">Искать ключи на Keybase.io</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <string name="pref_proxy_tor_title">Использовать Tor</string>
+ <string name="pref_proxy_tor_summary">Требуется установка Orbot</string>
+ <string name="pref_proxy_normal_title">Использовать другой прокси</string>
+ <string name="pref_proxy_host_title">Хост прокси</string>
+ <string name="pref_proxy_host_err_invalid">Хост прокси не может быть пустым</string>
+ <string name="pref_proxy_port_title">Порт прокси</string>
+ <string name="pref_proxy_port_err_invalid">Введён неверный номер порта</string>
+ <string name="pref_proxy_type_title">Тип прокси</string>
+ <!--proxy type choices and values-->
+ <string name="pref_proxy_type_choice_http">HTTP</string>
+ <string name="pref_proxy_type_choice_socks">SOCKS</string>
+ <!--OrbotHelper strings-->
+ <string name="orbot_ignore_tor">Не использовать Tor</string>
+ <!--InstallDialogFragment strings-->
+ <string name="orbot_install_dialog_title">Установить Orbot чтобы использовать Tor?</string>
+ <string name="orbot_install_dialog_install">Установка</string>
+ <string name="orbot_install_dialog_content">Вам необходимо установить Orbot и пустить трафик прокси через него. Хотите установить Orbot?</string>
+ <string name="orbot_install_dialog_cancel">Отмена</string>
+ <string name="orbot_install_dialog_ignore_tor">Не использовать Tor</string>
+ <!--StartOrbotDialogFragment strings-->
+ <string name="orbot_start_dialog_title">Запустить Orbot?</string>
+ <string name="orbot_start_btn">Запустить Orbot</string>
+ <string name="orbot_start_dialog_start">Запустить Orbot</string>
+ <string name="orbot_start_dialog_cancel">Отмена</string>
+ <string name="orbot_start_dialog_ignore_tor">Не использовать Tor</string>
<string name="user_id_no_name">&lt;нет имени&gt;</string>
<string name="none">&lt;нет&gt;</string>
<plurals name="n_keys">
@@ -162,29 +224,36 @@
<string name="filemanager_title_open">Открыть...</string>
<string name="error">Ошибка</string>
<string name="error_message">Ошибка: %s</string>
+ <string name="theme_dark">Темная</string>
+ <string name="theme_light">Светлая</string>
<!--key flags-->
<string name="flag_certify">Сертификация</string>
<string name="flag_sign">Подписание</string>
<string name="flag_encrypt">Шифрование</string>
<string name="flag_authenticate">Аутентификация</string>
<!--sentences-->
- <string name="wrong_passphrase">Неправ. пароль</string>
+ <string name="wrong_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="passphrase_for">Введите пароль для \'%s\'</string>
<string name="pin_for">Введите PIN для
\'%s\'</string>
<string name="yubikey_pin_for">Введите PIN для доступа к YubiKey для
\'%s\'</string>
- <string name="nfc_text">Держите YubiKey возле задней части вашего устройства.</string>
- <string name="no_file_selected">Сначала выберите файл.</string>
+ <string name="file_delete_confirmation_title">Удалить исходные файлы?</string>
+ <string name="file_delete_confirmation">Следующие файлы будут удалены:%s</string>
+ <string name="no_file_selected">Файл не выбран</string>
<string name="encrypt_sign_successful">Успешно подписано и/или зашифровано.</string>
<string name="encrypt_sign_clipboard_successful">Успешно подписано и/или зашифровано в буфер обмена.</string>
<string name="select_encryption_key">Укажите хотя бы один ключ.</string>
- <string name="select_encryption_or_signature_key">Выберите хотя бы один ключ для шифрования или подписи.</string>
+ <string name="error_no_encryption_or_signature_key">Выберите хотя бы один ключ для шифрования или подписи.</string>
+ <string name="specify_file_to_encrypt_to">Пожалуйста, укажите, в какой файл произвести шифрование.\nВНИМАНИЕ: Файл будет перезаписан, если он уже существует!</string>
+ <string name="specify_file_to_decrypt_to">Пожалуйста, укажите, в какой файл произвести расшифровку.\nВНИМАНИЕ: Файл будет перезаписан, если он уже существует!</string>
+ <string name="key_deletion_confirmation_multi">Вы правда хотите удалить выбранные ключи?</string>
+ <string name="secret_key_deletion_confirmation">После удаления будет невозможно прочесть сообщения зашифрованные данным ключом и утрачены все подтверждения ключей выполненные с его помощью!</string>
+ <string name="public_key_deletetion_confirmation">Удалить ключ \'%s\'?</string>
<string name="also_export_secret_keys">Экспортировать секретные ключи</string>
<string name="reinstall_openkeychain">Вы столкнулись с багом Андроид. Пожалуйста, переустановите OpenKeychain чтобы связать ваши контакты и ключи. </string>
<string name="key_exported">Успешный экспорт 1 ключа.</string>
@@ -192,21 +261,25 @@
<string name="no_keys_exported">Ключи не были экспортированы.</string>
<string name="key_creation_el_gamal_info">Прим.: только вторичные ключи поддерживают ElGamal.</string>
<string name="key_not_found">Не удается найти ключ %08X.</string>
+ <string name="specify_file_to_export_log_to">Пожалуйста, выберите файл в который произвести экспорт.\nВНИМАНИЕ! Файл будет перезаписан если он уже существует!</string>
<string name="list_empty">Список пуст!</string>
<string name="nfc_successful">Ключ успешно передан через NFC!</string>
<string name="key_copied_to_clipboard">Ключ скопирован в буфер обмена!</string>
<string name="fingerprint_copied_to_clipboard">Отпечаток ключа скопирован в буфер обмена!</string>
+ <string name="select_key_to_certify">Выберите ключ, используемый для подтверждения!</string>
<string name="key_too_big_for_sharing">Ключ слишком большой для этого способа передачи!</string>
<string name="text_copied_to_clipboard">Тест скопирован в буфер обмена!</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">не удалены. Удалите их самостоятельно!</string>
+ <string name="error_file_added_already">%s уже был добавлен.</string>
<string name="error_file_not_found">файл не найден</string>
<string name="error_no_secret_key_found">нет подходящего секретного ключа</string>
<string name="error_external_storage_not_ready">внешняя память не готова</string>
<string name="error_key_size_minimum512bit">размер ключа должен быть не менее 512бит</string>
<string name="error_unknown_algorithm_choice">выбран неизвестный алгоритм</string>
- <string name="error_user_id_no_email">email не найден</string>
+ <string name="error_user_id_no_email">адрес почты не найден</string>
<string name="error_key_needs_a_user_id">необходим хотя бы один идентификатор</string>
<string name="error_no_signature_passphrase">пароль не задан</string>
<string name="error_no_signature_key">ключ для подписи не задан</string>
@@ -219,21 +292,22 @@
<string name="error_nfc_needed">Необходимо включить NFC!</string>
<string name="error_beam_needed">Необходимо включить Beam!</string>
<string name="error_nothing_import">Ключи не найдены!</string>
+ <string name="error_nothing_import_selected">Ключи для импорта не выбраны!</string>
<string name="error_contacts_key_id_missing">Ошибка извлечения идентификатора ключа из контактов!</string>
<string name="error_generic_report_bug">Выявлена ошибка. Пожалуйста, сообщите о ней разработчику.</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">Не подписано</string>
<string name="decrypt_result_invalid_signature">Неверная подпись!</string>
- <string name="decrypt_result_signature_uncertified">Подписано (не удостоверено!)</string>
- <string name="decrypt_result_signature_certified">Подписано:</string>
- <string name="decrypt_result_signature_expired_key">Срок годности ключа истёк!</string>
- <string name="decrypt_result_signature_revoked_key">Ключ аннулирован!</string>
- <string name="decrypt_result_signature_missing_key">Неизвестный открытый ключ</string>
+ <string name="decrypt_result_signature_uncertified">Подписано <b>неподтверждённым</b> ключом!</string>
+ <string name="decrypt_result_signature_secret">Подписано Вашим ключом</string>
+ <string name="decrypt_result_signature_certified">Подписано подтверждённым ключом</string>
+ <string name="decrypt_result_signature_expired_key">Подписано <b>просроченным</b> ключом!</string>
+ <string name="decrypt_result_signature_revoked_key">Подписано <b>отозванным</b> ключом!</string>
+ <string name="decrypt_result_signature_missing_key">Подписано <b>неизвестным ключом</b>!</string>
<string name="decrypt_result_encrypted">Зашифровано</string>
<string name="decrypt_result_not_encrypted">Не зашифровано</string>
<string name="decrypt_result_action_show">Показать</string>
<string name="decrypt_result_action_Lookup">Искать</string>
- <string name="decrypt_invalid_text">Неверная подпись или ключ был отозван или просрочен. Авторство текста невозможно проверить. Показать текст?</string>
<string name="decrypt_invalid_button">Мне понятны все риски, показать!</string>
<!--Add keys-->
<string name="add_keys_my_key">Мой ключ:</string>
@@ -243,6 +317,7 @@
<string name="progress_cancelling">отмена...</string>
<string name="progress_saving">сохранение...</string>
<string name="progress_importing">импорт...</string>
+ <string name="progress_updating">Обновление ключей...</string>
<string name="progress_exporting">экспорт...</string>
<string name="progress_uploading">загружается...</string>
<string name="progress_building_key">создание ключа...</string>
@@ -269,6 +344,7 @@
<item quantity="many">экспорт ключей...</item>
<item quantity="other">экспорт ключей...</item>
</plurals>
+ <string name="progress_start">подготовка действия...</string>
<string name="progress_extracting_signature_key">извлечение подписи ключа...</string>
<string name="progress_extracting_key">извлечение ключа...</string>
<string name="progress_preparing_streams">подготовка к передаче...</string>
@@ -288,6 +364,7 @@
<string name="progress_deleting">удаление ключей...</string>
<string name="progress_con_saving">объединение: сохранение в кэш...</string>
<string name="progress_con_reimport">объединение: реимпорт...</string>
+ <string name="progress_verifying_keyserver_url">подтверждение сервера ключей...</string>
<!--action strings-->
<string name="hint_cloud_search_hint">Искать через Имя, Email...</string>
<!--key bit length selections-->
@@ -317,6 +394,7 @@
<!--Help-->
<string name="help_tab_start">Начать</string>
<string name="help_tab_faq">ЧаВо</string>
+ <string name="help_tab_wot">Подтверждение ключей</string>
<string name="help_tab_nfc_beam">NFC Beam</string>
<string name="help_tab_changelog">Изменения</string>
<string name="help_tab_about">О программе</string>
@@ -328,11 +406,13 @@
<string name="import_tab_qr_code">QR код/NFC</string>
<string name="import_import">Импорт выбранных ключей</string>
<string name="import_qr_code_wrong">Некорректный QR код. Попробуйте снова!</string>
- <string name="import_qr_code_too_short_fingerprint">Отпечаток слишком коротнкий (&lt; 16 символов)</string>
+ <string name="import_qr_code_fp">Отпечаток нарушен или слишком короткий!</string>
+ <string name="import_qr_code_too_short_fingerprint">Отпечаток слишком короткий!</string>
<string name="import_qr_code_button">Сканировать QR код...</string>
<string name="import_qr_code_text">Расположите вашу камеру над QR кодом!</string>
+ <!--Import from URL-->
<!--Generic result toast-->
- <string name="view_log">Сведения</string>
+ <string name="snackbar_details">Сведения</string>
<string name="with_warnings">, с предупреждениями</string>
<string name="with_cancelled">, до отмены</string>
<!--Import result toast-->
@@ -347,6 +427,7 @@
<!--Delete result toast-->
<string name="delete_nothing">Нет данных для удаления!</string>
<string name="delete_cancelled">Удаление отменено.</string>
+ <!--Revoke result toast (snackbar)-->
<!--Certify result toast-->
<!--Intent labels-->
<string name="intent_decrypt_file">OpenKeychain: Расшифровать файл</string>
@@ -355,11 +436,12 @@
<string name="intent_send_decrypt">OpenKeychain: Расшифровать</string>
<!--Remote API-->
<string name="api_settings_show_info">Показать подробную информацию</string>
- <string name="api_settings_hide_info">Скрыть подробную информацию</string>
+ <string name="api_settings_hide_info">Скрыть дополнительную информацию</string>
<string name="api_settings_show_advanced">Показать расширенные настройки</string>
<string name="api_settings_hide_advanced">Скрыть расширенные настройки</string>
<string name="api_settings_no_key">Ключ не выбран</string>
<string name="api_settings_select_key">Выбрать ключ</string>
+ <string name="api_settings_create_key">Создать новый ключ</string>
<string name="api_settings_save">Сохранить</string>
<string name="api_settings_save_msg">Аккаунт сохранен</string>
<string name="api_settings_cancel">Отмена</string>
@@ -367,9 +449,8 @@
<string name="api_settings_start">Запустить приложение</string>
<string name="api_settings_delete_account">Удалить аккаунт</string>
<string name="api_settings_package_name">Наименование пакета</string>
- <string name="api_settings_package_signature">SHA-256 подписи пакета</string>
<string name="api_settings_accounts">Аккаунты (устаревший API)</string>
- <string name="api_settings_advanced">Подробная информация</string>
+ <string name="api_settings_advanced">Дополнительные сведения</string>
<string name="api_settings_allowed_keys">Разрешённые ключи</string>
<string name="api_settings_settings">Настройки</string>
<string name="api_settings_key">Ключ аккаунта:</string>
@@ -380,14 +461,17 @@
<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">Для данных лиц ключи не найдены:</string>
- <string name="api_select_pub_keys_dublicates_text">Для данных лиц имеется более одного ключа: </string>
<string name="api_select_pub_keys_text">Пожалуйста, проверьте получателей!</string>
<string name="api_select_pub_keys_text_no_user_ids">Пожалуйста, выберите получателей!</string>
<string name="api_error_wrong_signature">Проверка подписи пакета не удалась! Если вы установили программу из другого источника, отзовите для неё доступ к этой программе или обновите право доступа.</string>
+ <string name="api_select_sign_key_text">Пожалуйста, выберите свой ключ или создайте новый.</string>
+ <string name="api_select_keys_text">Ни один из доступных ключей не позволил расшифровать содержимое. Пожалуйста, выберите правильный ключ.</string>
<!--Share-->
<string name="share_qr_code_dialog_title">Отправить как QR код</string>
<string name="share_nfc_dialog">Отправить через NFC</string>
+ <!--retry upload dialog-->
+ <!--Delete or revoke private key dialog-->
+ <!--Delete Or Revoke Dialog spinner-->
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">1 ключ выбран.</item>
@@ -402,6 +486,7 @@
<string name="key_view_action_edit">Изменить ключ</string>
<string name="key_view_action_encrypt">Зашифровать текст</string>
<string name="key_view_action_encrypt_files">файлы</string>
+ <string name="key_view_action_certify">Подтвердить ключ</string>
<string name="key_view_action_update">Обновить с сервера ключей</string>
<string name="key_view_action_share_with">Отправить...</string>
<string name="key_view_action_share_nfc">Отправить через NFC</string>
@@ -420,24 +505,25 @@
<string name="user_id_info_invalid_title">Недействительно</string>
<string name="user_id_info_invalid_text">Что-то не так с идентификатором!</string>
<!--Key trust-->
+ <string name="key_trust_already_verified">Этот ключ уже подтверждён!</string>
<string name="key_trust_it_is_yours">Это один из ваших ключей!</string>
<string name="key_trust_revoked">Этот ключ отозван владельцем. Вы не должны доверять ему.</string>
<string name="key_trust_expired">У этого ключа истек срок годности. Вы не должны доверять ему.</string>
<string name="key_trust_start_cloud_search">Начать поиск</string>
<!--keybase proof stuff-->
- <string name="keybase_narrative_twitter">Отправить в Твиттер как</string>
- <string name="keybase_narrative_github">Это известно на GitHub как</string>
- <string name="keybase_narrative_dns">Управлять доменным именем(именами)</string>
- <string name="keybase_narrative_reddit">Опубликовать на Reddit как</string>
- <string name="keybase_narrative_hackernews">Опубликовать на Hacker News как</string>
- <string name="keybase_narrative_unknown">Неизвестный тип доказательства</string>
<string name="keybase_proof_failure">К сожалению это доказательство не может быть верифицировано.</string>
<string name="keybase_unknown_proof_failure">Неопознанная проблема с проверкой доказательства </string>
<string name="keybase_problem_fetching_evidence">Проблема с доказательством</string>
<string name="keybase_key_mismatch">Отпечаток ключа не совпадает с таковым в доказательной записи.</string>
+ <string name="keybase_dns_query_failure">Сбой получения записи DNS TXT</string>
<string name="keybase_no_prover_found">Не найдено проверки доказательства для </string>
<string name="keybase_message_payload_mismatch">Расшифрованная доказательная запись не соответствует ожидаемому значению</string>
+ <string name="keybase_message_fetching_data">Получение подтверждения</string>
+ <string name="keybase_proof_succeeded">Подтверждение успешно проверено!</string>
+ <string name="keybase_dns_proof">Запись DNS TXT</string>
+ <string name="keybase_web_site_proof">Текстовый файл</string>
<string name="keybase_reddit_proof">JSON file</string>
+ <string name="keybase_verify">Подтвердить</string>
<!--Edit key-->
<string name="edit_key_action_change_passphrase">Изменить пароль</string>
<string name="edit_key_action_add_identity">Добавить идентификатор</string>
@@ -452,27 +538,39 @@
</string-array>
<string name="edit_key_edit_user_id_revoked">Этот идентификатор был отозван. Это не может быть отменено.</string>
<string name="edit_key_edit_subkey_title">Выберите действие!</string>
- <string-array name="edit_key_edit_subkey">
- <item>Изменить срок годности</item>
- <item>Отозвать доп. ключ</item>
- <item>Отделить доп. ключ</item>
- </string-array>
<string name="edit_key_new_subkey">новый доп. ключ</string>
<string name="edit_key_select_flag">Пожалуйста, выберите хотя бы один флаг!</string>
<string name="edit_key_error_add_identity">Добавьте хотя бы один идентификатор!</string>
<string name="edit_key_error_add_subkey">Добавьте хотя бы один доп. ключ!</string>
<!--Create key-->
+ <string name="create_key_upload">Синхронизировать с облаком</string>
<string name="create_key_empty">Это обязательне поле</string>
- <string name="create_key_passphrases_not_equal">Пароли не совпадают.</string>
+ <string name="create_key_passphrases_not_equal">Пароли не совпадают</string>
<string name="create_key_final_text">Вы указали следующие данные:</string>
<string name="create_key_final_robot_text">Создание ключа займет некоторое время, можете пока выпить чашечку кофе...</string>
<string name="create_key_rsa">(3 доп. ключа, RSA, 4096 bit)</string>
<string name="create_key_custom">(произвольная конфигурация ключа)</string>
+ <string name="create_key_name_text">Выберите имя для данного ключа. Это может быть полное имя, например, \'Иван Петров\', или сокращенно, как например, \'Ванька\'.</string>
+ <string name="create_key_email_text">Введите Ваш главный адрес эл.почты, используемый для безопасной переписки.</string>
+ <string name="create_key_passphrase_text">Выберите надежный пароль. Он защитит Ваш ключ, в случае кражи вашего устройства.</string>
+ <string name="create_key_hint_full_name">Полное имя или псевдоним</string>
<string name="create_key_edit">Изменить конфигурацию ключа</string>
+ <string name="create_key_add_email">Введите адрес эл.почты</string>
+ <string name="create_key_email_already_exists_text">Почтовый адрес уже был добавлен</string>
+ <string name="create_key_email_invalid_email">Неправильный формат почтового адреса</string>
<!--View key-->
<string name="view_key_revoked">Аннулирован: Ключ не должен использоваться когда-либо в будущем!</string>
+ <string name="view_key_expired">Срок годности истёк: Контакт должен продлить срок!</string>
<string name="view_key_expired_secret">Срок годности истёк: Вы можете продлить срок, изменив ключ!</string>
<string name="view_key_my_key">Мой ключ</string>
+ <string name="view_key_verified">Подтверждённый ключ</string>
+ <string name="view_key_unverified">Не подтверждён: Сканируйте QR для подтверждения!</string>
+ <string name="view_key_fragment_no_system_contact">&lt;нет&gt;</string>
+ <!--Add/Edit keyserver-->
+ <string name="add_keyserver_dialog_title">Добавить сервер ключей</string>
+ <string name="add_keyserver_verified">Сервер ключей подтверждён!</string>
+ <string name="add_keyserver_without_verification">Сервер ключей добавлен без подтверждения.</string>
+ <string name="add_keyserver_invalid_url">Неправильный адрес!</string>
<!--Navigation Drawer-->
<string name="nav_keys">Ключи</string>
<string name="nav_encrypt_decrypt">Зашифровать/Расшифровать</string>
@@ -588,10 +686,8 @@
<string name="msg_is_pubring_generate">Формирование публичной связки из секретной связки</string>
<string name="msg_is_subkey_nonexistent">Доп. ключ %s недоступен в секретном ключе</string>
<string name="msg_is_subkey_ok">Секретный доп. ключ %s отмечен как доступный</string>
- <string name="msg_is_subkey_empty">Секретный доп. ключ %s отмечен как доступный, с пустым паролем</string>
<string name="msg_is_subkey_pin">Секретный доп. ключ %s отмечен как доступный, с PIN</string>
<string name="msg_is_subkey_stripped">Секретный доп. ключ %s отмечен как отделённый</string>
- <string name="msg_is_subkey_divert">Секретный доп. ключ %s отмечен как \'переключенный на смарт-карту/NFC\'</string>
<string name="msg_is_success_identical">Связка ключей не содержит новых данных, нечего делать</string>
<string name="msg_is_success">Успешно добавлена связка секретных ключей</string>
<!--Keyring Canonicalization log entries-->
@@ -641,6 +737,7 @@
<string name="msg_kc_uid_no_cert">Нет допустимого самостоятельного сертификата для ID \'%s\', удаление из связки</string>
<string name="msg_kc_uid_remove">Удаление неверного ID \'%s\'</string>
<string name="msg_kc_uid_dup">Удаление повторяющегося ID \'%s\'. Связка содержит их два. Это может привести к отсутствию сертификатов!</string>
+ <string name="msg_kc_uid_warn_encoding">ID пользователя не соответствует UTF-8!</string>
<string name="msg_kc_uat_jpeg">Обработка атрибута пользователя типа JPEG</string>
<string name="msg_kc_uat_unknown">Обработка атрибута пользователя неизвестного типа</string>
<string name="msg_kc_uat_bad_err">Удаление плохого самостоятельного сертификата для атрибута пользователя</string>
@@ -655,6 +752,7 @@
<string name="msg_kc_uat_revoke_old">Удаление устаревшего сертификата аннулирования для атрибута пользователя</string>
<string name="msg_kc_uat_no_cert">Нет допустимого самостоятельного сертификата для атрибута пользователя, удаление из связки</string>
<string name="msg_kc_uat_remove">Удаление неверного атрибута пользователя</string>
+ <string name="msg_kc_uat_warn_encoding">ID пользователя не соответствует UTF-8!</string>
<!--Keyring merging log entries-->
<string name="msg_mg_error_secret_dummy">Найден новый публичный доп. ключ, но фиктивное создание секретного доп. ключа не поддерживается!</string>
<string name="msg_mg_error_heterogeneous">Попытка объединить связки с различными отпечатками!</string>
@@ -687,13 +785,16 @@
<string name="msg_mf_error_integrity">Внутренняя ошибка, сбой проверки целостности!</string>
<string name="msg_mf_error_master_none">Не найден основной сертификат! (Все отозваны?)</string>
<string name="msg_mf_error_revoked_primary">Аннулированные идентификаторы не могут быть основными!</string>
+ <string name="msg_mf_error_noop">Выполнять нечего!</string>
<string name="msg_mf_error_pgp">Внутренняя ошибка OpenPGP!</string>
<string name="msg_mf_error_sig">Ошибка подписи!</string>
+ <string name="msg_mf_error_subkey_missing">Попытка работы с отсутствующим доп. ключом %s!</string>
<string name="msg_mf_master">Изменение основного сертификата</string>
- <string name="msg_mf_passphrase_key">Перешифрование доп. ключа %s новым паролем</string>
+ <string name="msg_mf_passphrase">Изменение пароля для связки ключей</string>
+ <string name="msg_mf_passphrase_empty_retry">Ошибка установки нового пароля, пробую снова используя старый пустой пароль</string>
<string name="msg_mf_primary_new">Создание нового сертификата для основного идентификатора</string>
<string name="msg_mf_subkey_change">Изменение доп. ключа %s</string>
- <string name="msg_mf_error_subkey_missing">Попытка работы с отсутствующим доп. ключом %s!</string>
+ <string name="msg_mf_require_passphrase">Для действий необходим пароль</string>
<string name="msg_mf_subkey_new">Добавление нового доп. ключа типа %s</string>
<string name="msg_mf_subkey_new_id">Идентификатор нового доп. ключа: %s</string>
<string name="msg_mf_error_past_expiry">Срок годности не может быть в прошлом!</string>
@@ -706,6 +807,8 @@
<!--Consolidate-->
<string name="msg_con">Консолидация базы данных</string>
<string name="msg_con_error_bad_state">Консолидация началась но база не кэширована! Это программная ошибка, пожалуйста, сообщите об этом.</string>
+ <string name="msg_con_save_secret">Сохранение секретной связки ключей</string>
+ <string name="msg_con_save_public">Сохранение публичной связки ключей</string>
<string name="msg_con_db_clear">Очистка базы данных</string>
<string name="msg_con_error_db">Ошибка открытия базы данных!</string>
<string name="msg_con_error_io_public">Ошибка записи публичных ключей в кэш!</string>
@@ -713,30 +816,31 @@
<string name="msg_con_error_public">Ошибка переимпортирования публичных ключей!</string>
<string name="msg_con_error_secret">Ошибка переимпортирования приватных ключей!</string>
<!--Edit Key (higher level than modify)-->
+ <string name="msg_ed">Выполнение операции ключа</string>
+ <string name="msg_ed_caching_new">Кэширование нового пароля</string>
<string name="msg_ed_error_key_not_found">Ключ не найден!</string>
<string name="msg_ed_success">Операция с ключом успешна!</string>
<!--Promote key-->
- <string name="msg_pr_error_already_secret">Этот ключ уже приватный!</string>
<string name="msg_pr_error_key_not_found">Ключ не найден!</string>
<!--Other messages used in OperationLogs-->
- <string name="msg_ek_error_divert">Редактирование NFC ключей (еще) не поддерживается!</string>
<string name="msg_ek_error_not_found">Ключ не найден!</string>
<!--Messages for DecryptVerify operation-->
<string name="msg_dc_clear_decompress">Распаковка сжатых данных</string>
<string name="msg_dc_clear_meta_size_unknown">Неизвестный размер файла</string>
<string name="msg_dc_clear_signature_bad">Проверка подписи НЕ ПРОЙДЕНА!</string>
- <string name="msg_dc_error_unsupported_hash_algo">Неподдерживаемый, и потенциально небезопасный алгоритм хэширования!</string>
<string name="msg_dc_clear_signature_check">Проверка подписи данных</string>
<string name="msg_dc_clear_signature_ok">Проверка подписи ПРОЙДЕНА</string>
<string name="msg_dc_clear_signature">Сохранение данных подписи</string>
- <string name="msg_dc_error_bad_passphrase">Ошибка разблокирования ключа, неверная фраза-пароль!</string>
+ <string name="msg_dc_error_bad_passphrase">Ошибка разблокировки ключа, неправильный пароль!</string>
<string name="msg_dc_error_extract_key">Неизвестная ошибка разблокировения ключа!</string>
<string name="msg_dc_error_no_data">Зашифрованные данные не найдены!</string>
<string name="msg_dc_error_no_key">Зашифрованные данные с известным приватным ключом не найдены!</string>
<string name="msg_dc_ok">Расшифрование/проверка закончена</string>
- <string name="msg_dc_pass_cached">Использование фразы-пароля из кэша</string>
+ <string name="msg_dc_pass_cached">Применяю кэшированный пароль</string>
+ <string name="msg_dc_pending_passphrase">Требуется пароль, жду действий пользователя...</string>
+ <string name="msg_dc">Расшифровка началась...</string>
+ <string name="msg_dc_sym">Обнаружена симметрично зашифрованная информация</string>
<string name="msg_dc_unlocking">Разблокировка секретного ключа</string>
- <string name="msg_dc_old_symmetric_encryption_algo">Был использован потенциально небезопасный алгоритм шифрования!</string>
<!--Messages for VerifySignedLiteralData operation-->
<string name="msg_vl_clear_signature_check">Проверка подписи данных</string>
<string name="msg_vl_ok">ОК</string>
@@ -754,14 +858,17 @@
<string name="msg_pse_clearsign_only">Подписание пустого текста не поддерживается!</string>
<string name="msg_pse_compressing">Подготовка сжатия</string>
<string name="msg_pse_encrypting">Шифрование данных</string>
- <string name="msg_pse_error_bad_passphrase">Неправильный пароль!</string>
- <string name="msg_pse_error_hash_algo">Запрашиваемый алгоритм хеширования не поддерживается этим ключом!</string>
+ <string name="msg_pse_error_bad_passphrase">Неверный пароль!</string>
<string name="msg_pse_error_io">Обнаружена исключительная ситуация ввода\вывода во время выполнения операции!</string>
<string name="msg_pse_error_key_sign">Выбранным ключом подписи нельзя подписать данные! </string>
<string name="msg_pse_error_sign_key">Ошибка при выборке ключа подписи!</string>
<string name="msg_pse_error_nfc">Ошибка данных NFC!</string>
- <string name="msg_pse_error_no_passphrase">Парольных фраз не найдено!</string>
<string name="msg_pse_error_pgp">Внутренняя ошибка OpenPGP!</string>
+ <string name="msg_pse_key_ok">Шифрование для ключа: %s</string>
+ <string name="msg_pse_ok">Подпись/Шифрование успешно произведены!</string>
+ <string name="msg_pse_pending_passphrase">Требуется пароль, жду действий пользователя...</string>
+ <string name="msg_pse">Подпись и/или шифрование начаты</string>
+ <string name="msg_pse_symmetric">Подготовка симметричного шифрования</string>
<string name="msg_crt_certifying">Генерация сертификатов</string>
<string name="msg_crt_error_master_not_found">Основной ключ не найден!</string>
<string name="msg_crt_error_nothing">Нет сертифицированных ключей!</string>
@@ -787,21 +894,23 @@
<string name="msg_del_error_empty">Нет данных для удаления!</string>
<string name="msg_del_error_multi_secret">Секретные ключи можно удалять только по одному!</string>
<string name="msg_acc_saved">Аккаунт сохранен</string>
+ <string name="msg_download_success">Загрузка завершена!</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">часть загруженного файла содержит данные OpenPGP, но это не ключ</item>
<item quantity="few">части загруженного файла содержат данные OpenPGP, но это не ключ</item>
<item quantity="many">части загруженного файла содержат данные OpenPGP, но это не ключ</item>
<item quantity="other">части загруженного файла содержат данные OpenPGP, но это не ключ</item>
</plurals>
+ <!--Messages for Keybase Verification operation-->
<!--Messages for Export Log operation-->
+ <string name="msg_export_log_start">Экспорт журнала...</string>
<string name="msg_export_log_error_fopen">Ошибка открытия файла</string>
<string name="msg_export_log_error_no_file">Не выбрано имя файла!</string>
<string name="msg_export_log_error_writing">Ошибка записи в файл!</string>
<string name="msg_export_log_success">Лог успешно экспортирован!</string>
<!--PassphraseCache-->
- <string name="passp_cache_notif_keys">Кэшированные пароли:</string>
- <string name="passp_cache_notif_clear">Очистить кэш</string>
<string name="passp_cache_notif_pwd">Пароль</string>
+ <!--Keyserver sync-->
<!--First Time-->
<string name="first_time_text1">Верните вашу приватность с помощью OpenKeychain!</string>
<string name="first_time_create_key">Создать свой ключ</string>
@@ -818,25 +927,39 @@
<string name="label_cert_type">Тип</string>
<string name="error_key_not_found">Ключ не найден!</string>
<string name="error_key_processing">Ошибка обработки ключа!</string>
- <string name="key_no_passphrase">Без фразы-пароля</string>
<string name="key_unavailable">недоступно</string>
<string name="title_view_cert">Просмотреть детали сертификации</string>
<string name="unknown_algorithm">неизв.</string>
<string name="can_sign_not">не для подписания</string>
<string name="error_no_encrypt_subkey">Нет доп. ключа для шифрования!</string>
<string name="contact_show_key">Показать ключ (%s)</string>
+ <string name="error_no_file_selected">Выберите хотя бы один файл для шифрования!</string>
+ <string name="error_empty_text">Введите текст для шифрования!</string>
<string name="key_colon">Ключ:</string>
<string name="btn_start_exchange">Начать обмен</string>
+ <!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
<string name="title_unlock_method">Выберите метод разблокировки</string>
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<string name="enter_passphrase">Введите пароль</string>
<string name="passphrase">Пароль</string>
+ <string name="noPassphrase">Без пароля</string>
+ <string name="no_passphrase_set">Пароль не установлен</string>
+ <string name="passphrases_match">Пароли совпадают</string>
+ <string name="passphrase_saved">Пароль сохранён</string>
<string name="passphrase_invalid">Неверный пароль</string>
<string name="passphrase_again">Еще раз</string>
+ <string name="lockpattern">Рисунок блокировки</string>
+ <string name="lockpatternNFC">NFC + рисунок блокировки</string>
+ <string name="unlock_method">Метод разблокировки</string>
+ <string name="set_passphrase">Задать пароль</string>
<string name="nfc_title">NFC</string>
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
<string name="unlocked">Разблокирован</string>
<string name="nfc_settings">Настройки</string>
+ <string name="snack_yubikey_view">Просмотр</string>
+ <string name="snack_yubikey_import">Импорт</string>
+ <string name="yubikey_key_holder">Владелец ключа:</string>
+ <string name="error_nfc">Ошибка NFC: %s</string>
</resources>
diff --git a/OpenKeychain/src/main/res/values-sl/strings.xml b/OpenKeychain/src/main/res/values-sl/strings.xml
index 4fee0250d..32f63b9b4 100644
--- a/OpenKeychain/src/main/res/values-sl/strings.xml
+++ b/OpenKeychain/src/main/res/values-sl/strings.xml
@@ -7,13 +7,10 @@
<string name="title_encrypt_text">Šifriraj</string>
<string name="title_encrypt_files">Šifriraj</string>
<string name="title_decrypt">Dešifriraj</string>
- <string name="title_unlock">Odkleni ključ</string>
<string name="title_add_subkey">Dodaj podključ</string>
<string name="title_edit_key">Uredi ključ</string>
<string name="title_preferences">Nastavitve</string>
<string name="title_api_registered_apps">Aplikacije</string>
- <string name="title_key_server_preference">Strežniki javnih ključev</string>
- <string name="title_change_passphrase">Spremeni geslo</string>
<string name="title_share_fingerprint_with">Deli prstni odtis z...</string>
<string name="title_share_key">Deli ključ z...</string>
<string name="title_share_file">Deli datoteko z...</string>
@@ -21,8 +18,6 @@
<string name="title_encrypt_to_file">Šifriraj v datoteko</string>
<string name="title_decrypt_to_file">Dešifriraj v datoteko</string>
<string name="title_import_keys">Uvozi ključe</string>
- <string name="title_export_key">Izvozi kluč</string>
- <string name="title_export_keys">Izvozi ključe</string>
<string name="title_key_not_found">Ključ ni bil najden</string>
<string name="title_send_key">Naloži na strežnik</string>
<string name="title_certify_key">Potrdi ključ</string>
@@ -30,7 +25,6 @@
<string name="title_help">Pomoč</string>
<string name="title_log_display">Dnevnik</string>
<string name="title_exchange_keys">Izmenjava ključev</string>
- <string name="title_advanced_key_info">Napredne informacije o ključu</string>
<string name="title_delete_secret_key">Izbrišem VAŠ ključ \'%s\'?</string>
<string name="title_export_log">Izvozi sistemsko zabeležbo</string>
<string name="title_manage_my_keys">Upravljanje mojih ključev</string>
@@ -42,21 +36,18 @@
<string name="section_cloud_evidence">Dokazila iz oblaka</string>
<string name="section_keys">Podključi</string>
<string name="section_cloud_search">Iskanje v oblaku</string>
- <string name="section_passphrase_cache">Hranjenje gesla v spominu</string>
<string name="section_certify">Potrdi</string>
<string name="section_actions">Ravnanja</string>
<string name="section_share_key">Ključ</string>
<string name="section_key_server">Strežnik</string>
<string name="section_fingerprint">Prstni odtis</string>
<string name="section_encrypt">Šifriraj</string>
- <string name="section_decrypt">Dešifriraj</string>
<string name="section_current_expiry">Trenuten datum poteka</string>
<string name="section_new_expiry">Nov datum poteka</string>
<!--button-->
<string name="btn_decrypt_verify_file">Dešifriraj, preveri in shrani</string>
<string name="btn_encrypt_share_file">Šifriraj in deli datoteko</string>
<string name="btn_encrypt_save_file">Šifriraj in shrani datoteko</string>
- <string name="btn_save">Shrani</string>
<string name="btn_do_not_save">Prekliči</string>
<string name="btn_delete">Izbriši</string>
<string name="btn_no_date">Ne preteče</string>
@@ -71,17 +62,12 @@
<string name="btn_view_cert_key">Poglej ključ za overjanje</string>
<string name="btn_create_key">Ustvari ključ</string>
<string name="btn_add_files">Dodaj datoteko</string>
- <string name="btn_add_share_decrypted_text">Deli dešifrirano besedilo</string>
- <string name="btn_decrypt_clipboard">Dešifriraj besedilo iz odložišča</string>
- <string name="btn_decrypt_and_verify">in preveri podpise</string>
- <string name="btn_decrypt_files">Dešifriraj datoteke</string>
<string name="btn_encrypt_files">Šifriraj datoteke</string>
<string name="btn_encrypt_text">Šifriraj besedilo</string>
<string name="btn_add_email">Dodaj elektronske naslove</string>
<!--menu-->
<string name="menu_preferences">Nastavitve</string>
<string name="menu_help">Pomoč</string>
- <string name="menu_export_key">Izvozi v datoteko</string>
<string name="menu_delete_key">Izbriši ključ</string>
<string name="menu_manage_keys">Upravljanje mojih ključev</string>
<string name="menu_search">Išči</string>
@@ -91,7 +77,6 @@
<string name="menu_select_all">Izberi vse</string>
<string name="menu_export_all_keys">Izvozi vse ključe</string>
<string name="menu_update_all_keys">Posodobi vse ključe</string>
- <string name="menu_advanced">Prikaži dodatne informacije</string>
<string name="menu_certify_fingerprint">Potrdi s primerjavo prstnih odtisov</string>
<string name="menu_export_log">Izvozi sistemsko zabeležbo</string>
<!--label-->
@@ -99,11 +84,7 @@
<string name="label_file">Datoteka</string>
<string name="label_files">Datoteke</string>
<string name="label_file_colon">Datoteka:</string>
- <string name="label_no_passphrase">Brez gesla</string>
- <string name="label_passphrase">Geslo</string>
<string name="label_unlock">Odklepanje...</string>
- <string name="label_passphrase_again">Ponovi geslo</string>
- <string name="label_show_passphrase">Prikaži geslo</string>
<string name="label_algorithm">Algoritem</string>
<string name="label_ascii_armor">ASCII oklep datotek</string>
<string name="label_file_ascii_armor">Omogoči ASCII ovoj</string>
@@ -112,18 +93,13 @@
<string name="label_use_default_yubikey_pin">Uporabi privzeto YubiKey PIN kodo</string>
<string name="label_use_num_keypad_for_yubikey_pin">Uporabi numerično tipkovnico za YubiKey PIN kodo</string>
<string name="label_label_use_default_yubikey_pin_summary">Uporablja privzeto PIN kodo (123456) za dostop do ključev YubiKey preko NFC</string>
- <string name="label_asymmetric_from">Podpisal:</string>
<string name="label_to">Šifriraj za:</string>
<string name="label_delete_after_encryption">Po dešifriranju izbriši datoteke</string>
<string name="label_delete_after_decryption">Izbriši po dešifriranju</string>
<string name="label_encryption_algorithm">Šifrirni algoritem</string>
<string name="label_hash_algorithm">Zgostitveni algoritem</string>
- <string name="label_symmetric">Šifriranje z geslom</string>
- <string name="label_passphrase_cache_ttl">Čas pomnjenja</string>
- <string name="label_passphrase_cache_subs">Pomni gesla glede na podključ</string>
<string name="label_message_compression">Kompresija besedila</string>
<string name="label_file_compression">Stiskanje datotek</string>
- <string name="label_keyservers">Strežniki</string>
<string name="label_key_id">ID ključa</string>
<string name="label_creation">Ustvarjanje</string>
<string name="label_expiry">Pretek</string>
@@ -137,15 +113,16 @@
<string name="label_send_key">Sinhroniziraj z oblakom</string>
<string name="label_fingerprint">Prstni odtis</string>
<string name="expiry_date_dialog_title">Določi datum poteka veljavnosti</string>
- <string name="label_first_keyserver_is_used">(Prednost ima prvi strežnik na seznamu)</string>
<string name="label_preferred">prednostni</string>
<string name="label_enable_compression">Omogoči kompresijo</string>
<string name="label_encrypt_filenames">Šifriraj imena datotek</string>
<string name="label_hidden_recipients">Skrij prejemnike</string>
- <string name="pref_keyserver">Preišči strežnik s ključi</string>
- <string name="pref_keyserver_summary">Preišči strežnik HKP</string>
- <string name="pref_keybase">Preišči strežnik Keybase.io</string>
- <string name="pref_keybase_summary">Preišči seznam Keybase.io</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <!--proxy type choices and values-->
+ <!--OrbotHelper strings-->
+ <!--InstallDialogFragment strings-->
+ <!--StartOrbotDialogFragment strings-->
<string name="user_id_no_name">&lt;brez imena&gt;</string>
<string name="none">&lt;nič&gt;</string>
<plurals name="n_keys">
@@ -189,26 +166,18 @@
<string name="flag_encrypt">Šifriraj</string>
<string name="flag_authenticate">Preveri avtentičnost</string>
<!--sentences-->
- <string name="wrong_passphrase">Napačno geslo.</string>
<string name="no_filemanager_installed">Nimate nameščenega združljivega upravljalnika datotek.</string>
- <string name="passphrases_do_not_match">Gesli se ne ujemata.</string>
- <string name="passphrase_must_not_be_empty">Vnesite geslo.</string>
<string name="passphrase_for_symmetric_encryption">Simetrično šifriranje.</string>
- <string name="passphrase_for">Vnesite geslo za \'%s\'</string>
<string name="pin_for">Vnesite PIN kodo za \'%s\'</string>
<string name="yubikey_pin_for">Vnesite PIN kodo za dostop YubiKey za \'%s\'</string>
- <string name="nfc_text">Prislonite YubiKey k hrbtišču mobilne naprave.</string>
<string name="file_delete_confirmation_title">Izbrišem izvirne datoteke?</string>
<string name="file_delete_confirmation">Izbrisane bodo naslednje datoteke:%s</string>
<string name="file_delete_successful">Izbrisanih je bilo %1$d od %2$d datotek.%3$s</string>
- <string name="no_file_selected">Najprej izberite datoteko.</string>
<string name="encrypt_sign_successful">Uspešno podpisano in/ali šifrirano.</string>
<string name="encrypt_sign_clipboard_successful">Uspešno podpisano in/ali šifrirano ter poslano v odložišče.</string>
<string name="select_encryption_key">Izberite vsaj en šifrirni ključ.</string>
- <string name="select_encryption_or_signature_key">Vnesite vsaj en šifrirni ključ ali ključ za podpisovanje.</string>
<string name="specify_file_to_encrypt_to">Določite datoteko, v katero želite šifrirati vsebino.\nPOZOR: če datoteka že obstaja, bo prepisana.</string>
<string name="specify_file_to_decrypt_to">Določite datoteko, v katero želite dešifrirati vsebino.\nPOZOR: če datoteka že obstaja, bo prepisana.</string>
- <string name="specify_file_to_export_to">Določite datoteko, v katero želite izvoziti vsebino.\nPOZOR: če datoteka že obstaja, bo prepisana.</string>
<string name="key_deletion_confirmation_multi">Ali zares želite izbrisati vse izbrane ključe?</string>
<string name="secret_key_deletion_confirmation">Po izbrisu ne bo več mogoče prebirati sporočil šifriranih s tem ključem! Izgubljene bodo tudi vse z njim narejene potrditve.</string>
<string name="public_key_deletetion_confirmation">Izbrišem ključ \'%s\'?</string>
@@ -243,13 +212,10 @@
<string name="error_external_storage_not_ready">zunanja shramba ni na voljo</string>
<string name="error_key_size_minimum512bit">velikost ključa mora biti najmanj 512bit</string>
<string name="error_unknown_algorithm_choice">neznana izbira algoritma</string>
- <string name="error_user_id_no_email">najti ni nobene e-pošte</string>
<string name="error_key_needs_a_user_id">potrebujete vsaj eno identiteto</string>
- <string name="error_no_signature_passphrase">dano ni bilo nobeno geslo</string>
<string name="error_no_signature_key">dan ni bil noben podpisni ključ</string>
<string name="error_invalid_data">Ne najdem nobene vsebine šifrirane ali podpisane z OpenPGP!</string>
<string name="error_integrity_check_failed">Preverba neokrnjenosti ni bila uspešna! Podatki so bili spremenjeni!</string>
- <string name="error_wrong_passphrase">napačno geslo</string>
<string name="error_could_not_extract_private_key">ne morem izvleči zasebnega ključa</string>
<!--errors without preceeding Error:-->
<string name="error_jelly_bean_needed">Za uporabo storitve NFC Beam potrebujete najmanj Android 4.1!</string>
@@ -261,16 +227,10 @@
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">Ni podpisano</string>
<string name="decrypt_result_invalid_signature">Neveljaven podpis!</string>
- <string name="decrypt_result_signature_uncertified">Podpisal (ne overjen!)</string>
- <string name="decrypt_result_signature_certified">Podpisal</string>
- <string name="decrypt_result_signature_expired_key">Ključ je pretekel!</string>
- <string name="decrypt_result_signature_revoked_key">Ključ je bil preklican!</string>
- <string name="decrypt_result_signature_missing_key">Neznan javni ključ</string>
<string name="decrypt_result_encrypted">Šifrirano</string>
<string name="decrypt_result_not_encrypted">Nešifrirano</string>
<string name="decrypt_result_action_show">Prikaži</string>
<string name="decrypt_result_action_Lookup">Poišči</string>
- <string name="decrypt_invalid_text">Nekaj ni v redu: ali je neveljaven podpis ali pa je ključ potekel oz. bil preklican. Zato se ne da reči, kdo je v resnici napisal besedilo. Naj ga vseeno prikažem?</string>
<string name="decrypt_invalid_button">Seznanjen sem s tveganji, prikaži!</string>
<!--Add keys-->
<string name="add_keys_my_key">Moj ključ:</string>
@@ -300,7 +260,6 @@
<string name="progress_modify_subkeyrevoke">preklicevanje podključev...</string>
<string name="progress_modify_subkeystrip">razstavljam podključe...</string>
<string name="progress_modify_subkeyadd">dodajanje podključev...</string>
- <string name="progress_modify_passphrase">menjava gesla...</string>
<plurals name="progress_exporting_key">
<item quantity="one">izvažam ključ...</item>
<item quantity="two">izvažam ključa...</item>
@@ -367,11 +326,10 @@
<string name="import_tab_qr_code">Koda QR/NFC</string>
<string name="import_import">Uvozi izbrane ključe</string>
<string name="import_qr_code_wrong">Koda QR je deformirana! Poskusite znova!</string>
- <string name="import_qr_code_too_short_fingerprint">Prstni odtis je prekratek (&lt; 16 znakov)</string>
<string name="import_qr_code_button">Skeniraj kodo QR</string>
<string name="import_qr_code_text">Zajamite kodo QR s kamero!</string>
+ <!--Import from URL-->
<!--Generic result toast-->
- <string name="view_log">Podrobnosti</string>
<string name="with_warnings">, z opozorili</string>
<string name="with_cancelled">, do preklica</string>
<!--Import result toast-->
@@ -440,6 +398,7 @@
</plurals>
<string name="delete_nothing">Ničesar ni za izbrisati.</string>
<string name="delete_cancelled">Operacija brisanja prekinjena.</string>
+ <!--Revoke result toast (snackbar)-->
<!--Certify result toast-->
<plurals name="certify_keys_ok">
<item quantity="one">Uspešno potrjen ključ%2$s.</item>
@@ -465,10 +424,6 @@
<string name="intent_send_encrypt">Šifriraj z OpenKeychain</string>
<string name="intent_send_decrypt">Dešifriraj z OpenKeychain</string>
<!--Remote API-->
- <string name="api_settings_show_info">Pokaži dodatne informacije</string>
- <string name="api_settings_hide_info">Skrij dodatne informacije</string>
- <string name="api_settings_show_advanced">Pokaži napredne nastavitve</string>
- <string name="api_settings_hide_advanced">Skrij napredne nastavitve</string>
<string name="api_settings_no_key">Izbran ni bil noben ključ</string>
<string name="api_settings_select_key">Izberite ključ</string>
<string name="api_settings_create_key">Ustvari nov ključ</string>
@@ -479,9 +434,6 @@
<string name="api_settings_start">Zaženi aplikacijo</string>
<string name="api_settings_delete_account">Izbriši račun</string>
<string name="api_settings_package_name">Ime paketa</string>
- <string name="api_settings_package_signature">SHA-256 podpisa paketa</string>
- <string name="api_settings_accounts">Računi (opuščen API)</string>
- <string name="api_settings_advanced">Dodatne informacije</string>
<string name="api_settings_allowed_keys">Dovoljeni ključi</string>
<string name="api_settings_settings">Nastavitve</string>
<string name="api_settings_key">Ključ računa:</string>
@@ -492,8 +444,6 @@
<string name="api_register_allow">Dovoli dostop</string>
<string name="api_register_disallow">Zavrni dostop</string>
<string name="api_register_error_select_key">Izberite ključ!</string>
- <string name="api_select_pub_keys_missing_text">Za te identitete ni bil najden noben ključ:</string>
- <string name="api_select_pub_keys_dublicates_text">Za te identitete obstaja več ključev:</string>
<string name="api_select_pub_keys_text">Preverite seznam prejemnikov!</string>
<string name="api_select_pub_keys_text_no_user_ids">Prosim, izberite prejemnike!</string>
<string name="api_error_wrong_signature">Preverjanje podpisa ni uspelo! Ste namestili to aplikacijo iz drugega vira? Če ste prepričani, da to ni napad, prekličite registracijo te aplikacije v OpenKeychain in jo izvedite znova.</string>
@@ -501,6 +451,9 @@
<!--Share-->
<string name="share_qr_code_dialog_title">Deli s kodo QR</string>
<string name="share_nfc_dialog">Deli preko NFC</string>
+ <!--retry upload dialog-->
+ <!--Delete or revoke private key dialog-->
+ <!--Delete Or Revoke Dialog spinner-->
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">Izbran 1 ključ.</item>
@@ -545,14 +498,6 @@
<string name="key_trust_results_prefix">Keybase.io ponuja \"dokazilo\", da je lastnik tega kluča:</string>
<string name="key_trust_header_text">Pozor: Dokazila Keybase.io v aplikaciji OpenKeychain so eksperimantalne narave. Za večjo zanesljivost priporočamo, da dodatno skenirate kode QR ali izmenjate ključe preko NFC.</string>
<!--keybase proof stuff-->
- <string name="keybase_narrative_twitter">Objavlja na Twitterju kot</string>
- <string name="keybase_narrative_github">Prisoten na GitHub-u kot</string>
- <string name="keybase_narrative_dns">Upravlja domeno(e)</string>
- <string name="keybase_narrative_web_site">Objavlja na spletni strani(eh)</string>
- <string name="keybase_narrative_reddit">Objavlja na Reddit-u kot</string>
- <string name="keybase_narrative_coinbase">Prisoten na Coinbase-u kot</string>
- <string name="keybase_narrative_hackernews">Objavlja na Hacker News kot</string>
- <string name="keybase_narrative_unknown">Neznano dokazilo</string>
<string name="keybase_proof_failure">Dokazila se žal ne da preveriti.</string>
<string name="keybase_unknown_proof_failure">Neznan problem pri pregledovalniku dokazil</string>
<string name="keybase_problem_fetching_evidence">Problem z dokazilom</string>
@@ -570,7 +515,6 @@
<string name="keybase_reddit_proof">Datoteka JSON</string>
<string name="keybase_verify">Overi</string>
<!--Edit key-->
- <string name="edit_key_action_change_passphrase">Zamenjaj geslo</string>
<string name="edit_key_action_add_identity">Dodaj identiteto</string>
<string name="edit_key_action_add_subkey">Dodaj podključ</string>
<string name="edit_key_edit_user_id_title">Izberite dejanje!</string>
@@ -583,11 +527,6 @@
</string-array>
<string name="edit_key_edit_user_id_revoked">Identiteta je bila preklicana. Ta poteza ne more biti razveljavljena.</string>
<string name="edit_key_edit_subkey_title">Izberite dejanje!</string>
- <string-array name="edit_key_edit_subkey">
- <item>Spremeni datum poteka</item>
- <item>Prekliči podključ</item>
- <item>Razstavi podključ</item>
- </string-array>
<string name="edit_key_new_subkey">nov podključ</string>
<string name="edit_key_select_flag">Izberite vsaj eno oznako!</string>
<string name="edit_key_error_add_identity">Dodajte vsaj eno identiteto!</string>
@@ -595,19 +534,16 @@
<!--Create key-->
<string name="create_key_upload">Sinhroniziraj z oblakom</string>
<string name="create_key_empty">To polje je obvezno</string>
- <string name="create_key_passphrases_not_equal">Gesli se ne ujemata</string>
<string name="create_key_final_text">Vnesli ste identiteto:</string>
<string name="create_key_final_robot_text">Ustvarjanje ključa zna potrajati. Privoščite si kavo...</string>
<string name="create_key_rsa">(3 podključi, RSA, 4096 bit)</string>
<string name="create_key_custom">(konfiguracija po meri)</string>
<string name="create_key_name_text">Izberite ime povezano s tem ključem. Lahko je polno ime, npr. \'Marjan Novak\', ali nadimek, npr. \'Mare\'.</string>
<string name="create_key_email_text">Vnesite svoj glavni e-poštni naslov za varno komunikacijo.</string>
- <string name="create_key_passphrase_text">Izberite močno geslo. To bo varovalo vaš ključ v primeru odtujitve naprave.</string>
<string name="create_key_hint_full_name">Polno ime ali nadimek</string>
<string name="create_key_edit">Spremeni konfiguracijo ključa.</string>
<string name="create_key_add_email">Dodaj naslov e-pošte</string>
<string name="create_key_add_email_text">Dodatni e-poštni naslovi so prav tako povezani s tem ključem in so na voljo za varno komunikacijo.</string>
- <string name="create_key_email_already_exists_text">E-poštni naslov je že bil dodan</string>
<!--View key-->
<string name="view_key_revoked">Ključ je preklican: ne smete ga več uporabljati!</string>
<string name="view_key_expired">Ključ je potekel: stvaritelj mora podaljšati njegovo veljavo!</string>
@@ -616,6 +552,7 @@
<string name="view_key_verified">Potrjen ključ</string>
<string name="view_key_unverified">Ključ ni potrjen: za potrditev skenirajte kodo QR!</string>
<string name="view_key_fragment_no_system_contact">&lt;brez&gt;</string>
+ <!--Add/Edit keyserver-->
<!--Navigation Drawer-->
<string name="nav_keys">Ključi</string>
<string name="nav_encrypt_decrypt">Šifriraj/Dešifriraj</string>
@@ -744,10 +681,8 @@
<string name="msg_is_pubring_generate">Generiram javno zbirko ključev iz zasebne</string>
<string name="msg_is_subkey_nonexistent">Podključ %s ni na voljo v zasebnem ključu</string>
<string name="msg_is_subkey_ok">Zasebni podključ %s označen kot razpoložljiv</string>
- <string name="msg_is_subkey_empty">Zasebni podključ %s označen kot razpoložljiv, s praznim geslom</string>
<string name="msg_is_subkey_pin">Zasebni podključ %s označen kot razpoložljiv, s PIN kodo</string>
<string name="msg_is_subkey_stripped">Zasebni podključ %s označen kot razstavljen</string>
- <string name="msg_is_subkey_divert">Zasebni podključ %s označen kot \'preusmerjen na pamtno kartico/NFC\'</string>
<string name="msg_is_success_identical">Zbirka ključev ne vsebuje nobenih novih podatkov. Nič za storiti.</string>
<string name="msg_is_success">Zasebna zbirka ključev uspešno uvožena</string>
<!--Keyring Canonicalization log entries-->
@@ -870,12 +805,10 @@
<item quantity="few">Deli naložene datoteke so veljavni objekti OpenPGP a niso ključi.</item>
<item quantity="other">Deli naložene datoteke so veljavni objekti OpenPGP a niso ključi.</item>
</plurals>
+ <!--Messages for Keybase Verification operation-->
<!--Messages for Export Log operation-->
<!--PassphraseCache-->
- <string name="passp_cache_notif_click_to_clear">Kliknite za izbris zapomnjenih gesel</string>
- <string name="passp_cache_notif_n_keys">OpenKeychain pomni gesla: %d</string>
- <string name="passp_cache_notif_keys">Zapomnjena gesla:</string>
- <string name="passp_cache_notif_pwd">Geslo</string>
+ <!--Keyserver sync-->
<!--First Time-->
<string name="first_time_text1">Vzemite si svojo zasebnost nazaj v svoje roke z OpenKeychain!</string>
<string name="first_time_skip">Preskoči nastavitev</string>
@@ -891,8 +824,6 @@
<string name="error_key_not_found">Ključ ni bil najden!</string>
<string name="error_key_processing">Napaka pri obdelavi ključa!</string>
<string name="key_stripped">slečen</string>
- <string name="key_divert">preusmeri na pametno kartico/NFC</string>
- <string name="key_no_passphrase">brez gesla</string>
<string name="key_unavailable">ni na voljo</string>
<string name="title_view_cert">Preglej podrobosti potrdila</string>
<string name="unknown_algorithm">neznan</string>
@@ -900,8 +831,9 @@
<string name="error_no_encrypt_subkey">Ni nobenega podključa za šifriranje!</string>
<string name="contact_show_key">Prikaži ključ (%s)</string>
<string name="key_colon">Ključ:</string>
+ <!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
</resources>
diff --git a/OpenKeychain/src/main/res/values-sr/strings.xml b/OpenKeychain/src/main/res/values-sr/strings.xml
index c4c92dce5..8d4134e40 100644
--- a/OpenKeychain/src/main/res/values-sr/strings.xml
+++ b/OpenKeychain/src/main/res/values-sr/strings.xml
@@ -4,66 +4,64 @@
http://developer.android.com/guide/topics/resources/string-resource.html (scroll down to "Escaping apostrophes and quotes").-->
<string name="app_name">Отворени кључарник</string>
<!--title-->
- <string name="title_select_recipients">Изаберите кључ</string>
- <string name="title_select_secret_key">Изаберите ваш кључ</string>
- <string name="title_encrypt_text">Шифруј текст</string>
- <string name="title_encrypt_files">Шифруј фајлове</string>
+ <string name="title_encrypt_text">Шифруј</string>
+ <string name="title_encrypt_files">Шифруј</string>
<string name="title_decrypt">Дешифруј</string>
- <string name="title_unlock">Откључај кључ</string>
<string name="title_add_subkey">Додај поткључ</string>
<string name="title_edit_key">Уреди кључ</string>
<string name="title_preferences">Поставке</string>
- <string name="title_cloud_search_preferences">Поставке клауд претраге</string>
<string name="title_api_registered_apps">Апликације</string>
- <string name="title_key_server_preference">Сервери кључева</string>
- <string name="title_change_passphrase">Измени лозинку</string>
+ <string name="title_key_server_preference">ОпенПГП сервери кључева</string>
+ <string name="title_change_passphrase">Измена лозинке</string>
<string name="title_share_fingerprint_with">Подели отисак са…</string>
- <string name="title_share_key">Подели кључ са…</string>
- <string name="title_share_file">Подели фајл са…</string>
- <string name="title_share_message">Подели поруку са…</string>
+ <string name="title_share_key">Подели кључ преко…</string>
+ <string name="title_share_file">Подели фајл преко…</string>
+ <string name="title_share_message">Подели текст преко…</string>
<string name="title_encrypt_to_file">Шифруј у фајл</string>
<string name="title_decrypt_to_file">Дешифруј у фајл</string>
<string name="title_import_keys">Увези кључеве</string>
- <string name="title_add_keys">Додај кључеве</string>
- <string name="title_export_key">Извези кључ</string>
- <string name="title_export_keys">Извези кључеве</string>
+ <string name="title_export_key">Направи резерву кључа</string>
+ <string name="title_export_keys">Направи резерву кључева</string>
<string name="title_key_not_found">Кључ није нађен</string>
<string name="title_send_key">Отпреми на сервер кључева</string>
- <string name="title_certify_key">Овери идентитете</string>
+ <string name="title_certify_key">Потврда кључа</string>
<string name="title_key_details">Детаљи кључа</string>
<string name="title_help">Помоћ</string>
<string name="title_log_display">Дневник</string>
- <string name="title_create_key">Направи кључ</string>
<string name="title_exchange_keys">Размена кључева</string>
- <string name="title_advanced_key_info">Напредни подаци о кључу</string>
- <string name="title_keys">Кључеви</string>
+ <string name="title_advanced_key_info">Додатни подаци</string>
+ <string name="title_delete_secret_key">Обрисати ВАШ кључ „%s“?</string>
+ <string name="title_export_log">Извоз дневника</string>
+ <string name="title_manage_my_keys">Управљање мојим кључевима</string>
<!--section-->
<string name="section_user_ids">Идентитети</string>
+ <string name="section_yubikey">Јубикључ</string>
+ <string name="section_linked_system_contact">Повезани контакт</string>
+ <string name="section_should_you_trust">Смијете ли да се поуздате у овај кључ?</string>
+ <string name="section_proof_details">Овера доказа</string>
+ <string name="section_cloud_evidence">Докази са клауда</string>
<string name="section_keys">Поткључеви</string>
<string name="section_cloud_search">Претрага клауда</string>
- <string name="section_general">Опште</string>
- <string name="section_defaults">Подразумевано</string>
- <string name="section_advanced">Напредно</string>
- <string name="section_passphrase_cache">Кеширај лозинку</string>
- <string name="section_certify">Овера</string>
+ <string name="section_passphrase_cache">Руковање лозинком/ПИНом</string>
+ <string name="section_proxy_settings">Поставке проксија</string>
+ <string name="section_gui">Сучеље</string>
+ <string name="section_sync_settings">Поставке синхронизације</string>
+ <string name="section_certify">Потврда</string>
<string name="section_actions">Радње</string>
<string name="section_share_key">Кључ</string>
- <string name="section_certification_key">Ваш кључ за оверу</string>
- <string name="section_upload_key">Синхронизуј кључ</string>
<string name="section_key_server">Сервер кључева</string>
<string name="section_fingerprint">Отисак</string>
- <string name="section_key_to_certify">Кључ за оверавање</string>
- <string name="section_decrypt_files">Фајлови</string>
- <string name="section_decrypt_text">Текст</string>
- <string name="section_certs">Сертификати</string>
<string name="section_encrypt">Шифровање</string>
- <string name="section_decrypt">Дешифровање</string>
+ <string name="section_decrypt">Дешифруј/овери</string>
+ <string name="section_current_expiry">Текући истек</string>
+ <string name="section_new_expiry">Нови истек</string>
<!--button-->
<string name="btn_decrypt_verify_file">Дешифруј, овери и сачувај фајл</string>
- <string name="btn_decrypt_verify_message">Дешифруј и овери поруку</string>
- <string name="btn_encrypt_file">Шифруј и сачувај фајл</string>
<string name="btn_encrypt_share_file">Шифруј и подели фајл</string>
+ <string name="btn_encrypt_save_file">Шифруј и сачувај фајл</string>
+ <string name="btn_save_file">Сачувај фајл</string>
<string name="btn_save">Сачувај</string>
+ <string name="btn_view_log">Прикажи дневник</string>
<string name="btn_do_not_save">Одустани</string>
<string name="btn_delete">Обриши</string>
<string name="btn_no_date">Не истиче</string>
@@ -73,37 +71,41 @@
<string name="btn_back">Назад</string>
<string name="btn_no">Не</string>
<string name="btn_match">Отисци се поклапају</string>
- <string name="btn_lookup_key">Потражи кључ</string>
- <string name="btn_share_encrypted_signed">Шифруј и подели поруку</string>
+ <string name="btn_share_encrypted_signed">Шифруј и подели текст</string>
+ <string name="btn_copy_encrypted_signed">Шифруј и копирај текст</string>
<string name="btn_view_cert_key">Прикажи кључ за оверавање</string>
<string name="btn_create_key">Направи кључ</string>
<string name="btn_add_files">Додај фајл(ове)</string>
- <string name="btn_add_share_decrypted_text">Подели дешифрован текст</string>
- <string name="btn_decrypt_clipboard">Дешифруј текст са клипборда</string>
- <string name="btn_decrypt_and_verify">и овери потписе</string>
- <string name="btn_decrypt_files">Дешифруј фајлове</string>
+ <string name="btn_share_decrypted_text">Подели дешифровани текст</string>
+ <string name="btn_copy_decrypted_text">Копирај дешифровани текст</string>
+ <string name="btn_decrypt_clipboard">Учитај са клипборда</string>
+ <string name="btn_decrypt_files">Изабери фајл</string>
<string name="btn_encrypt_files">Шифруј фајлове</string>
<string name="btn_encrypt_text">Шифруј текст</string>
+ <string name="btn_add_email">Додај додатну е-адресу</string>
+ <string name="btn_unlock">Откључај</string>
+ <string name="btn_add_keyserver">Додај</string>
+ <string name="btn_save_default">Сачувај за подразумевано</string>
+ <string name="btn_saved">Сачувано!</string>
<!--menu-->
<string name="menu_preferences">Поставке</string>
<string name="menu_help">Помоћ</string>
- <string name="menu_export_key">Извези у фајл</string>
+ <string name="menu_export_key">Направи резерву у фајл</string>
<string name="menu_delete_key">Обриши кључ</string>
- <string name="menu_manage_keys">Направи ми кључ</string>
- <string name="menu_import_existing_key">Увези из фајла</string>
+ <string name="menu_manage_keys">Управљај мојим кључевима</string>
<string name="menu_search">Претрага</string>
<string name="menu_nfc_preferences">НФЦ поставке</string>
<string name="menu_beam_preferences">Поставке Снопа</string>
- <string name="menu_key_edit_cancel">Одустани</string>
<string name="menu_encrypt_to">Шифруј у…</string>
<string name="menu_select_all">Изабери све</string>
- <string name="menu_add_keys">Додај кључеве</string>
- <string name="menu_search_cloud">Клауд претрага</string>
<string name="menu_export_all_keys">Извези све кључеве</string>
- <string name="menu_advanced">Прикажи напредне податке</string>
- <string name="menu_certify_fingerprint">Овери поређењем отисака</string>
+ <string name="menu_update_all_keys">Ажурирај све кључеве</string>
+ <string name="menu_advanced">Додатни подаци</string>
+ <string name="menu_certify_fingerprint">Потврди поређењем отисака</string>
+ <string name="menu_export_log">Извези дневник</string>
+ <string name="menu_keyserver_add">Додај</string>
<!--label-->
- <string name="label_message">Порука</string>
+ <string name="label_message">Текст</string>
<string name="label_file">Фајл</string>
<string name="label_files">Фајл(ови)</string>
<string name="label_file_colon">Фајл:</string>
@@ -111,6 +113,7 @@
<string name="label_passphrase">Лозинка</string>
<string name="label_unlock">Откључавам…</string>
<string name="label_passphrase_again">Лозинка поново</string>
+ <string name="label_show_passphrase">Прикажи лозинку</string>
<string name="label_algorithm">Алгоритам</string>
<string name="label_ascii_armor">Аски оклоп фајла</string>
<string name="label_file_ascii_armor">Омогући Аски оклоп</string>
@@ -119,19 +122,20 @@
<string name="label_use_default_yubikey_pin">Користи подразумевани Јубикључ ПИН</string>
<string name="label_use_num_keypad_for_yubikey_pin">Користи бројчану тастатуру за Јубикључ ПИН</string>
<string name="label_label_use_default_yubikey_pin_summary">Користи подразумевани ПИН (123456) за приступ Јубикључевима преко НФЦ-а</string>
- <string name="label_asymmetric_from">Потписник:</string>
+ <string name="label_asymmetric_from">Потпиши помоћу:</string>
<string name="label_to">Шифруј за:</string>
- <string name="label_delete_after_encryption">Обриши фајл након шифровања</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_symmetric">Шифровање са лозинком</string>
- <string name="label_passphrase_cache_ttl">Време кеширања</string>
- <string name="label_passphrase_cache_subs">Кеширај по поткључу</string>
- <string name="label_message_compression">Компресија поруке</string>
+ <string name="label_passphrase_cache_ttl">Време памћења</string>
+ <string name="label_passphrase_cache_subs">Памти лозинке по поткључу</string>
+ <string name="label_message_compression">Компресија текста</string>
<string name="label_file_compression">Компресија фајла</string>
- <string name="label_keyservers">Сервери кључева</string>
+ <string name="label_keyservers">Изаберите ОпенПГП сервере кључева</string>
<string name="label_key_id">ИД кључа</string>
+ <string name="label_key_created">Кључ направљен %s</string>
<string name="label_creation">Створен</string>
<string name="label_expiry">Истиче</string>
<string name="label_usage">Употреба</string>
@@ -144,17 +148,58 @@
<string name="label_send_key">Синхронизуј са клаудом</string>
<string name="label_fingerprint">Отисак</string>
<string name="expiry_date_dialog_title">Датум истицања</string>
- <string name="label_first_keyserver_is_used">(Први сервер кључева на списку је приоритетан)</string>
+ <string name="label_keyservers_title">Сервери кључева</string>
+ <string name="label_keyserver_settings_hint">Превлачите за измену редоследа, тапните за уређивање/брисање</string>
+ <string name="label_selected_keyserver_title">Изабрани сервер кључева</string>
<string name="label_preferred">приоритетан</string>
+ <string name="label_enable_compression">Омогући компресију</string>
+ <string name="label_encrypt_filenames">Шифруј имена фајлова</string>
+ <string name="label_hidden_recipients">Сакриј примаоце</string>
+ <string name="label_verify_keyserver">Овери сервер кључева</string>
+ <string name="label_enter_keyserver_url">Унесите УРЛ сервера кључева</string>
+ <string name="label_keyserver_dialog_delete">Обриши сервер кључева</string>
+ <string name="label_theme">Тема</string>
+ <string name="pref_keyserver">ОпенПГП сервера кључева</string>
+ <string name="pref_keyserver_summary">Тражи кључеве на изабраним ОпенПГП серверима кључева (ХКП протокол)</string>
+ <string name="pref_keybase">keybase.io</string>
+ <string name="pref_keybase_summary">Тражи кључеве на keybase.io</string>
+ <string name="label_sync_settings_keyserver_title">Аутоматски ажурирај кључеве</string>
+ <string name="label_sync_settings_keyserver_summary_on">Кључеви старији од седам дана се ажурирају са пожељног сервера кључева</string>
+ <string name="label_sync_settings_keyserver_summary_off">Кључеви се не ажурирају аутоматски</string>
+ <string name="label_sync_settings_contacts_title">Синхронизуј контакте са кључевима</string>
+ <string name="label_sync_settings_contacts_summary_on">Кључеви повезани са контактима са поклапајућим е-адресама, одвија се у потпуности ван везе</string>
+ <string name="label_sync_settings_contacts_summary_off">Нови кључеви неће бити повезани са контактима</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <string name="keyserver_sync_settings_title">Аутоматски ажурирај кључеве</string>
+ <!--Proxy Preferences-->
+ <string name="pref_proxy_tor_title">Омогући Тор</string>
+ <string name="pref_proxy_tor_summary">Захтева Орбот</string>
+ <string name="pref_proxy_normal_title">Омогући други прокси</string>
+ <string name="pref_proxy_host_title">Домаћин проксија</string>
+ <string name="pref_proxy_host_err_invalid">Домаћин проксија не може бити празан</string>
+ <string name="pref_proxy_port_title">Порт проксија</string>
+ <string name="pref_proxy_port_err_invalid">Унесен неисправан број порта</string>
+ <string name="pref_proxy_type_title">Тип проксија</string>
+ <!--proxy type choices and values-->
+ <string name="pref_proxy_type_choice_http">ХТТП</string>
+ <string name="pref_proxy_type_choice_socks">СОЦКС</string>
+ <!--OrbotHelper strings-->
+ <string name="orbot_ignore_tor">Не користи Тор</string>
+ <!--InstallDialogFragment strings-->
+ <string name="orbot_install_dialog_title">Инсталирати Орбот да бих користио Тор?</string>
+ <string name="orbot_install_dialog_install">Инсталирај</string>
+ <string name="orbot_install_dialog_content">Морате имати Орбот инсталиран и активиран да бисте преусмерили саобраћај кроз њега. Желите ли да га инсталирате?</string>
+ <string name="orbot_install_dialog_cancel">Одустани</string>
+ <string name="orbot_install_dialog_ignore_tor">Не користи Тор</string>
+ <!--StartOrbotDialogFragment strings-->
+ <string name="orbot_start_dialog_title">Да покренем Орбот?</string>
+ <string name="orbot_start_dialog_content">Чини се да Орбот није покренут. Желите ли да га покренете и повежете са Тором?</string>
+ <string name="orbot_start_btn">Покрени Орбот</string>
+ <string name="orbot_start_dialog_start">Покрени Орбот</string>
+ <string name="orbot_start_dialog_cancel">Одустани</string>
+ <string name="orbot_start_dialog_ignore_tor">Не користи Тор</string>
<string name="user_id_no_name">&lt;нема имена&gt;</string>
<string name="none">&lt;нема&gt;</string>
- <string name="no_key">&lt;нема кључа&gt;</string>
- <string name="can_encrypt">може да шифрује</string>
- <string name="can_sign">може да потпише</string>
- <string name="can_certify">може да овери</string>
- <string name="can_certify_not">не може да овери</string>
- <string name="expired">истекао</string>
- <string name="revoked">опозван</string>
<plurals name="n_keys">
<item quantity="one">%d кључ</item>
<item quantity="few">%d кључа</item>
@@ -186,9 +231,10 @@
<string name="ecdh">ЕЦДХ</string>
<string name="ecdsa">ЕЦДСА</string>
<string name="filemanager_title_open">Отвори…</string>
- <string name="warning">Упозорење</string>
<string name="error">Грешка</string>
<string name="error_message">Грешка: %s</string>
+ <string name="theme_dark">Тамна</string>
+ <string name="theme_light">Светла</string>
<!--key flags-->
<string name="flag_certify">Овера</string>
<string name="flag_sign">Потпис</string>
@@ -203,19 +249,27 @@
<string name="passphrase_for">Унесите лозинку за „%s“</string>
<string name="pin_for">Унесите ПИН за „%s“</string>
<string name="yubikey_pin_for">Унесите ПИН за приступ Јубикључу за „%s“</string>
- <string name="nfc_text">Држите Јубикључ на полеђини вашег уређаја.</string>
- <string name="no_file_selected">Најпре изаберите фајл.</string>
+ <string name="nfc_text">Држите Јубикључ на НФЦ ознаци на полеђини вашег уређаја.</string>
+ <string name="nfc_wait">Држите Јубикључ на полеђини!</string>
+ <string name="nfc_finished">Склоните сада Јубикључ.</string>
+ <string name="nfc_try_again_text">Склоните сада Јубикључ и тапните на ПОКУШАЈ ПОНОВО.</string>
+ <string name="file_delete_confirmation_title">Обрисати оригиналне фајлове?</string>
+ <string name="file_delete_confirmation">Следећи фајлови ће бити обрисани:%s</string>
+ <string name="file_delete_successful">%1$d од %2$d фајлова је обрисано.%3$s</string>
+ <string name="no_file_selected">Ниједан фајл није изабран.</string>
<string name="encrypt_sign_successful">Потписивање и/или шифровање је успело.</string>
<string name="encrypt_sign_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="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="key_deletion_confirmation_multi">Желите ли заиста да обришете све изабране јавне кључеве?\nОво не можете да поништите!</string>
- <string name="secret_key_deletion_confirmation">Желите ли заиста да обришете ТАЈНИ кључ „%s“?\nОво не можете да поништите!</string>
- <string name="public_key_deletetion_confirmation">Желите ли заиста да обришете јавни кључ „%s“??\nОво не можете да поништите!</string>
+ <string name="error_no_encryption_or_signature_key">Изаберите бар један кључ за шифровање или потписивање.</string>
+ <string name="specify_file_to_encrypt_to">Одредите у који фајл да шифрујем.\nУПОЗОРЕЊЕ: Фајл ће бити пребрисан ако постоји!</string>
+ <string name="specify_file_to_decrypt_to">Одредите у који фајл да дешифрујем.\nУПОЗОРЕЊЕ: Фајл ће бити пребрисан ако постоји!</string>
+ <string name="specify_backup_dest">Биће направљена резерва без ваших кључева, наведите одредишни фајл.\nУПОЗОРЕЊЕ: Фајл ће бити пребрисан ако већ постоји!</string>
+ <string name="specify_backup_dest_single">Овај кључ ће бити подељен, наведите одредишни фајл.\nУПОЗОРЕЊЕ: Фајл ће бити пребрисан ако већ постоји!</string>
+ <string name="specify_backup_dest_secret_single">Биће направљена потпуна резерва вашег кључа, наведите одредишни фајл.\nУПОЗОРЕЊЕ: Фајл ће бити пребрисан ако већ постоји!</string>
+ <string name="specify_backup_dest_secret">Биће направљена потпуна резерва свих кључева укључујући и ваше, наведите одредишни фајл.\nУПОЗОРЕЊЕ: Фајл ће бити пребрисан ако већ постоји!</string>
+ <string name="key_deletion_confirmation_multi">Желите ли заиста да обришете све изабране кључеве?</string>
+ <string name="secret_key_deletion_confirmation">Након брисања нећете моћи да читате поруке шифроване овим кључем и изгубићете све потврде кључева направљене њиме!</string>
+ <string name="public_key_deletetion_confirmation">Да обришем кључ „%s“?</string>
<string name="also_export_secret_keys">Такође извези тајне кључеве</string>
<string name="reinstall_openkeychain">Наишли сте на познату грешку у Андроиду. Поново инсталирајте Отворени кључарник ако желите да повежете ваше контакте са кључевима.</string>
<string name="key_exported">Успешно извезен 1 кључ.</string>
@@ -223,6 +277,7 @@
<string name="no_keys_exported">Ниједан кључ није извезен.</string>
<string name="key_creation_el_gamal_info">Напомена: само поткључеви подржавају Елгамал.</string>
<string name="key_not_found">Нисам могао да нађем кључ %08X.</string>
+ <string name="specify_file_to_export_log_to">Наведите у који фајл да извезем.\nУПОЗОРЕЊЕ: Фајл ће бити пребрисан ако постоји.</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\"</item>
@@ -232,18 +287,20 @@
<string name="nfc_successful">Кључ је успешно послат преко НФЦ Снопа!</string>
<string name="key_copied_to_clipboard">Кључ је копиран на клипборд!</string>
<string name="fingerprint_copied_to_clipboard">Отисак је копиран на клипборд!</string>
- <string name="select_key_to_certify">Изаберите кључ који ће бити коришћен за оверавање!</string>
+ <string name="select_key_to_certify">Изаберите кључ којим ћете извршити потврду!</string>
<string name="key_too_big_for_sharing">Кључ је превелик да би се делио на овај начин!</string>
<string name="text_copied_to_clipboard">Текст је копиран на клипборд!</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_file_delete_failed">нису обрисани. Обришите их ручно!</string>
+ <string name="error_file_added_already">„%s“ је већ додат.</string>
+ <string name="error_file_not_found">фајл није нађен</string>
<string name="error_no_secret_key_found">одговарајући тајни кључ није нађен</string>
<string name="error_external_storage_not_ready">спољашње складиште није спремно</string>
<string name="error_key_size_minimum512bit">величина кључа мора да буде најмање 512 бита</string>
<string name="error_unknown_algorithm_choice">непознат избор алгоритма</string>
- <string name="error_user_id_no_email">е-адреса није нађена</string>
+ <string name="error_user_id_no_email">није нађена е-адреса</string>
<string name="error_key_needs_a_user_id">потребан је бар један идентитет</string>
<string name="error_no_signature_passphrase">није задата лозинка</string>
<string name="error_no_signature_key">није задат кључ за потпис</string>
@@ -256,21 +313,25 @@
<string name="error_nfc_needed">НФЦ мора бити укључен!</string>
<string name="error_beam_needed">Бим мора бити укључен</string>
<string name="error_nothing_import">Нема нађених кључева!</string>
+ <string name="error_nothing_import_selected">Није изабран ниједан кључ за увоз!</string>
<string name="error_contacts_key_id_missing">Добављање ИД кључа из контаката није успело!</string>
<string name="error_generic_report_bug">Дошло је до опште грешке, направите нови извештај о грешци за Отворени кључарник.</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">Није потписано</string>
<string name="decrypt_result_invalid_signature">Неисправан потпис!</string>
- <string name="decrypt_result_signature_uncertified">Потписник (није оверено!)</string>
- <string name="decrypt_result_signature_certified">Потписник</string>
- <string name="decrypt_result_signature_expired_key">Кључ је истекао!</string>
- <string name="decrypt_result_signature_revoked_key">Кључ је опозван!</string>
- <string name="decrypt_result_signature_missing_key">Непознат јавни кључ</string>
+ <string name="decrypt_result_insecure_cryptography">Неисправан потпис (небезбедна криптографија)!</string>
+ <string name="decrypt_result_signature_uncertified">Потписано <b>непотврђеним</b> кључем</string>
+ <string name="decrypt_result_signature_secret">Потписано вашим кључем</string>
+ <string name="decrypt_result_signature_certified">Потписано потврђеним кључем</string>
+ <string name="decrypt_result_signature_expired_key">Потписано <b>истеклим</b> кључем!</string>
+ <string name="decrypt_result_signature_revoked_key">Потписано <b>опозваним</b> кључем!</string>
+ <string name="decrypt_result_signature_missing_key">Потписано <b>непознатим јавним кључем</b></string>
<string name="decrypt_result_encrypted">Шифровано</string>
<string name="decrypt_result_not_encrypted">Није шифровано</string>
+ <string name="decrypt_result_insecure">Небезбедно шифровање</string>
<string name="decrypt_result_action_show">Прикажи</string>
<string name="decrypt_result_action_Lookup">Потражи</string>
- <string name="decrypt_invalid_text">Или је потпис неисправан или је кључ опозван/истекао. Не можете бити сигурни ко је написао текст. Желите ли свеједно да га прикажете?</string>
+ <string name="decrypt_invalid_text">Или је потпис неисправан или је кључ опозван. Не можете бити сигурни ко је написао текст. Желите ли свеједно да га прикажете?</string>
<string name="decrypt_invalid_button">Свестан сам ризика, прикажи!</string>
<!--Add keys-->
<string name="add_keys_my_key">Мој кључ:</string>
@@ -280,6 +341,8 @@
<string name="progress_cancelling">одустајем…</string>
<string name="progress_saving">уписујем…</string>
<string name="progress_importing">увозим…</string>
+ <string name="progress_revoking_uploading">Опозивам и отпремам кључ…</string>
+ <string name="progress_updating">Ажурирам кључеве…</string>
<string name="progress_exporting">извозим…</string>
<string name="progress_uploading">отпремам…</string>
<string name="progress_building_key">градим кључ…</string>
@@ -300,11 +363,14 @@
<string name="progress_modify_subkeystrip">огољавам поткључеве…</string>
<string name="progress_modify_subkeyadd">додајем поткључеве…</string>
<string name="progress_modify_passphrase">мењам лозинку…</string>
+ <string name="progress_modify_pin">мењам ПИН…</string>
+ <string name="progress_modify_admin_pin">мењам администраторски ПИН…</string>
<plurals name="progress_exporting_key">
<item quantity="one">извозим кључ…</item>
<item quantity="few">извозим кључеве…</item>
<item quantity="other">извозим кључеве…</item>
</plurals>
+ <string name="progress_start">припремам радњу…</string>
<string name="progress_extracting_signature_key">извлачим кључ потписа…</string>
<string name="progress_extracting_key">извлачим кључ…</string>
<string name="progress_preparing_streams">припремам токове…</string>
@@ -324,9 +390,10 @@
<string name="progress_deleting">бришем кључеве…</string>
<string name="progress_con_saving">учвршћивање: уписујем у кеш…</string>
<string name="progress_con_reimport">учвршћивање: поново увозим…</string>
+ <string name="progress_verifying_keyserver_url">оверавам сервер кључева…</string>
+ <string name="progress_starting_orbot">Покрећем Орбот…</string>
<!--action strings-->
- <string name="hint_keyserver_search_hint">Име/е-пошта/ИД кључа…</string>
- <string name="hint_cloud_search_hint">Име/е-пошта/доказ/кључ…</string>
+ <string name="hint_cloud_search_hint">Тражи преко имена, е-адресе…</string>
<!--key bit length selections-->
<string name="key_size_512">512</string>
<string name="key_size_768">768</string>
@@ -354,7 +421,7 @@
<!--Help-->
<string name="help_tab_start">Старт</string>
<string name="help_tab_faq">Честа питања</string>
- <string name="help_tab_wot">Веб поверења</string>
+ <string name="help_tab_wot">Потврда кључа</string>
<string name="help_tab_nfc_beam">НФЦ Сноп</string>
<string name="help_tab_changelog">Дневник измена</string>
<string name="help_tab_about">О програму</string>
@@ -363,14 +430,17 @@
<string name="import_tab_keyserver">Сервер кључева</string>
<string name="import_tab_cloud">Клауд претрага</string>
<string name="import_tab_direct">Фајл/клипборд</string>
- <string name="import_tab_qr_code">Бар-код/НФЦ</string>
+ <string name="import_tab_qr_code">Бар-кôд/НФЦ</string>
<string name="import_import">Увези изабране кључеве</string>
- <string name="import_qr_code_wrong">Бар-код деформисан! Покушајте поново!</string>
- <string name="import_qr_code_too_short_fingerprint">Отисак је прекратак (&lt; 16 знакова)</string>
- <string name="import_qr_code_button">Очитај бар-код</string>
- <string name="import_qr_code_text">Усмерите камеру на бар-код!</string>
+ <string name="import_qr_code_wrong">Бар-кôд деформисан! Покушајте поново!</string>
+ <string name="import_qr_code_fp">Отисак је деформисан или прекратак!</string>
+ <string name="import_qr_code_too_short_fingerprint">Отисак је прекратак!</string>
+ <string name="import_qr_code_button">Очитај бар-кôд</string>
+ <string name="import_qr_code_text">Усмерите камеру на бар-кôд!</string>
+ <!--Import from URL-->
+ <string name="import_url_warn_no_search_parameter">Упит за претрагу није дефинисан. И даље можете ручно да тражите на овом серверу кључева.</string>
<!--Generic result toast-->
- <string name="view_log">Детаљи</string>
+ <string name="snackbar_details">Детаљи</string>
<string name="with_warnings">, са упозорењима</string>
<string name="with_cancelled">, док није отказано</string>
<!--Import result toast-->
@@ -429,6 +499,11 @@
</plurals>
<string name="delete_nothing">Нема ништа за брисање.</string>
<string name="delete_cancelled">Радња брисања је отказана.</string>
+ <!--Revoke result toast (snackbar)-->
+ <string name="revoke_ok">Успешно опозван кључ.</string>
+ <string name="revoke_fail">Грешка опозивања кључа!</string>
+ <string name="revoke_nothing">Нема ништа за опозив.</string>
+ <string name="revoke_cancelled">Радња опозива је отказана.</string>
<!--Certify result toast-->
<plurals name="certify_keys_ok">
<item quantity="one">Успешно оверен кључ%2$s.</item>
@@ -451,13 +526,13 @@
<string name="intent_send_encrypt">Шифруј помоћу Отвореног кључарника</string>
<string name="intent_send_decrypt">Дешифруј помоћу Отвореног кључарника</string>
<!--Remote API-->
- <string name="api_settings_show_info">Прикажи напредне податке</string>
- <string name="api_settings_hide_info">Сакриј напредне податке</string>
- <string name="api_settings_show_advanced">Прикажи напредне поставке</string>
- <string name="api_settings_hide_advanced">Сакриј напредне поставке</string>
+ <string name="api_settings_show_info">Прикажи додатне податке</string>
+ <string name="api_settings_hide_info">Сакриј додатне податке</string>
+ <string name="api_settings_show_advanced">Прикажи додатне поставке</string>
+ <string name="api_settings_hide_advanced">Сакриј додатне поставке</string>
<string name="api_settings_no_key">Није изабран кључ</string>
<string name="api_settings_select_key">Изаберите кључ</string>
- <string name="api_settings_create_key">Направи нови кључ за овај налог</string>
+ <string name="api_settings_create_key">Направи нови кључ</string>
<string name="api_settings_save">Сачувај</string>
<string name="api_settings_save_msg">Налог је сачуван</string>
<string name="api_settings_cancel">Одустани</string>
@@ -465,9 +540,9 @@
<string name="api_settings_start">Покрени апликацију</string>
<string name="api_settings_delete_account">Обриши налог</string>
<string name="api_settings_package_name">Име пакета</string>
- <string name="api_settings_package_signature">СХА-256 потписа пакета</string>
- <string name="api_settings_accounts">Налози (застарели АПИ)</string>
- <string name="api_settings_advanced">Напредни подаци</string>
+ <string name="api_settings_package_certificate">СХА-256 сертификата пакета</string>
+ <string name="api_settings_accounts">Налози (стари АПИ)</string>
+ <string name="api_settings_advanced">Додатни подаци</string>
<string name="api_settings_allowed_keys">Дозвољени кључеви</string>
<string name="api_settings_settings">Поставке</string>
<string name="api_settings_key">Кључ налога:</string>
@@ -478,14 +553,29 @@
<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">Нема кључева за ове идентитете:</string>
- <string name="api_select_pub_keys_dublicates_text">Постоји више кључева за ове идентитете:</string>
+ <string name="api_select_pub_keys_missing_text">Нема кључева за ове е-адресе:</string>
+ <string name="api_select_pub_keys_dublicates_text">Постоји више кључева за ове е-адресе:</string>
<string name="api_select_pub_keys_text">Ревидирајте списак прималаца!</string>
<string name="api_select_pub_keys_text_no_user_ids">Изаберите примаоце!</string>
<string name="api_error_wrong_signature">Провера потписа није успела! Да ли сте инсталирали ову апликацију са другог извора? Ако сте сигурни да ово није напад, опозовите регистрацију ове апликације у Отвореном кључарнику и региструјте је поново.</string>
+ <string name="api_select_sign_key_text">Изаберите ваш постојећи кључ или направите нови.</string>
+ <string name="api_select_keys_text">Ниједан од дозвољених кључева није у стању да дешифрује садржај. Изаберите дозвољене кључеве.</string>
<!--Share-->
- <string name="share_qr_code_dialog_title">Поделите бар-кодом</string>
+ <string name="share_qr_code_dialog_title">Поделите бар-кôдом</string>
<string name="share_nfc_dialog">Поделите преко НФЦ-а</string>
+ <!--retry upload dialog-->
+ <string name="retry_up_dialog_title">Отпремање није успело</string>
+ <string name="retry_up_dialog_message">Отпремање није успело. Желите ли да покушам поново?</string>
+ <string name="retry_up_dialog_btn_reupload">Понови радњу</string>
+ <string name="retry_up_dialog_btn_cancel">Откажи радњу</string>
+ <!--Delete or revoke private key dialog-->
+ <string name="del_rev_dialog_message">Ако више не желите да користите овај кључ, требали бисте да га опозовете и отпремите. Изаберите „САМО ОБРИШИ“ ако желите да га уклоните из Отвореног кључарника али наставите да га користите на другде.</string>
+ <string name="del_rev_dialog_title">Опозови/обриши кључ „%s“</string>
+ <string name="del_rev_dialog_btn_revoke">Опозови и отпреми</string>
+ <string name="del_rev_dialog_btn_delete">Само обриши</string>
+ <!--Delete Or Revoke Dialog spinner-->
+ <string name="del_rev_dialog_choice_delete">Само обриши</string>
+ <string name="del_rev_dialog_choice_rev_upload">Опозови и отпреми</string>
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">%d кључ изабран.</item>
@@ -499,13 +589,13 @@
<string name="key_view_action_edit">Уреди кључ</string>
<string name="key_view_action_encrypt">Шифруј текст</string>
<string name="key_view_action_encrypt_files">фајлови</string>
- <string name="key_view_action_certify">Овери идентитете</string>
+ <string name="key_view_action_certify">Потврди кључ</string>
<string name="key_view_action_update">Ажурирај са сервера кључева</string>
- <string name="key_view_action_share_with">Подели са…</string>
+ <string name="key_view_action_share_with">Подели преко…</string>
<string name="key_view_action_share_nfc">Подели преко НФЦ</string>
<string name="key_view_action_upload">Отпреми на сервер кључева</string>
<string name="key_view_tab_main">Главни подаци</string>
- <string name="key_view_tab_share">Подели</string>
+ <string name="key_view_tab_share">Подела</string>
<string name="key_view_tab_keys">Поткључеви</string>
<string name="key_view_tab_certs">Сертификати</string>
<string name="key_view_tab_keybase">Keybase.io</string>
@@ -518,11 +608,44 @@
<string name="user_id_info_invalid_title">Неисправан</string>
<string name="user_id_info_invalid_text">Нешто није у реду са овим идентитетом!</string>
<!--Key trust-->
+ <string name="key_trust_already_verified">Већ сте потврдили овај кључ!</string>
<string name="key_trust_it_is_yours">Ово је један од ваших кључева!</string>
- <string name="key_trust_revoked">Власни је опозвао овај кључ. Не бисте требали да се поуздате у њега.</string>
+ <string name="key_trust_maybe">Кључ није ни опозван нити је истекао.\nЈош га нисте потврдили, али можете да се поуздате у њега ако желите.</string>
+ <string name="key_trust_revoked">Власник је опозвао овај кључ. Не бисте требали да се поуздате у њега.</string>
<string name="key_trust_expired">Овај кључ је истекао. Не бисте требали да се поуздате у њега.</string>
+ <string name="key_trust_old_keys">Можда је у реду користити овај кључ за дешифровање старе поруке из времена кад је био важећи.</string>
+ <string name="key_trust_no_cloud_evidence">Нема доказа са клауда о поузданости овог кључа.</string>
<string name="key_trust_start_cloud_search">Почни претрагу</string>
+ <string name="key_trust_results_prefix">Keybase.io нуди „доказе“ који потврђују да власник овог кључа: </string>
+ <string name="key_trust_header_text">Напомена: Докази са Keybase.io су експериментална функција Отвореног кључарника. Препорука је да очитавате бар-кôдове или размењујете кључеве преко НФЦ-а како бисте их потврдили.</string>
<!--keybase proof stuff-->
+ <string name="keybase_narrative_twitter">Објављује на Твитеру као %s</string>
+ <string name="keybase_narrative_github">Познат је на Гитхабу као %s</string>
+ <string name="keybase_narrative_dns">Управља доменима %s</string>
+ <string name="keybase_narrative_web_site">Може да објављује на вебсајтовима %s</string>
+ <string name="keybase_narrative_reddit">Објављује на Редиту као %s</string>
+ <string name="keybase_narrative_coinbase">Познат на Коинбејсу као %s</string>
+ <string name="keybase_narrative_hackernews">Објављује на Хакер Њуз као %s</string>
+ <string name="keybase_narrative_unknown">Непознат тип доказа %s</string>
+ <string name="keybase_proof_failure">Нажалост, овај доказ не може да се овери.</string>
+ <string name="keybase_unknown_proof_failure">Непрепознат проблем са провером доказа</string>
+ <string name="keybase_problem_fetching_evidence">Проблем са доказом</string>
+ <string name="keybase_key_mismatch">Отисак кључа се не поклапа са оним у објави доказа</string>
+ <string name="keybase_dns_query_failure">Добављање ДНС ТЕКСТ записа није успело</string>
+ <string name="keybase_no_prover_found">Нема провере доказа за</string>
+ <string name="keybase_message_payload_mismatch">Дешифрована објава доказа се не поклапа са очекиваном вредношћу</string>
+ <string name="keybase_message_fetching_data">Добављам доказ</string>
+ <string name="keybase_proof_succeeded">Овај доказ је оверен!</string>
+ <string name="keybase_a_post">Објава</string>
+ <string name="keybase_fetched_from">добављен/а са</string>
+ <string name="keybase_for_the_domain">за домен</string>
+ <string name="keybase_contained_signature">садржи поруку коју је могао да направи само власник овог кључа:</string>
+ <string name="keybase_twitter_proof">Твит</string>
+ <string name="keybase_dns_proof">ДНС ТЕКСТ запис</string>
+ <string name="keybase_web_site_proof">текстуални фајл</string>
+ <string name="keybase_github_proof">гист</string>
+ <string name="keybase_reddit_proof">ЈСОН фајл</string>
+ <string name="keybase_reddit_attribution">приписује Редит за</string>
<string name="keybase_verify">Овери</string>
<!--Edit key-->
<string name="edit_key_action_change_passphrase">Измени лозинку</string>
@@ -542,29 +665,56 @@
<item>Измени истицање</item>
<item>Опозови поткључ</item>
<item>Оголи поткључ</item>
+ <item>Премести поткључ у Јубикључ/Смарт картицу</item>
</string-array>
<string name="edit_key_new_subkey">нови поткључ</string>
<string name="edit_key_select_flag">Изаберите бар једну заставицу!</string>
<string name="edit_key_error_add_identity">Додајте бар један идентитет!</string>
<string name="edit_key_error_add_subkey">Додајте бар један поткључ!</string>
+ <string name="edit_key_error_bad_nfc_algo">Смарт картица не подржава овај алгоритам!</string>
+ <string name="edit_key_error_bad_nfc_size">Смарт картица не подржава ову величину кључа!</string>
+ <string name="edit_key_error_bad_nfc_stripped">Не могу да преместим кључ на смарт картицу (или је огољен или је „преусмери-на-картицу“)!</string>
<!--Create key-->
- <string name="create_key_upload">Отпреми кључ на сервер кључева</string>
+ <string name="create_key_upload">Синхронизуј са клаудом</string>
<string name="create_key_empty">Ово поље је обавезно</string>
<string name="create_key_passphrases_not_equal">Лозинке се не поклапају</string>
<string name="create_key_final_text">Унели сте следећи идентитет:</string>
<string name="create_key_final_robot_text">Прављење кључа може да потраје, попијте кафу у међувремену…</string>
<string name="create_key_rsa">(3 поткључа, РСА, 4096 бита)</string>
<string name="create_key_custom">(прилагођена конфигурација кључа)</string>
- <string name="create_key_identity_text">Унесите пуно име, адресу е-поште и укуцајте лозинку.</string>
- <string name="create_key_hint_full_name">Пуно име, нпр. Петар Петровић</string>
+ <string name="create_key_name_text">Одредите име које се односи на овај кључ. То може да буде пуно име, нпр. „Јован Јовановић“, или надимак, нпр. „Змај“.</string>
+ <string name="create_key_email_text">Унесите вашу главну е-адресу коју користите за безбедну комуникацију.</string>
+ <string name="create_key_passphrase_text">Одредите јаку лозинку. Она штити ваш кључ ако вам украду уређај.</string>
+ <string name="create_key_hint_full_name">Пуно име или надимак</string>
<string name="create_key_edit">Промени конфигурацију кључа</string>
+ <string name="create_key_add_email">Додај е-адресу</string>
+ <string name="create_key_add_email_text">Додатне е-адресе се такође односе на овај кљул и могу да се користе за безбедну комуникацију.</string>
+ <string name="create_key_email_already_exists_text">Е-адреса је већ додата</string>
+ <string name="create_key_email_invalid_email">Формат е-адресе није исправан</string>
+ <string name="create_key_yubi_key_pin_text">Запамтите ПИН, биће вам потребан за касније коришћење Јубикључа. Запишите администраторски ПИН и сачувајте га на безбедном месту.</string>
+ <string name="create_key_yubi_key_pin">ПИН</string>
+ <string name="create_key_yubi_key_admin_pin">Администраторски ПИН</string>
+ <string name="create_key_yubi_key_pin_repeat_text">Унесите ПИН и администраторски ПИН да бисте наставили.</string>
+ <string name="create_key_yubi_key_pin_repeat">Поновите ПИН</string>
+ <string name="create_key_yubi_key_admin_pin_repeat">Поновите администраторски ПИН</string>
+ <string name="create_key_yubi_key_pin_not_correct">ПИН није тачан!</string>
<!--View key-->
<string name="view_key_revoked">Опозван: кључ више не смије бити коришћен!</string>
<string name="view_key_expired">Истекао: контакт мора да продужи ваљаност кључа!</string>
<string name="view_key_expired_secret">Истекао: можете да уредите кључ и продужите му ваљаност!</string>
<string name="view_key_my_key">Мој кључ</string>
- <string name="view_key_verified">Оверен кључ</string>
- <string name="view_key_unverified">Неоверен: очитајте бар-код да бисте оверили кључ!</string>
+ <string name="view_key_verified">Потврђен кључ</string>
+ <string name="view_key_unverified">Непотврђен: очитајте бар-кôд да бисте потврдили кључ!</string>
+ <string name="view_key_fragment_no_system_contact">&lt;ништа&gt;</string>
+ <!--Add/Edit keyserver-->
+ <string name="add_keyserver_dialog_title">Додај сервер кључева</string>
+ <string name="edit_keyserver_dialog_title">Промени сервер кључева</string>
+ <string name="add_keyserver_verified">Сервер кључева оверен!</string>
+ <string name="add_keyserver_without_verification">Сервер кључева додат без оверивања.</string>
+ <string name="add_keyserver_invalid_url">Неисправан УРЛ!</string>
+ <string name="add_keyserver_connection_failed">Неуспех повезивања са сервером кључева. Проверите УРЛ и вашу везу са интернетом.</string>
+ <string name="keyserver_preference_deleted">%s обрисан</string>
+ <string name="keyserver_preference_cannot_delete_last">Не могу да обришем последњи сервер кључева. Потребан је бар један!</string>
<!--Navigation Drawer-->
<string name="nav_keys">Кључеви</string>
<string name="nav_encrypt_decrypt">Шифруј/дешифруј</string>
@@ -572,6 +722,7 @@
<string name="drawer_open">Отвори навигациону фиоку</string>
<string name="drawer_close">Затвори навигациону фиоку</string>
<string name="my_keys">Моји кључеви</string>
+ <string name="nav_backup">Резерва</string>
<!--hints-->
<string name="encrypt_content_edit_text_hint">Укуцајте текст</string>
<!--certs-->
@@ -695,10 +846,10 @@
<string name="msg_is_pubring_generate">Генеришем јавни привезак од тајног привеска</string>
<string name="msg_is_subkey_nonexistent">Поткључ %s није доступан у тајном кључу</string>
<string name="msg_is_subkey_ok">Тајни поткључ %d означен као доступан</string>
- <string name="msg_is_subkey_empty">Тајни поткључ %d означен као доступан, без лозинке</string>
+ <string name="msg_is_subkey_empty">Тајни поткључ %s означен као доступан, са празном лозинком</string>
<string name="msg_is_subkey_pin">Тајни поткључ %s означен као доступан, са ПИНом</string>
<string name="msg_is_subkey_stripped">Тајни поткључ %d означен као огољен</string>
- <string name="msg_is_subkey_divert">Тајни поткључ %d означен као „преусмерен на картицу/НФЦ“</string>
+ <string name="msg_is_subkey_divert">Тајни поткључ %s означен као „преусмери-на-картицу“</string>
<string name="msg_is_success_identical">Привезак не садржи нове податке, нема шта да се ради</string>
<string name="msg_is_success">Успешно увезен тајни привезак</string>
<!--Keyring Canonicalization log entries-->
@@ -725,6 +876,7 @@
<string name="msg_kc_sub_bad_local">Уклањам повезујући сертификат поткључа са заставицом „локални“</string>
<string name="msg_kc_sub_bad_keyid">Неслагање ид-а издаваоца везивања поткључа</string>
<string name="msg_kc_sub_bad_time">Уклањам повезујући сертификат поткључа са временском ознаком у будућности</string>
+ <string name="msg_kc_sub_bad_time_early">Повезујући сертификат поткључа има ранију временску ознаку него његов кључ!</string>
<string name="msg_kc_sub_bad_type">Непознат тип сертификата поткључа: %s</string>
<string name="msg_kc_sub_dup">Уклањам сувишни повезујући сертификат поткључа</string>
<string name="msg_kc_sub_primary_bad">Уклањам повезујући сертификат поткључа због неисправног примарног повезујућег сертификата</string>
@@ -803,7 +955,9 @@
<string name="msg_cr_error_flags_ecdh">Изабране су погрешне заставице кључа, ЕЦДХ не може да се користи за потписивање!</string>
<!--modifySecretKeyRing-->
<string name="msg_mr">Модификујем привезак %s</string>
- <string name="msg_mf_error_divert_serial">Серијски број преусмеравање-у-картицу кључа мора бити 16 бита. Ово је грешка у програмирању, поднесите извештај о грешци!</string>
+ <string name="msg_mf_divert">Преусмеревам на смарт картицу за криптографске радње</string>
+ <string name="msg_mf_error_divert_newsub">Прављење нових поткључева није подржано за „преусмери-на-картицу“ примарне кључеве!</string>
+ <string name="msg_mf_error_divert_serial">Серијски број „преусмери-на-картицу“ кључа мора бити 16 бита! Ово је грешка у програмирању, поднесите извештај о грешци!</string>
<string name="msg_mf_error_encode">Изузетак кодирања!</string>
<string name="msg_mf_error_fingerprint">Стварни отисак кључа не одговара очекиваном!</string>
<string name="msg_mf_error_keyid">Нема ИД-а кључа. Ово је унутрашња грешка, поднесите извештај о грешци!</string>
@@ -814,25 +968,37 @@
<string name="msg_mf_error_restricted">Покушај извршења ограничене радње без лозинке! Ово је грешка у програмирању, поднесите извештај о грешци!</string>
<string name="msg_mf_error_revoked_primary">Опозвани кориснички ИД-ови не могу бити примарни!</string>
<string name="msg_mf_error_null_expiry">Датум истицања не може бити „исти као пре“ на стварању поткључа. Ово је грешка у програмирању, поднесите извештај о грешци!</string>
+ <string name="msg_mf_error_noop">Нема ништа за радити!</string>
<string name="msg_mf_error_passphrase_master">Кобна грешка дешифровања главног кључа! Ово је вероватно грешка у програмирању, поднесите извештај о грешци!</string>
<string name="msg_mf_error_pgp">Унутрашња ОпенПГП грешка!</string>
<string name="msg_mf_error_sig">Изузетак потписа!</string>
+ <string name="msg_mf_error_sub_stripped">Не могу да модификујем огољени поткључ %s!</string>
+ <string name="msg_mf_error_subkey_missing">Покушај радње на недостајућем поткључу %s!</string>
+ <string name="msg_mf_error_conflicting_nfc_commands">Не могу да преместим кључ на смарт картицу истом радњом која прави потпис на картици.</string>
+ <string name="msg_mf_error_duplicate_keytocard_for_slot">Смарт картица подржава само један слот по типу кључа.</string>
+ <string name="msg_mf_error_invalid_flags_for_keytocard">Неодговарајуће заставице кључа за кључ смарт картице.</string>
<string name="msg_mf_master">Модификујем главне сертификате</string>
<string name="msg_mf_notation_empty">Додајем празни пакет нотације</string>
<string name="msg_mf_notation_pin">Додајем ПИН пакет нотације</string>
<string name="msg_mf_passphrase">Мењам лозинку за привезак</string>
+ <string name="msg_mf_pin">Мењам ПИН на картици</string>
+ <string name="msg_mf_admin_pin">Мењам администраторски ПИН на картици</string>
<string name="msg_mf_passphrase_key">Поново шифрујем поткључ %s новом лозинком</string>
<string name="msg_mf_passphrase_empty_retry">Постављање нове лозинке није успело, покушавам поново са празном старом лозинком</string>
<string name="msg_mf_passphrase_fail">Не могу да променим лозинку за поткључ! (Да ли се разликује од оне у осталим кључевима?)</string>
<string name="msg_mf_primary_replace_old">Замењујем сертификат претходног примарног корисничког ИД-а</string>
<string name="msg_mf_primary_new">Генеришем нови сертификат за нови примарни кориснички ИД</string>
+ <string name="msg_mf_restricted_mode">Пребацујем на ограничени режим рада</string>
<string name="msg_mf_subkey_change">Модификујем поткључ %s</string>
- <string name="msg_mf_error_subkey_missing">Покушај радње на недостајућем поткључу %s!</string>
+ <string name="msg_mf_require_divert">Преусмеравам на смарт картицу за крипто радње</string>
+ <string name="msg_mf_require_passphrase">Потребна је лозинка за извршење радњи</string>
<string name="msg_mf_subkey_new">Додајем нови поткључ типа %s</string>
<string name="msg_mf_subkey_new_id">ИД новог поткључа: %s</string>
<string name="msg_mf_error_past_expiry">Датум истицања не може бити у прошлости!</string>
<string name="msg_mf_subkey_revoke">Опозивам поткључ %s</string>
<string name="msg_mf_subkey_strip">Огољавам поткључ %s</string>
+ <string name="msg_mf_keytocard_start">Премештам поткључ %s у Смарт картицу</string>
+ <string name="msg_mf_keytocard_finish">Премештен %1$s на смарт картицу %2$s</string>
<string name="msg_mf_success">Привезак успешно модификован</string>
<string name="msg_mf_uid_add">Додајем кориснички ИД %s</string>
<string name="msg_mf_uid_primary">Постављам примарни кориснички ИД на %s</string>
@@ -882,16 +1048,18 @@
<string name="msg_ed_caching_new">Кеширам нову лозинку</string>
<string name="msg_ed_error_no_parcel">Недостаје SaveKeyringParcel! (ово је грешка, пријавите је)</string>
<string name="msg_ed_error_key_not_found">Кључ није нађен!</string>
+ <string name="msg_ed_error_extract_public_upload">Грешка извлачења јавног кључа за отпремање!</string>
<string name="msg_ed_fetching">Добављам кључ за модификовање (%s)</string>
<string name="msg_ed_success">Радња на кључу је успела</string>
<!--Promote key-->
<string name="msg_pr">Унапређујем јавни кључ у тајни кључ</string>
- <string name="msg_pr_error_already_secret">Кључ је већ тајни кључ!</string>
+ <string name="msg_pr_all">Унапређујем све поткључеве</string>
<string name="msg_pr_error_key_not_found">Кључ није нађен!</string>
<string name="msg_pr_fetching">Добављам кључ за модификовање (%s)</string>
+ <string name="msg_pr_subkey_match">Унапређујем поткључ: %s</string>
+ <string name="msg_pr_subkey_nomatch">Поткључ није на Јубикључу: %s</string>
<string name="msg_pr_success">Кључ успешно унапређен</string>
<!--Other messages used in OperationLogs-->
- <string name="msg_ek_error_divert">Уређивање НФЦ кључева (још) није подржано!</string>
<string name="msg_ek_error_dummy">Не могу да уредим кључ са огољеним главним кључем!</string>
<string name="msg_ek_error_not_found">Кључ није нађен!</string>
<!--Messages for DecryptVerify operation-->
@@ -912,17 +1080,19 @@
<string name="msg_dc_clear_signature">Уписујем податке потписа за касније</string>
<string name="msg_dc_clear">Обрађујем податке обичног текста</string>
<string name="msg_dc_error_bad_passphrase">Грешка откључавања кључа, погрешна лозинка!</string>
+ <string name="msg_dc_error_sym_passphrase">Грешка дешифровања података! (Лоша лозинка?)</string>
+ <string name="msg_dc_error_corrupt_data">Подаци су оштећени!</string>
<string name="msg_dc_error_extract_key">Непозната грешка откључавања кључа!</string>
<string name="msg_dc_error_integrity_check">Грешка провере интегритета!</string>
- <string name="msg_dc_error_integrity_missing">Недостаје провера интегритета! Ово може да се деси ако је апликација за шифровање застарела, или услед напада старијег издања.</string>
- <string name="msg_dc_error_invalid_siglist">Нису нађени исправни подаци потписа!</string>
- <string name="msg_dc_error_io">Наиђох на У/И изузетак током радње!</string>
+ <string name="msg_dc_error_invalid_data">Нема исправних ОпенПГП шифрованих или потписаних података!</string>
+ <string name="msg_dc_error_io">Наиђох на грешку при читању улазних података!</string>
+ <string name="msg_dc_error_input">Грешка отварања тока улазних података!</string>
<string name="msg_dc_error_no_data">Шифровани подаци нису нађени у току!</string>
<string name="msg_dc_error_no_key">Подаци шифровани познатим тајним кључем нису нађени у току!</string>
<string name="msg_dc_error_pgp_exception">Наиђох на ОпенПГП изузетак током радње!</string>
<string name="msg_dc_integrity_check_ok">Провера интегритета је у реду!</string>
<string name="msg_dc_ok_meta_only">Само су метаподаци затражени, прескачем дешифровање</string>
- <string name="msg_dc_ok">У реду</string>
+ <string name="msg_dc_ok">Дешифровање/оверавање завршено</string>
<string name="msg_dc_pass_cached">Користим лозинку из кеша</string>
<string name="msg_dc_pending_nfc">Потребан је НФЦ токен, захтевам унос корисника…</string>
<string name="msg_dc_pending_passphrase">Потребна је лозинка, захтевам унос корисника…</string>
@@ -934,8 +1104,15 @@
<string name="msg_dc_trail_sym">Наиђох на пратеће симетрично шифроване податке</string>
<string name="msg_dc_trail_unknown">Наиђох на пратеће податке непознатог типа</string>
<string name="msg_dc_unlocking">Откључавам тајни кључ</string>
+ <string name="msg_dc_insecure_symmetric_encryption_algo">Коришћен је небезбедан алгоритам шифровања. Ово може да се деси ако је апликација за застарела, или услед напада.</string>
+ <string name="msg_dc_insecure_hash_algo">Коришћен је небезбедан алгоритам хеша. Ово може да се деси ако је апликација застарела, или услед напада.</string>
+ <string name="msg_dc_insecure_mdc_missing">Недостаје пакет Кôда детекције измена (МДЦ)! Ово може да се деси ако је апликација за шифровање застарела, или услед напада старијег издања.</string>
+ <string name="msg_dc_insecure_key">Небезбедан кључ: или је дужина РСА/ДСА/Елгамал кључа прекратка или је ЕЦЦ кривуља/алгоритам сматрана небезбедном! Ово може да се деси ако је апликација застарела, или услед напада.</string>
<!--Messages for VerifySignedLiteralData operation-->
<string name="msg_vl">Почињем проверу потписа</string>
+ <string name="msg_vl_error_no_siglist">Нема списка потписа у потписаним дословним подацима</string>
+ <string name="msg_vl_error_wrong_key">Порука није потписана правим кључем</string>
+ <string name="msg_vl_error_missing_literal">Нема корисних података у потписаним дословним подацима</string>
<string name="msg_vl_clear_meta_file">Име фајла: %s</string>
<string name="msg_vl_clear_meta_mime">МИМЕ тип: %s</string>
<string name="msg_vl_clear_meta_time">Време измене: %s</string>
@@ -951,7 +1128,6 @@
<string name="msg_se_error_input_uri_not_found">Грешка отварања УРИ-ја за читање!</string>
<string name="msg_se_error_output_uri_not_found">Грешка отварања УРИ-ја за упис!</string>
<string name="msg_se_error_too_many_inputs">Наведено више улаза него излаза! Ово је грешка у програмирању, поднесите извештај о грешци!</string>
- <string name="msg_se_warn_output_left">Преостали су излази али нема улаза. Ово је вероватно грешка у програмирању, поднесите извештај о грешци.</string>
<string name="msg_se_success">Радња потписивања/шифровања је успела!</string>
<!--Messages for PgpSignEncrypt operation-->
<string name="msg_pse_asymmetric">Припремам јавне кључеве за шифровање</string>
@@ -959,7 +1135,6 @@
<string name="msg_pse_compressing">Припремам компресију</string>
<string name="msg_pse_encrypting">Шифрујем податке</string>
<string name="msg_pse_error_bad_passphrase">Нетачна лозинка!</string>
- <string name="msg_pse_error_hash_algo">Овај кључ не подржава захтевани хеш алгоритам!</string>
<string name="msg_pse_error_io">Наиђох на У/И изузетак током радње!</string>
<string name="msg_pse_error_key_sign">Изабрани кључ за потписивање не може да потпише податке!</string>
<string name="msg_pse_error_sign_key">Грешка добављања кључа за потписивање!</string>
@@ -981,19 +1156,23 @@
<string name="msg_pse">Почињем радњу потписивања и/или шифровања</string>
<string name="msg_pse_symmetric">Припремам симетрично шифровање</string>
<string name="msg_crt_certifying">Генеришем сертификате</string>
- <string name="msg_crt_certify_all">Оверавам све корисничке ИД-ове за кључ %s</string>
- <plurals name="msg_crt_certify_some">
- <item quantity="one">Оверавам један кориснички ИД за кључ %2$s</item>
- <item quantity="few">Оверавам %1$d корисничка ИД-а за кључ %2$s</item>
- <item quantity="other">Оверавам %1$d корисничких ИД-ова за кључ %2$s</item>
+ <plurals name="msg_crt_certify_uids">
+ <item quantity="one">Потврђујем кориснички ИД за кључ %2$s</item>
+ <item quantity="few">Потврђујем %1$d корисничка ИД-а за кључ %2$s</item>
+ <item quantity="other">Потврђујем %1$d корисничких ИД-ова за кључ %2$s</item>
+ </plurals>
+ <plurals name="msg_crt_certify_uats">
+ <item quantity="one">Потврђујем кориснички атрибут за кључ %2$s</item>
+ <item quantity="few">Потврђујем %1$d корисничка атрибута за кључ %2$s</item>
+ <item quantity="other">Потврђујем %1$d корисничких атрибута за кључ %2$s</item>
</plurals>
<string name="msg_crt_error_self">Не могу да издам овакав самопотписани сертификат!</string>
<string name="msg_crt_error_master_not_found">Главни кључ није нађен!</string>
<string name="msg_crt_error_nothing">Нема оверених кључева!</string>
<string name="msg_crt_error_unlock">Грешка откључавања главног кључа!</string>
- <string name="msg_crt_error_divert">Оверавање помоћу НФЦ-а (још) није подржано!</string>
<string name="msg_crt">Оверавам привеске</string>
<string name="msg_crt_master_fetch">Добављам главни кључ за оверавање</string>
+ <string name="msg_crt_nfc_return">Враћам на НФЦ екран</string>
<string name="msg_crt_save">Уписујем оверени кључ %s</string>
<string name="msg_crt_saving">Уписујем привеске</string>
<string name="msg_crt_unlock">Откључавам главни кључ</string>
@@ -1001,6 +1180,7 @@
<string name="msg_crt_warn_not_found">Кључ није нађен!</string>
<string name="msg_crt_warn_cert_failed">Генерисање сертификата није успело!</string>
<string name="msg_crt_warn_save_failed">Радња уписа није успела!</string>
+ <string name="msg_crt_warn_upload_failed">Радња отпремања није успела!</string>
<string name="msg_crt_upload_success">Кључ успешно отпремљен на сервер</string>
<plurals name="msg_import">
<item quantity="one">Увозим кључ</item>
@@ -1010,13 +1190,14 @@
<string name="msg_import_fetch_error_decode">Грешка декодирања добављеног привеска!</string>
<string name="msg_import_fetch_error">Не могу да добавим кључ! (Проблеми са мрежом?)</string>
<string name="msg_import_fetch_keybase">Добављам са keybase.io: %s</string>
- <string name="msg_import_fetch_keyserver_error">Не могу да добавим кључ из базе кључева!</string>
+ <string name="msg_import_fetch_keyserver_error">Не могу да добавим кључ са сервера кључева: %s</string>
<string name="msg_import_fetch_keyserver">Добављам са сервера кључева: %s</string>
<string name="msg_import_fetch_keyserver_ok">Добављање кључева је успело</string>
<string name="msg_import_keyserver">Користим сервер кључева %s</string>
<string name="msg_import_fingerprint_error">Отисак добављеног кључа не одговара очекиваном!</string>
<string name="msg_import_fingerprint_ok">Провера отиска је у реду</string>
<string name="msg_import_merge">Спајам добављене податке</string>
+ <string name="msg_import_merge_error">Грешка спајања добављених података!</string>
<string name="msg_import_error">Радња увоза није успела!</string>
<string name="msg_import_error_io">Радња увоза није успела због У/И грешке!</string>
<string name="msg_import_partial">Радња увоза је успела, са грешкама!</string>
@@ -1026,8 +1207,10 @@
<item quantity="few">Извозим %d кључа</item>
<item quantity="other">Извозим %d кључева</item>
</plurals>
+ <string name="msg_export_file_name">Име фајла: %s</string>
<string name="msg_export_all">Извозим све кључеве</string>
<string name="msg_export_public">Извозим јавни кључ %s</string>
+ <string name="msg_export_upload_public">Отпремам јавни кључ %s</string>
<string name="msg_export_secret">Извозим тајни кључ %s</string>
<string name="msg_export_error_no_file">Није наведено име фајла!</string>
<string name="msg_export_error_fopen">Грешка отварања фајла!</string>
@@ -1037,7 +1220,9 @@
<string name="msg_export_error_db">Грешка базе података!</string>
<string name="msg_export_error_io">Грешка улаза/излаза!</string>
<string name="msg_export_error_key">Грешка предобраде података кључа!</string>
+ <string name="msg_export_error_upload">Грешка отпремања кључа на сервер. Проверите вашу везу са интернетом.</string>
<string name="msg_export_success">Радња извоза је успела</string>
+ <string name="msg_export_upload_success">Отпремање на сервер кључева је успело</string>
<string name="msg_del_error_empty">Нема ништа за брисање!</string>
<string name="msg_del_error_multi_secret">Тајне кључеве можете брисати само појединачно!</string>
<plurals name="msg_del">
@@ -1058,6 +1243,11 @@
<item quantity="few">Брисање %d кључа није успело</item>
<item quantity="other">Брисање %d кључева није успело</item>
</plurals>
+ <string name="msg_revoke_error_empty">Нема ништа за опозив!</string>
+ <string name="msg_revoke_error_not_found">Не могу да нађем кључ за опозив!</string>
+ <string name="msg_revoke_key">Опозивам кључ %s</string>
+ <string name="msg_revoke_key_fail">Неуспех опозивања кључа</string>
+ <string name="msg_revoke_ok">Успешно опозван кључ</string>
<string name="msg_acc_saved">Налог је сачуван</string>
<string name="msg_download_success">Успешно преузето!</string>
<string name="msg_download_no_valid_keys">Нема исправних кључева у фајлу/клипборду!</string>
@@ -1071,17 +1261,47 @@
<string name="msg_download_too_many_responses">Претрага кључева је вратила превише кандидата. Прецизирајте упит!</string>
<string name="msg_download_query_too_short_or_too_many_responses">Или није нађен ниједан кључ или их је нађено превише. Побољшајте ваш упит!</string>
<string name="msg_download_query_failed">Дошло је до грешке приликом претраге кључева.</string>
+ <!--Messages for Keybase Verification operation-->
+ <string name="msg_keybase_verification">Покушавам оверавање на keybase.io за %s</string>
+ <string name="msg_keybase_error_no_prover">Нема провере доказа за %s</string>
+ <string name="msg_keybase_error_fetching_evidence">Проблем са добављањем доказа</string>
+ <string name="msg_keybase_error_key_mismatch">Отисак кључа се не поклапа са оним у објави доказа</string>
+ <string name="msg_keybase_error_dns_fail">Добављање ДНС ТЕКСТ записа није успело</string>
+ <string name="msg_keybase_error_specific">%s</string>
+ <string name="msg_keybase_error_msg_payload_mismatch">Дешифрована објава доказа се не поклапа са очекиваном вредношћу</string>
+ <!--Messages for Export Log operation-->
+ <string name="msg_export_log_start">Извозим дневник</string>
+ <string name="msg_export_log_error_fopen">Грешка отварања фајла</string>
+ <string name="msg_export_log_error_no_file">Није наведено име фајла!</string>
+ <string name="msg_export_log_error_writing">У/И грешка уписа у фајл!</string>
+ <string name="msg_export_log_success">Дневник успешно извезен!</string>
<!--PassphraseCache-->
- <string name="passp_cache_notif_click_to_clear">Кликните да уклоните лозинке из кеша</string>
- <string name="passp_cache_notif_n_keys">Отворени кључарник је кеширао %d лозинки</string>
- <string name="passp_cache_notif_keys">Кеширане лозинке:</string>
- <string name="passp_cache_notif_clear">Очисти кеш</string>
+ <string name="passp_cache_notif_click_to_clear">Тапните да очистите лозинке.</string>
+ <plurals name="passp_cache_notif_n_keys">
+ <item quantity="one">%d лозинка запамћена</item>
+ <item quantity="few">%d лозинке запамћене</item>
+ <item quantity="other">%d лозинки запамћено</item>
+ </plurals>
+ <string name="passp_cache_notif_keys">Запамћене лозинке</string>
+ <string name="passp_cache_notif_clear">Очисти лозинке</string>
<string name="passp_cache_notif_pwd">Лозинка</string>
+ <!--Keyserver sync-->
+ <string name="keyserver_sync_orbot_notif_title">Синхронизација са облака захтева Орбот</string>
+ <string name="keyserver_sync_orbot_notif_msg">Тапните да бисте покренули Орбот</string>
+ <string name="keyserver_sync_orbot_notif_start">Покрени Орбот</string>
+ <string name="keyserver_sync_orbot_notif_ignore">Директно</string>
<!--First Time-->
<string name="first_time_text1">Преузмите вашу приватност помоћу Отвореног кључарника!</string>
<string name="first_time_create_key">Направи ми кључ</string>
- <string name="first_time_import_key">Увези из фајла</string>
+ <string name="first_time_import_key">Увези кључ из фајла</string>
+ <string name="first_time_yubikey">Користи Јубикључ НЕО</string>
<string name="first_time_skip">Прескочи поставу</string>
+ <string name="first_time_blank_yubikey">Желите ли да користите овај празни Јубикључ НЕО са Отвореним кључарником?\n\nСклоните сада Јубикључ, бићете упитани када опет буде потребан!</string>
+ <string name="first_time_blank_yubikey_yes">Користи овај Јубикључ</string>
+ <string name="backup_text">Резерве које укључују ваше сопствене кључеве никад немојте делити са другима!</string>
+ <string name="backup_all">Свих кључева + сопствених</string>
+ <string name="backup_public_keys">Свих кључева</string>
+ <string name="backup_section">Резерва</string>
<!--unsorted-->
<string name="section_certifier_id">Сертификатор</string>
<string name="section_cert">Детаљи сертификата</string>
@@ -1090,16 +1310,15 @@
<string name="empty_certs">Нема сертификата за овај кључ</string>
<string name="certs_text">Приказани су само потврђени сопствени сертификати и потврђени сертификати направљени вашим кључем.</string>
<string name="section_uids_to_certify">Идентитети за</string>
- <string name="certify_text">Кључеви које увозите садрже „идентитете“: имена и е-адресе. Одредите за оверу само оне који одговарају ономе што очекујете.</string>
+ <string name="certify_text">Кључеви које увозите садрже „идентитете“: имена и е-адресе. Одредите за потврду само оне који одговарају ономе што очекујете.</string>
<string name="certify_fingerprint_text">Упоредите приказани отисак, знак по знак, са оним приказаним на другаревом уређају.</string>
<string name="certify_fingerprint_text2">Да ли се приказани отисци поклапају?</string>
<string name="label_revocation">Разлог опозива</string>
- <string name="label_verify_status">Стање овере</string>
<string name="label_cert_type">Тип</string>
<string name="error_key_not_found">Кључ није нађен!</string>
<string name="error_key_processing">Грешка обраде кључа!</string>
<string name="key_stripped">огољен</string>
- <string name="key_divert">преусмери на картицу/НФЦ</string>
+ <string name="key_divert">преусмери на картицу</string>
<string name="key_no_passphrase">нема лозинке</string>
<string name="key_unavailable">недоступан</string>
<string name="secret_cannot_multiple">Сопствене кључеве можете брисати само појединачно!</string>
@@ -1107,24 +1326,30 @@
<string name="unknown_algorithm">непознат</string>
<string name="can_sign_not">не може да потпише</string>
<string name="error_no_encrypt_subkey">Поткључ за шифровање није доступан!</string>
- <string name="info_no_manual_account_creation">Не прави налоге Отвореног кључарника ручно.\nПогледајте Помоћ за више информација.</string>
<string name="contact_show_key">Прикажи кључ (%s)</string>
<string name="swipe_to_update">Превуците прстом доле да ажурирате са сервера кључева</string>
<string name="error_no_file_selected">Изаберите бар један фајл за шифровање!</string>
- <string name="error_multi_not_supported">Упис више фајлова није подржан. Ово је ограничење у текућем издању Андроида.</string>
+ <string name="error_multi_files">Упис више фајлова није подржан. Ово је ограничење у текућем издању Андроида.</string>
+ <string name="error_multi_clipboard">Дешифровање више фајлова на клипборд није подржано.</string>
+ <string name="error_detached_signature">Радња само потписивања бинарних фајлова није подржана, одредите бар један кључ шифровања.</string>
+ <string name="error_empty_text">Укуцајте неки текст за шифровање!</string>
<string name="key_colon">Кључ:</string>
<string name="exchange_description">Да бисте почели размену кључева, са десне стране изаберите број учесника и додирните дугме „Почни размену“.\n\nБиће вам постављено још два питања да би се осигурало да су само исправни учесници у размени и да су њихови отисци тачни.</string>
<string name="btn_start_exchange">Почни размену</string>
<string name="user_id_none"><![CDATA[<ништа>]]></string>
+ <!--Android Account-->
+ <string name="account_no_manual_account_creation">Не можете да направите налоге Отвореног кључарника ручно.</string>
+ <string name="account_privacy_title">Приватност</string>
+ <string name="account_privacy_text">Отворени кључарник не синхронизује ваше контакте на интернет. Само повезује контакте са кључевима засновано на именима и е-адресама. Ово ради ван везе на вашем уређају.</string>
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
<string name="title_unlock_method">Одредите методу откључавања</string>
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<string name="enter_passphrase">Унесите лозинку</string>
<string name="passphrase">Лозинка</string>
- <string name="noPassphrase">Без лозинке</string>
+ <string name="noPassphrase">Нема лозинке</string>
<string name="no_passphrase_set">Лозинка није постављена</string>
- <string name="passphrases_match">Лозинке се поклапају</string>
+ <string name="passphrases_match">Лозинке се не поклапају</string>
<string name="passphrase_saved">Лозинка сачувана</string>
<string name="passphrase_invalid">Лозинка није исправна</string>
<string name="missing_passphrase">Недостаје лозинка</string>
@@ -1142,5 +1367,63 @@
<string name="nfc_write_succesful">Успешно уписах на НФЦ ознаку</string>
<string name="unlocked">Откључан</string>
<string name="nfc_settings">Поставке</string>
- <string name="file_delete_successful"></string>
+ <string name="snack_yubikey_view">Приказ</string>
+ <string name="snack_yubikey_import">Увоз</string>
+ <string name="button_bind_key">Повежи кључ</string>
+ <string name="yubikey_serno">Серијски број: %s</string>
+ <string name="yubikey_key_holder">Власник кључа: </string>
+ <string name="yubikey_key_holder_not_set">Власник кључа: &lt;није постављен&gt;</string>
+ <string name="yubikey_status_bound">Јубикључ одговара и повезан са кључем</string>
+ <string name="yubikey_status_unbound">Јубикључ одговара, може да се повеже са кључем</string>
+ <string name="yubikey_status_partly">Јубикључ одговара, делимично повезан са кључем</string>
+ <string name="yubikey_create">Држите Јубикључ на полеђини вашег уређаја.</string>
+ <string name="btn_import">Увези</string>
+ <string name="snack_yubi_other">Други кључ је смештен на Јубикључу!</string>
+ <string name="error_nfc">НФЦ грешка: %s</string>
+ <plurals name="error_pin">
+ <item quantity="one">Нетачан ПИН!\n%d покушај преостао.</item>
+ <item quantity="few">Нетачан ПИН!\n%d покушаај преостала.</item>
+ <item quantity="other">Нетачан ПИН!\n%d покушаја преостало.</item>
+ </plurals>
+ <string name="error_nfc_terminated">Јубикључ у завршном стању.</string>
+ <string name="error_nfc_wrong_length">Унети ПИН је прекратак. ПИНови су дуги најмање 6 бројки.</string>
+ <string name="error_nfc_conditions_not_satisfied">Услови употребе нису задовољени.</string>
+ <string name="error_nfc_security_not_satisfied">Безбедносно стање није задовољено.</string>
+ <string name="error_nfc_authentication_blocked">ПИН блокиран након превише покушаја.</string>
+ <string name="error_nfc_data_not_found">Кључ или објекат нису нађени!</string>
+ <string name="error_nfc_unknown">Непозната грешка</string>
+ <string name="error_nfc_bad_data">Јубикључ је пријавио неисправне податке.</string>
+ <string name="error_nfc_chaining_error">Јубикључ је очекивао последњу наредбу у ланцу.</string>
+ <string name="error_nfc_header">Јубикључ је пријавио неисправан %s бит.</string>
+ <string name="error_nfc_tag_lost">Јубикључ је прерано склоњен. Држите Јубикључ на полеђини уређаја док се радња не заврши.</string>
+ <string name="error_nfc_try_again">Покушај поново</string>
+ <string name="error_pin_nodefault">Подразумевани ПИН је одбијен!</string>
+ <string name="error_temp_file">Грешка стварања привременог фајла.</string>
+ <string name="btn_delete_original">Обриши оригинални фајл</string>
+ <string name="snack_encrypt_filenames_on">Имена фајлова <b>су</b> шифрована.</string>
+ <string name="snack_encrypt_filenames_off">Имена фајлова <b>нису</b> шифрована.</string>
+ <string name="snack_armor_on">Излаз кодиран као текст.</string>
+ <string name="snack_armor_off">Излаз кодиран као бинарна.</string>
+ <string name="snack_compression_on">Компресија је <b>омогућена</b>.</string>
+ <string name="snack_compression_off">Компресија је <b>онемогућена</b>.</string>
+ <string name="error_loading_keys">Грешка учитавања кључева!</string>
+ <string name="error_empty_log">(грешка, празан дневник)</string>
+ <string name="error_reading_text">Не могу да очитам унос за дешифровање!</string>
+ <string name="filename_unknown">&lt;нема имена&gt;</string>
+ <string name="filename_unknown_text">&lt;обични текстуални подаци&gt;</string>
+ <string name="intent_show">Прикажи потписани/шифровани садржај</string>
+ <string name="view_internal">Прикажи у Отвореном кључарнику</string>
+ <string name="error_preparing_data">Грешка припремања података!</string>
+ <string name="label_clip_title">Шифровани подаци</string>
+ <string name="progress_processing">Обрађујем…</string>
+ <string name="error_saving_file">Грешка уписа фајла!</string>
+ <string name="file_saved">Фајл сачуван!</string>
+ <string name="file_delete_ok">Оригинални фајл обрисан.</string>
+ <string name="file_delete_none">Ниједан фајл није обрисан! (Већ обрисано?)</string>
+ <string name="file_delete_exception">Оригинални фајл није обрисан!</string>
+ <string name="error_clipboard_empty">Клипборд је празан!</string>
+ <string name="error_clipboard_copy">Грешка копирања података на клипборд!</string>
+ <string name="error_scan_fp">Грешка очитавања отиска!</string>
+ <string name="error_scan_match">Отисци се не поклапају!</string>
+ <string name="error_expiry_past">Датум истицања је у прошлости!</string>
</resources>
diff --git a/OpenKeychain/src/main/res/values-sv/strings.xml b/OpenKeychain/src/main/res/values-sv/strings.xml
index f73be4861..1ccac9199 100644
--- a/OpenKeychain/src/main/res/values-sv/strings.xml
+++ b/OpenKeychain/src/main/res/values-sv/strings.xml
@@ -7,13 +7,12 @@
<string name="title_encrypt_text">Kryptera</string>
<string name="title_encrypt_files">Kryptera</string>
<string name="title_decrypt">Dekryptera</string>
- <string name="title_unlock">Lås upp nyckel</string>
<string name="title_add_subkey">Lägg till undernyckel</string>
<string name="title_edit_key">Redigera nyckel</string>
<string name="title_preferences">Inställningar</string>
<string name="title_api_registered_apps">Appar</string>
- <string name="title_key_server_preference">Nyckelservrar</string>
- <string name="title_change_passphrase">Ändra lösenordsfras</string>
+ <string name="title_key_server_preference">OpenPGP-nyckelservrar</string>
+ <string name="title_change_passphrase">Ändra lösenord</string>
<string name="title_share_fingerprint_with">Dela fingeravtryck med…</string>
<string name="title_share_key">Dela nyckel med…</string>
<string name="title_share_file">Dela fil med…</string>
@@ -21,8 +20,6 @@
<string name="title_encrypt_to_file">Kryptera till fil</string>
<string name="title_decrypt_to_file">Dekryptera till fil</string>
<string name="title_import_keys">Importera nycklar</string>
- <string name="title_export_key">Exportera nyckel</string>
- <string name="title_export_keys">Exportera nycklar</string>
<string name="title_key_not_found">Nyckel hittades inte</string>
<string name="title_send_key">Ladda upp till nyckelserver</string>
<string name="title_certify_key">Bekräfta nyckel</string>
@@ -30,31 +27,31 @@
<string name="title_help">Hjälp</string>
<string name="title_log_display">Logg</string>
<string name="title_exchange_keys">Utbyt nycklar</string>
- <string name="title_advanced_key_info">Avancerad nyckelinfo</string>
+ <string name="title_advanced_key_info">Förlängd information</string>
<string name="title_delete_secret_key">Radera DIN nyckel \'%s\'?</string>
<string name="title_export_log">Exportera logg</string>
+ <string name="title_manage_my_keys">Hantera mina nycklar</string>
<!--section-->
<string name="section_user_ids">Identiteter</string>
+ <string name="section_yubikey">YubiKey</string>
+ <string name="section_linked_system_contact">Länkade systemkontakten</string>
<string name="section_should_you_trust">Skulle du lita på denna nyckel?</string>
<string name="section_proof_details">Bevisverifiering</string>
<string name="section_cloud_evidence">Bevis från molnet</string>
<string name="section_keys">Undernycklar</string>
<string name="section_cloud_search">Molnsökning</string>
- <string name="section_passphrase_cache">Cache för lösenordsfras</string>
<string name="section_certify">Bekräfta</string>
<string name="section_actions">Åtgärder</string>
<string name="section_share_key">Nyckel</string>
<string name="section_key_server">Nyckelserver</string>
<string name="section_fingerprint">Fingeravtryck</string>
<string name="section_encrypt">Kryptera</string>
- <string name="section_decrypt">Dekryptera</string>
<string name="section_current_expiry">Aktuellt utgångsdatum</string>
<string name="section_new_expiry">Nytt utgångsdaum</string>
<!--button-->
<string name="btn_decrypt_verify_file">Dekryptera, verifiera och spara fil</string>
<string name="btn_encrypt_share_file">Kryptera och dela fil</string>
<string name="btn_encrypt_save_file">Kryptera och spara fil</string>
- <string name="btn_save">Spara</string>
<string name="btn_do_not_save">Avbryt</string>
<string name="btn_delete">Radera</string>
<string name="btn_no_date">Inget utgångsdatum</string>
@@ -69,25 +66,28 @@
<string name="btn_view_cert_key">Visa nyckel för certifiering</string>
<string name="btn_create_key">Skapa nyckel</string>
<string name="btn_add_files">Lägg till fil(er)</string>
- <string name="btn_add_share_decrypted_text">Dela dekrypterad text</string>
- <string name="btn_decrypt_clipboard">Dekryptera text från urklipp</string>
- <string name="btn_decrypt_and_verify">och verifiera signaturer</string>
- <string name="btn_decrypt_files">Dekryptera filer</string>
+ <string name="btn_share_decrypted_text">Dela dekrypterad text</string>
+ <string name="btn_copy_decrypted_text">Kopiera dekrypterad text</string>
<string name="btn_encrypt_files">Kryptera filer</string>
<string name="btn_encrypt_text">Kryptera text</string>
<string name="btn_add_email">Lägg till extra e-postadress</string>
+ <string name="btn_unlock">Lås upp</string>
+ <string name="btn_add_keyserver">Lägg till</string>
+ <string name="btn_save_default">Spara som standard</string>
+ <string name="btn_saved">Sparad!</string>
<!--menu-->
<string name="menu_preferences">Inställningar</string>
<string name="menu_help">Hjälp</string>
- <string name="menu_export_key">Exportera till fil</string>
<string name="menu_delete_key">Radera nyckel</string>
+ <string name="menu_manage_keys">Hantera mina nycklar</string>
<string name="menu_search">Sök</string>
<string name="menu_nfc_preferences">NFC-inställningar</string>
<string name="menu_beam_preferences">Beam-inställningar</string>
<string name="menu_encrypt_to">Kryptera till…</string>
<string name="menu_select_all">Markera alla</string>
<string name="menu_export_all_keys">Exportera alla nycklar</string>
- <string name="menu_advanced">Visa avancerad information</string>
+ <string name="menu_update_all_keys">Uppdatera alla nycklar</string>
+ <string name="menu_advanced">Utökad information</string>
<string name="menu_certify_fingerprint">Bekräfta via fingeravtrycksjämförelse</string>
<string name="menu_export_log">Exportera logg</string>
<!--label-->
@@ -95,11 +95,11 @@
<string name="label_file">Fil</string>
<string name="label_files">Fil(er)</string>
<string name="label_file_colon">Fil:</string>
- <string name="label_no_passphrase">Ingen lösenordsfras</string>
- <string name="label_passphrase">Lösenordsfras</string>
+ <string name="label_no_passphrase">Inget lösenord</string>
+ <string name="label_passphrase">Lösenord</string>
<string name="label_unlock">Låser upp…</string>
- <string name="label_passphrase_again">Upprepa lösenordsfras</string>
- <string name="label_show_passphrase">Visa lösenordsfras</string>
+ <string name="label_passphrase_again">Upprepa lösenord</string>
+ <string name="label_show_passphrase">Visa lösenord</string>
<string name="label_algorithm">Algoritm</string>
<string name="label_ascii_armor">Fil i ASCII-format</string>
<string name="label_file_ascii_armor">Aktivera ASCII-format</string>
@@ -108,19 +108,18 @@
<string name="label_use_default_yubikey_pin">Använd förvald YubiKey PIN</string>
<string name="label_use_num_keypad_for_yubikey_pin">Använd numeriska tangentbordet för YubiKey PIN</string>
<string name="label_label_use_default_yubikey_pin_summary">Använder förvald PIN (123456) för att få åtkomst till YubiKeys via NFC</string>
- <string name="label_asymmetric_from">Signerat av:</string>
<string name="label_to">Kryptera till:</string>
<string name="label_delete_after_encryption">Radera filer efter kryptering</string>
<string name="label_delete_after_decryption">Radera efter dekryptering</string>
<string name="label_encryption_algorithm">Krypteringsalgoritm</string>
<string name="label_hash_algorithm">Hash-algoritm</string>
- <string name="label_symmetric">Kryptera med lösenordsfras</string>
- <string name="label_passphrase_cache_ttl">Tid i cache</string>
- <string name="label_passphrase_cache_subs">Cacha lösenordsfraser efter undernyckel</string>
+ <string name="label_symmetric">Kryptera med lösenord</string>
<string name="label_message_compression">Textkompression</string>
<string name="label_file_compression">Filkompression</string>
- <string name="label_keyservers">Nyckelservrar</string>
+ <string name="label_keyservers">Välj OpenPGP-nyckelservrar</string>
<string name="label_key_id">Nyckel-ID</string>
+ <string name="label_key_created">Nyckel skapad %s</string>
+ <string name="label_creation">Skapande</string>
<string name="label_expiry">Går ut</string>
<string name="label_usage">Användning</string>
<string name="label_key_size">Nyckelstorlek</string>
@@ -132,11 +131,22 @@
<string name="label_send_key">Synkronisera med molnet</string>
<string name="label_fingerprint">Fingeravtryck</string>
<string name="expiry_date_dialog_title">Ställ in utgångsdatum</string>
- <string name="label_first_keyserver_is_used">(Nyckelservern först i listan är den som föredras)</string>
<string name="label_preferred">föredraget</string>
<string name="label_enable_compression">Aktivera kompression</string>
<string name="label_encrypt_filenames">Kryptera filnamn</string>
<string name="label_hidden_recipients">Dölj mottagare</string>
+ <string name="label_verify_keyserver">Verifiera nyckelserver</string>
+ <string name="label_enter_keyserver_url">Ange nyckelserver-URL</string>
+ <string name="pref_keyserver">OpenPGP nyckelservrar</string>
+ <string name="pref_keyserver_summary">Sök nycklar på valda OpenPGP nyckelservrar (HKP-protokollet)</string>
+ <string name="pref_keybase">keybase.io</string>
+ <string name="pref_keybase_summary">Sök nycklar på keybase.io</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <!--proxy type choices and values-->
+ <!--OrbotHelper strings-->
+ <!--InstallDialogFragment strings-->
+ <!--StartOrbotDialogFragment strings-->
<string name="user_id_no_name">&lt;inget namn&gt;</string>
<string name="none">&lt;ingen&gt;</string>
<plurals name="n_keys">
@@ -176,23 +186,22 @@
<string name="flag_encrypt">Kryptera</string>
<string name="flag_authenticate">Autentisera</string>
<!--sentences-->
- <string name="wrong_passphrase">Fel lösenordsfras.</string>
+ <string name="wrong_passphrase">Fel lösenord.</string>
<string name="no_filemanager_installed">Ingen kompatibel filhanterare är installerad.</string>
- <string name="passphrases_do_not_match">Lösenordsfraserna stämde inte överens.</string>
- <string name="passphrase_must_not_be_empty">Ange en lösenordsfras.</string>
+ <string name="passphrases_do_not_match">Lösenorden stämde inte överens.</string>
+ <string name="passphrase_must_not_be_empty">Var god ange ett lösenord.</string>
<string name="passphrase_for_symmetric_encryption">Symmetrisk kryptering.</string>
- <string name="passphrase_for">Ange lösenordsfras för \'%s\'</string>
+ <string name="passphrase_for">Ange lösenord för \'%s\'</string>
<string name="pin_for">Ange PIN för \'%s\'</string>
<string name="yubikey_pin_for">Ange PIN för att få åtkomst till YubiKey för \'%s\'</string>
- <string name="nfc_text">Håll YubiKey mot baksidan av din enhet.</string>
<string name="file_delete_confirmation_title">Radera orginalfiler?</string>
<string name="file_delete_confirmation">Följande filer kommer raderas:%s</string>
<string name="file_delete_successful">%1$d av %2$d filer har raderats.%3$s</string>
- <string name="no_file_selected">Välj en fil först.</string>
<string name="encrypt_sign_successful">Signerades och/eller krypterades.</string>
<string name="encrypt_sign_clipboard_successful">Signerades och/eller krypterades till urklipp.</string>
<string name="select_encryption_key">Välj åtminstone en krypteringsnyckel.</string>
- <string name="select_encryption_or_signature_key">Välj åtminstone en krypterings- eller signeringsnyckel.</string>
+ <string name="specify_file_to_encrypt_to">Ange vilken fil du vill kryptera till.\nVARNING: Om filen redan finns kommer den att skrivas över!</string>
+ <string name="specify_file_to_decrypt_to">Ange vilken fil du vill kryptera till.\nVARNING: Om filen redan finns kommer den att skrivas över!</string>
<string name="key_deletion_confirmation_multi">Vill du verkligen radera alla markerade nycklar?</string>
<string name="secret_key_deletion_confirmation">Efter radering kommer du inte kunna läsa meddelande krypterade med den här nyckeln samt förlora alla nyckelbekräftningar som gjorts med den!</string>
<string name="public_key_deletetion_confirmation">Radera nyckel \'%s\'?</string>
@@ -225,34 +234,31 @@
<string name="error_external_storage_not_ready">extern lagring inte redo</string>
<string name="error_key_size_minimum512bit">nyckelstorlek måste vara åtminstone 512bit</string>
<string name="error_unknown_algorithm_choice">okänt val av algoritm</string>
- <string name="error_user_id_no_email">ingen e-post hittades</string>
+ <string name="error_user_id_no_email">ingen e-postadress hittades</string>
<string name="error_key_needs_a_user_id">behöver åtminstone en identitet</string>
- <string name="error_no_signature_passphrase">ingen lösenordsfras angiven</string>
+ <string name="error_no_signature_passphrase">inget lösenord angett</string>
<string name="error_no_signature_key">ingen signaturnyckel angiven</string>
<string name="error_invalid_data">Inget giltigt krypterat eller signerat OpenPGP-innehåll!</string>
<string name="error_integrity_check_failed">integritetskontroll misslyckades! Data har modifierats!</string>
- <string name="error_wrong_passphrase">fel lösenordsfras</string>
+ <string name="error_wrong_passphrase">fel lösenord</string>
<string name="error_could_not_extract_private_key">kunde inte extrahera privat nyckel</string>
<!--errors without preceeding Error:-->
<string name="error_jelly_bean_needed">Du behöver Android 4.1 för att använda Androids NFC Beam-funktion!</string>
<string name="error_nfc_needed">NFC måste vara aktiverat!</string>
<string name="error_beam_needed">Beam måste vara aktiverat!</string>
<string name="error_nothing_import">Inga nycklar hittades!</string>
+ <string name="error_nothing_import_selected">Inga nycklar valda för import!</string>
<string name="error_contacts_key_id_missing">Det gick inte att hämta nyckel-ID:t från kontakter!</string>
<string name="error_generic_report_bug">Ett generiskt fel inträffade, skapa en ny buggrapport för OpenKeychain.</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">Inte signerat</string>
<string name="decrypt_result_invalid_signature">Ogiltig signatur!</string>
- <string name="decrypt_result_signature_uncertified">Signerat av (inte certifierad!)</string>
- <string name="decrypt_result_signature_certified">Signerat av</string>
- <string name="decrypt_result_signature_expired_key">Nyckeln har gått ut!</string>
- <string name="decrypt_result_signature_revoked_key">Nyckeln har återkallats!</string>
- <string name="decrypt_result_signature_missing_key">Okänd publik nyckel</string>
+ <string name="decrypt_result_signature_secret">Signerat av din nyckel</string>
+ <string name="decrypt_result_signature_certified">Signerat av bekräftad nyckel</string>
<string name="decrypt_result_encrypted">Krypterat</string>
<string name="decrypt_result_not_encrypted">Inte krypterat</string>
<string name="decrypt_result_action_show">Visa</string>
<string name="decrypt_result_action_Lookup">Sök efter</string>
- <string name="decrypt_invalid_text">Antingen är signaturen ogiltig eller så har nyckeln återkallats/gått ut. Du kan inte vara säker på vem som skrev texten. Vill du fortfarande visa den?</string>
<string name="decrypt_invalid_button">Jag förstår riskerna, visa den!</string>
<!--Add keys-->
<string name="add_keys_my_key">Min nyckel:</string>
@@ -262,6 +268,7 @@
<string name="progress_cancelling">avbryter…</string>
<string name="progress_saving">sparar…</string>
<string name="progress_importing">importerar…</string>
+ <string name="progress_updating">Uppdaterar nycklar...</string>
<string name="progress_exporting">exporterar…</string>
<string name="progress_uploading">laddar upp…</string>
<string name="progress_building_key">bygger nyckel…</string>
@@ -281,11 +288,12 @@
<string name="progress_modify_subkeyrevoke">återkallar undernycklar…</string>
<string name="progress_modify_subkeystrip">rensar undernycklar…</string>
<string name="progress_modify_subkeyadd">lägger till undernycklar…</string>
- <string name="progress_modify_passphrase">ändrar lösenordsfras…</string>
+ <string name="progress_modify_passphrase">byter lösenord...</string>
<plurals name="progress_exporting_key">
<item quantity="one">exporterar nyckel…</item>
<item quantity="other">exporterar nycklar…</item>
</plurals>
+ <string name="progress_start">förbereder operation...</string>
<string name="progress_extracting_signature_key">extraherar signaturnyckel…</string>
<string name="progress_extracting_key">extraherar nyckel…</string>
<string name="progress_preparing_streams">förbereder strömmar…</string>
@@ -305,6 +313,7 @@
<string name="progress_deleting">raderar nycklar…</string>
<string name="progress_con_saving">konsolidera: sparar till cache…</string>
<string name="progress_con_reimport">konsolidera: återimporterar…</string>
+ <string name="progress_verifying_keyserver_url">verifierar nyckelserver...</string>
<!--action strings-->
<string name="hint_cloud_search_hint">Söker via Namn, E-post...</string>
<!--key bit length selections-->
@@ -346,11 +355,11 @@
<string name="import_tab_qr_code">QR-kod/NFC</string>
<string name="import_import">Importera markerade nycklar</string>
<string name="import_qr_code_wrong">Något är fel med QR-koden! Försök igen!</string>
- <string name="import_qr_code_too_short_fingerprint">Fingeravtryck är för kort (&lt; 16 tecken)</string>
+ <string name="import_qr_code_too_short_fingerprint">Fingeravtrycket är för kort!</string>
<string name="import_qr_code_button">Skanna QR-kod</string>
<string name="import_qr_code_text">Håll din kamera över QR-koden!</string>
+ <!--Import from URL-->
<!--Generic result toast-->
- <string name="view_log">Detaljer</string>
<string name="with_warnings">, med varningar</string>
<string name="with_cancelled">, tills det avbryts</string>
<!--Import result toast-->
@@ -399,6 +408,7 @@
</plurals>
<string name="delete_nothing">Inget att radera.</string>
<string name="delete_cancelled">Raderingsoperation avbruten.</string>
+ <!--Revoke result toast (snackbar)-->
<!--Certify result toast-->
<plurals name="certify_keys_ok">
<item quantity="one">Certifierade nyckel%2$s.</item>
@@ -418,10 +428,10 @@
<string name="intent_send_encrypt">Kryptera med OpenKeychain</string>
<string name="intent_send_decrypt">Dekryptera med OpenKeychain</string>
<!--Remote API-->
- <string name="api_settings_show_info">Visa avancerad information</string>
- <string name="api_settings_hide_info">Göm avancerad information</string>
- <string name="api_settings_show_advanced">Visa avancerade inställningar</string>
- <string name="api_settings_hide_advanced">Göm avancerade inställningar</string>
+ <string name="api_settings_show_info">Visa utökad information</string>
+ <string name="api_settings_hide_info">Dölj utökad information</string>
+ <string name="api_settings_show_advanced">Visa utökade inställningar</string>
+ <string name="api_settings_hide_advanced">Dölj utökade inställningar</string>
<string name="api_settings_no_key">Ingen nyckel vald</string>
<string name="api_settings_select_key">Välj nyckel</string>
<string name="api_settings_create_key">Skapa ny nyckel</string>
@@ -432,9 +442,8 @@
<string name="api_settings_start">Starta app</string>
<string name="api_settings_delete_account">Radera konto</string>
<string name="api_settings_package_name">Paketnamn</string>
- <string name="api_settings_package_signature">SHA-256 för paketsignatur</string>
- <string name="api_settings_accounts">Konton (övergivet API)</string>
- <string name="api_settings_advanced">Avancerad information</string>
+ <string name="api_settings_accounts">Konton (gamla API:t)</string>
+ <string name="api_settings_advanced">Utökad information</string>
<string name="api_settings_allowed_keys">Tillåtna nycklar</string>
<string name="api_settings_settings">Inställningar</string>
<string name="api_settings_key">Kontonyckel:</string>
@@ -445,15 +454,17 @@
<string name="api_register_allow">Tillåt åtkomst</string>
<string name="api_register_disallow">Tillåt inte åtkomst</string>
<string name="api_register_error_select_key">Välj en nyckel!</string>
- <string name="api_select_pub_keys_missing_text">Inga nycklar hittades för dessa identiteter:</string>
- <string name="api_select_pub_keys_dublicates_text">Mer än en nyckel finns för dessa identiteter:</string>
<string name="api_select_pub_keys_text">Se över listan med mottagare!</string>
<string name="api_select_pub_keys_text_no_user_ids">Välj mottagare!</string>
<string name="api_error_wrong_signature">Signaturkontroll misslyckades! Har du installerat appen från en annan källa? Om du är säker på att det här inte är en attack, återkalla den här appens registrering i OpenKeychain och registrera sen om appen igen.</string>
<string name="api_select_sign_key_text">Välj en av dina existerande nycklar eller skapa en ny.</string>
+ <string name="api_select_keys_text">Ingen av de tillåtna nycklarna lyckas dekryptera innehållet. Var god välj de rätta nycklarna.</string>
<!--Share-->
<string name="share_qr_code_dialog_title">Dela med QR-kod</string>
<string name="share_nfc_dialog">Dela med NFC</string>
+ <!--retry upload dialog-->
+ <!--Delete or revoke private key dialog-->
+ <!--Delete Or Revoke Dialog spinner-->
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">1 nyckel vald.</item>
@@ -493,17 +504,23 @@
<string name="key_trust_old_keys">Det kan vara okej att använda den här för att avkryptera ett gammalt medelande från tiden när den här nyckeln var giltig.</string>
<string name="key_trust_no_cloud_evidence">Inget bevis från molnet angående den här nyckelns trovärdighet.</string>
<string name="key_trust_start_cloud_search">Påbörja sökning</string>
+ <string name="key_trust_results_prefix">Keybase.io erbjuder \"bevis\" som hävdar att ägaren av den här nyckeln:</string>
+ <string name="key_trust_header_text">Observera: Keybase.io bevis är en experimentell funktion i OpenKeychain. Vi uppmanar dig att skanna QR-koder eller utbyta nycklar via NFC utöver att bekräfta dem.</string>
<!--keybase proof stuff-->
- <string name="keybase_narrative_twitter">Postar på Twitter som</string>
- <string name="keybase_narrative_github">Är känd på GitHub som</string>
- <string name="keybase_narrative_dns">Kontrollerar domännamne(t|s)</string>
- <string name="keybase_narrative_reddit">Postar på Reddit som</string>
- <string name="keybase_narrative_coinbase">Är känd på Coinbase som</string>
- <string name="keybase_narrative_hackernews">Postar på Hacker News som</string>
- <string name="keybase_narrative_unknown">Okänd bevistyp</string>
<string name="keybase_proof_failure">Tyvärr kan detta bevis inte verifieras.</string>
+ <string name="keybase_problem_fetching_evidence">Problem med bevis</string>
+ <string name="keybase_dns_query_failure">Hämtning av DNS TXT-post misslyckades</string>
+ <string name="keybase_message_fetching_data">Hämtar bevis</string>
+ <string name="keybase_proof_succeeded">Detta bevis har verifierats!</string>
+ <string name="keybase_for_the_domain">för domänen</string>
+ <string name="keybase_contained_signature">innehåller ett meddelande som bara kan ha skapats av ägaren av den här nyckeln.</string>
+ <string name="keybase_twitter_proof">En tweet</string>
+ <string name="keybase_dns_proof">En DNS TXT-post</string>
+ <string name="keybase_web_site_proof">En textfil</string>
+ <string name="keybase_reddit_proof">En JSON-fil</string>
+ <string name="keybase_verify">Verifiera</string>
<!--Edit key-->
- <string name="edit_key_action_change_passphrase">Ändra lösenordsfras</string>
+ <string name="edit_key_action_change_passphrase">Byt lösenord</string>
<string name="edit_key_action_add_identity">Lägg till identitet</string>
<string name="edit_key_action_add_subkey">Lägg till undernyckel</string>
<string name="edit_key_edit_user_id_title">Välj en åtgärd!</string>
@@ -520,18 +537,41 @@
<string name="edit_key_select_flag">Välj åtminstone en flagga!</string>
<string name="edit_key_error_add_identity">Lägg till åtminstone en identitet!</string>
<string name="edit_key_error_add_subkey">Lägg till åtminstone en undernyckel!</string>
+ <string name="edit_key_error_bad_nfc_algo">Algoritmen stöds inte av smartcard!</string>
+ <string name="edit_key_error_bad_nfc_size">Nyckelstorleken stöds inte av smartcard!</string>
<!--Create key-->
+ <string name="create_key_upload">Synkronisera med molnet</string>
<string name="create_key_empty">Detta fält krävs</string>
- <string name="create_key_passphrases_not_equal">Lösenordsfraser stämmer inte överens</string>
+ <string name="create_key_passphrases_not_equal">Lösenorden stämmer inte överens</string>
<string name="create_key_final_text">Du angav följande identitet:</string>
<string name="create_key_final_robot_text">Att skapa en nyckel kan ta ett tag, drick en kopp kaffe under tiden…</string>
<string name="create_key_rsa">(3 undernycklar, RSA, 4096 bit)</string>
<string name="create_key_custom">(anpassad nyckelkonfiguration)</string>
+ <string name="create_key_name_text">Välj ett namn som hör till den här nyckeln. Det kan vara ett fullständigt namn, t.ex. \"John Doe\", eller ett smeknamn, t.ex. \"Johnny\".</string>
+ <string name="create_key_email_text">Ange din huvudsakliga e-postadress som används för säker kommunikation.</string>
+ <string name="create_key_passphrase_text">Välj ett starkt lösenord. Det skyddar din nyckel när din enhet tappas bort eller blir stulen.</string>
+ <string name="create_key_hint_full_name">Fullständigt namn eller smeknamn</string>
<string name="create_key_edit">Ändra nyckelkonfiguration</string>
+ <string name="create_key_add_email">Lägg till e-postadress</string>
+ <string name="create_key_email_already_exists_text">E-postadress har redan lagts till</string>
+ <string name="create_key_email_invalid_email">Formatet på e-postadressen är ogiltigt</string>
<!--View key-->
+ <string name="view_key_revoked">Återkallad: Nyckeln bör inte användas längre!</string>
+ <string name="view_key_expired">Utgånget: Kontakten behöver utöka nyckelns giltighetstid!</string>
+ <string name="view_key_expired_secret">Utgånget: Du kan förlänga nyckelns giltighetstid genom att editera den!</string>
+ <string name="view_key_my_key">Min nyckel</string>
+ <string name="view_key_verified">Bekräftad nyckel</string>
+ <string name="view_key_unverified">Obekräftad: Skanna QR-kod för att bekräfta nyckeln!</string>
+ <string name="view_key_fragment_no_system_contact">&lt;ingen&gt;</string>
+ <!--Add/Edit keyserver-->
+ <string name="add_keyserver_dialog_title">Lägg till nyckelserver</string>
+ <string name="add_keyserver_verified">Nyckelserver verifierad!</string>
+ <string name="add_keyserver_without_verification">Nyckelserver tillagd utan verifiering.</string>
+ <string name="add_keyserver_invalid_url">Ogiltig URL!</string>
+ <string name="add_keyserver_connection_failed">Misslyckades med att ansluta till nyckelserver. Kontrollera URL:en och din internetanslutning.</string>
<!--Navigation Drawer-->
<string name="nav_keys">Nycklar</string>
- <string name="nav_encrypt_decrypt">Kryptera/Dekryptera</string>
+ <string name="nav_encrypt_decrypt">Kryptera/dekryptera</string>
<string name="nav_apps">Appar</string>
<string name="my_keys">Mina nycklar</string>
<!--hints-->
@@ -701,9 +741,6 @@
<string name="msg_mf_error_noexist_primary">Dåligt primärt användar-ID angivet!</string>
<string name="msg_mf_error_revoked_primary">Återkallade användar-ID:n kan inte vara primära!</string>
<string name="msg_mf_error_pgp">Internt OpenPGP-fel!</string>
- <string name="msg_mf_passphrase">Ändrar lösenordsfras för nyckelring</string>
- <string name="msg_mf_passphrase_key">Krypterar undernyckel %s på nytt med ny lösenordsfras</string>
- <string name="msg_mf_passphrase_empty_retry">Det gick inte att ställa in ny lösenordsfras, försök igen med en tom gammal lösenordsfras</string>
<string name="msg_mf_primary_replace_old">Ersätter certifikat för tidigare primärt användar-ID</string>
<string name="msg_mf_primary_new">Genererar nytt certifikat för nytt primärt användar-ID</string>
<string name="msg_mf_subkey_change">Modifierar undernyckel %s</string>
@@ -741,14 +778,11 @@
<string name="msg_con_reimport_secret_skip">Inga privata nycklar att återimportera, hoppar över…</string>
<!--Edit Key (higher level than modify)-->
<string name="msg_ed">Utför nyckeloperation</string>
- <string name="msg_ed_caching_new">Cachar ny lösenordsfras</string>
<string name="msg_ed_error_key_not_found">Nyckel hittades inte!</string>
<string name="msg_ed_success">Nyckeloperation lyckades</string>
<!--Promote key-->
- <string name="msg_pr_error_already_secret">Nyckeln är redan en privat nyckel!</string>
<string name="msg_pr_error_key_not_found">Nyckel hittades inte!</string>
<!--Other messages used in OperationLogs-->
- <string name="msg_ek_error_divert">Det finns (ännu) inte stöd för att redigera NFC-nycklar!</string>
<string name="msg_ek_error_dummy">Kan inte redigera nyckelring med en rensad huvudnyckel!</string>
<string name="msg_ek_error_not_found">Nyckel hittades inte!</string>
<!--Messages for DecryptVerify operation-->
@@ -766,7 +800,6 @@
<string name="msg_dc_error_extract_key">Okänt fel vid upplåsning av nyckel</string>
<string name="msg_dc_error_integrity_check">Fel vid integritetskontroll!</string>
<string name="msg_dc_ok_meta_only">Endast metadata krävdes, hoppar över dekryptering</string>
- <string name="msg_dc_pass_cached">Använder lösenordsfras från cache</string>
<string name="msg_dc_prep_streams">Förbereder strömmar för dekryptering</string>
<string name="msg_dc">Startar dekrypteringsoperation…</string>
<string name="msg_dc_sym_skip">Symmetrisk data inte tillåten, hoppar över…</string>
@@ -779,9 +812,7 @@
<string name="msg_pse_asymmetric">Förbereder publika nycklar för kryptering</string>
<string name="msg_pse_compressing">Förbereder komprimering</string>
<string name="msg_pse_encrypting">Krypterar data</string>
- <string name="msg_pse_error_bad_passphrase">Dålig lösenordsfras!</string>
<string name="msg_pse_error_nfc">NFC datafel!</string>
- <string name="msg_pse_error_no_passphrase">Ingen lösenordsfras angiven!</string>
<string name="msg_pse_error_pgp">Internt OpenPGP-fel!</string>
<string name="msg_pse_signing_cleartext">Skapar signatur i klartext</string>
<string name="msg_pse_sigcrypting">Krypterar data med signatur</string>
@@ -841,15 +872,15 @@
<item quantity="one">en del av den inlästa filen är ett giltigt OpenPGP-objekt men inte en OpenPGP-nyckel</item>
<item quantity="other">delar av den inlästa filen är giltiga OpenPGP-objekt men inte OpenPGP-nycklar</item>
</plurals>
+ <!--Messages for Keybase Verification operation-->
<!--Messages for Export Log operation-->
<!--PassphraseCache-->
- <string name="passp_cache_notif_click_to_clear">Klicka för att rensa cachade lösenordsfraser</string>
- <string name="passp_cache_notif_n_keys">OpenKeychain har cachat %d lösenordsfraser</string>
- <string name="passp_cache_notif_keys">Cachade lösenordsfraser:</string>
- <string name="passp_cache_notif_clear">Rensa cache</string>
- <string name="passp_cache_notif_pwd">Lösenordsfras</string>
+ <!--Keyserver sync-->
<!--First Time-->
<string name="first_time_text1">Ta tillbaka din integritet med OpenKeychain!</string>
+ <string name="first_time_create_key">Skapa min nyckel</string>
+ <string name="first_time_import_key">Importera nyckel från fil</string>
+ <string name="first_time_yubikey">Använd YubiKey NEO</string>
<string name="first_time_skip">Hoppa över inställning</string>
<!--unsorted-->
<string name="section_cert">Certifikatinformation</string>
@@ -862,35 +893,25 @@
<string name="error_key_not_found">Nyckel hittades inte!</string>
<string name="error_key_processing">Fel vid bearbetning av nyckel!</string>
<string name="key_stripped">rensad</string>
- <string name="key_no_passphrase">ingen lösenordsfras</string>
<string name="key_unavailable">otillgänglig</string>
<string name="secret_cannot_multiple">Dina egna nycklar kan bara raderas var för sig!</string>
<string name="title_view_cert">Visa certifikatinformation</string>
<string name="unknown_algorithm">okänd</string>
<string name="can_sign_not">kan inte signera</string>
<string name="error_no_encrypt_subkey">Ingen krypteringsundernyckel tillgänglig!</string>
- <string name="info_no_manual_account_creation">Skapa inte OpenKeychain-konton manuellt. \nFör mer information, se Hjälp.</string>
<string name="contact_show_key">Visa nyckel (%s)</string>
<string name="swipe_to_update">Dra nedåt för att uppdatera från nyckelserver</string>
<string name="error_no_file_selected">Välj åtminstone en fil att kryptera!</string>
- <string name="error_multi_not_supported">Att spara flera filer stöds ej. Detta är en begränsning i nuvarande Android.</string>
<string name="key_colon">Nyckel:</string>
<string name="user_id_none"><![CDATA[<none>]]></string>
+ <!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
- <string name="enter_passphrase">Ange lösenordsfras</string>
- <string name="passphrase">Lösenordsfras</string>
- <string name="noPassphrase">Ingen lösenordsfras</string>
- <string name="passphrases_match">Lösenordsfraser stämmer överens</string>
- <string name="passphrase_saved">Lösenordsfras sparad</string>
- <string name="passphrase_invalid">Lösenordsfras ogiltig</string>
- <string name="missing_passphrase">Lösenordsfras saknas</string>
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<string name="passphrase_again">Igen</string>
<string name="lockpattern">Låsmönster</string>
<string name="lockpatternNFC">NFC + Låsmönster</string>
<string name="unlock_method">Upplåsningmetod</string>
- <string name="set_passphrase">Sätt lösenordsfras</string>
<string name="draw_lockpattern">Dra låsmönster</string>
<string name="nfc_title">NFC</string>
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
diff --git a/OpenKeychain/src/main/res/values-tr/strings.xml b/OpenKeychain/src/main/res/values-tr/strings.xml
index 12115e93b..090356f9d 100644
--- a/OpenKeychain/src/main/res/values-tr/strings.xml
+++ b/OpenKeychain/src/main/res/values-tr/strings.xml
@@ -8,27 +8,22 @@
<string name="title_add_subkey">Alt anahtar ekle</string>
<string name="title_edit_key">Anahtarı düzenle</string>
<string name="title_api_registered_apps">Uygulamalar</string>
- <string name="title_change_passphrase">Parolayı Değiştir</string>
<string name="title_share_fingerprint_with">Parmak izini paylaş...</string>
<string name="title_share_key">Anahtarı paylaş...</string>
<string name="title_share_file">Dosyayı paylaş...</string>
<string name="title_encrypt_to_file">Dosyaya Şifrele</string>
<string name="title_decrypt_to_file">Dosyaya Çözümle</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_send_key">Anahtar Sunucusuna Yükle</string>
<string name="title_key_details">Anahtar Detayları</string>
<string name="title_help">Yardım</string>
<string name="title_log_display">Günlük</string>
<string name="title_exchange_keys">Anahtarları Değiş Tokuş Et</string>
- <string name="title_advanced_key_info">Gelişmiş Anahtar Bilgisi</string>
<!--section-->
<string name="section_user_ids">Kimlikler</string>
<string name="section_keys">Alt anahtarlar</string>
<string name="section_cloud_search">Bulut araması</string>
- <string name="section_passphrase_cache">Parola Önbelleği</string>
<string name="section_actions">Eylemler</string>
<string name="section_share_key">Anahtar</string>
<string name="section_key_server">Anahtar Sunucusu</string>
@@ -36,7 +31,6 @@
<!--button-->
<string name="btn_decrypt_verify_file">Şifreyi çöz, doğrula ve dosyayı kaydet</string>
<string name="btn_encrypt_share_file">Şifrele ve dosyayı paylaş</string>
- <string name="btn_save">Kaydet</string>
<string name="btn_do_not_save">İptal</string>
<string name="btn_delete">Sil</string>
<string name="btn_no_date">Zaman Aşımı Yok</string>
@@ -47,28 +41,20 @@
<string name="btn_view_cert_key">Tasdikleme anahtarını görüntüle</string>
<string name="btn_create_key">Anahtar oluştur</string>
<string name="btn_add_files">Dosya(lar) ekle</string>
- <string name="btn_add_share_decrypted_text">Şifresi çözülmüş metni paylaş</string>
- <string name="btn_decrypt_and_verify">ve imzaları doğrula</string>
- <string name="btn_decrypt_files">Dosyaların şifresini çöz</string>
<!--menu-->
<string name="menu_preferences">Ayarlar</string>
<string name="menu_help">Yardım</string>
- <string name="menu_export_key">Dosyaya ver</string>
<string name="menu_delete_key">Anahtar sil</string>
<string name="menu_search">Ara</string>
<string name="menu_beam_preferences">NFC ayarları</string>
<string name="menu_encrypt_to">Şuna şifrele...</string>
<string name="menu_select_all">Hepsini seç</string>
<string name="menu_export_all_keys">Tüm anahtarları ver</string>
- <string name="menu_advanced">Gelişmiş bilgiyi göster</string>
<!--label-->
<string name="label_file">Dosya</string>
<string name="label_files">Dosya(lar)</string>
<string name="label_file_colon">Dosya:</string>
- <string name="label_no_passphrase">Parola Yok</string>
- <string name="label_passphrase">Parola</string>
<string name="label_unlock">Kilit açılıyor...</string>
- <string name="label_passphrase_again">Parolayı Tekrarla</string>
<string name="label_algorithm">Algoritma</string>
<string name="label_ascii_armor">ASCII formatında dosyalar</string>
<string name="label_file_ascii_armor">ASCII formatında çıktıları etkinleştir</string>
@@ -77,16 +63,11 @@
<string name="label_use_default_yubikey_pin">Varsayılan YubiKey PIN\'ini kullan</string>
<string name="label_use_num_keypad_for_yubikey_pin">YubiKey PIN\'i için sayısal klavyeyi kullan</string>
<string name="label_label_use_default_yubikey_pin_summary">NFC üzerinden YubiKey\'e ulaşmak için varsayılan PIN\'i (123456) kullanır</string>
- <string name="label_asymmetric_from">İmzalayan:</string>
<string name="label_to">Şuna şifrele:</string>
<string name="label_delete_after_decryption">Şifre çözme sonrasında sil</string>
<string name="label_encryption_algorithm">Şifreleme algoritması</string>
<string name="label_hash_algorithm">Özet algoritması</string>
- <string name="label_symmetric">Parolayla şifrele</string>
- <string name="label_passphrase_cache_ttl">Önbellek zamanı</string>
- <string name="label_passphrase_cache_subs">Parolaları altanahtarlara göre önbellekle</string>
<string name="label_file_compression">Dosya sıkıştırma</string>
- <string name="label_keyservers">Anahtar Sunucuları</string>
<string name="label_key_id">Anahtar ID</string>
<string name="label_creation">Oluşturma</string>
<string name="label_expiry">Bitiş</string>
@@ -100,8 +81,13 @@
<string name="label_send_key">Bulut ile eşitle</string>
<string name="label_fingerprint">Parmak izi</string>
<string name="expiry_date_dialog_title">Zaman aşımı tarihini ayarla</string>
- <string name="label_first_keyserver_is_used">(Listelenen ilk anahtar sunucu tercih edilecektir)</string>
<string name="label_preferred">tercih edilen</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <!--proxy type choices and values-->
+ <!--OrbotHelper strings-->
+ <!--InstallDialogFragment strings-->
+ <!--StartOrbotDialogFragment strings-->
<string name="user_id_no_name">&lt;isimsiz&gt;</string>
<string name="none">&lt;hiçbiri&gt;</string>
<plurals name="n_keys">
@@ -141,18 +127,11 @@
<string name="flag_encrypt">Şifreleme</string>
<string name="flag_authenticate">Kimlik kanıtlama</string>
<!--sentences-->
- <string name="wrong_passphrase">Yanlış parola.</string>
<string name="no_filemanager_installed">Uyumlu dosya yöneticisi yüklenmedi.</string>
- <string name="passphrases_do_not_match">Parolalar eşleşmedi.</string>
- <string name="passphrase_must_not_be_empty">Lütfen bir parola girin.</string>
<string name="passphrase_for_symmetric_encryption">Simetrik şifreleme.</string>
- <string name="passphrase_for">\'%s\' için bir parola girin</string>
- <string name="nfc_text">YubiKey\'inizi cihazınızın arkasında tutun.</string>
- <string name="no_file_selected">Önce bir dosya seçin.</string>
<string name="encrypt_sign_successful">Başarıyla imzalandı ve/veya şifrelendi.</string>
<string name="encrypt_sign_clipboard_successful">Kopyalama önbelleğine başarıyla imzalandı ve/veya şifrelendi.</string>
<string name="select_encryption_key">En az bir şifreleme anahtarı seçiniz.</string>
- <string name="select_encryption_or_signature_key">En az bir şifreleme anahtarı veya imza anahtarı seçiniz.</string>
<string name="also_export_secret_keys">Özel anahtarları da dışa aktar</string>
<string name="reinstall_openkeychain">Android için bilinen bir hataya denk geldiniz. Eğer kişilerinizi anahtarlarla eşlemek istiyorsanız, lütfen OpenKeychain uygulamasını yeniden yükleyin.</string>
<string name="key_exported">1 anahtar başarıyla dışa aktarıldı.</string>
@@ -178,13 +157,10 @@
<string name="error_external_storage_not_ready">harici depolama hazır değil</string>
<string name="error_key_size_minimum512bit">anahtar boyutu en az 512bit olmalı</string>
<string name="error_unknown_algorithm_choice">bilinmeyen algoritma seçimi</string>
- <string name="error_user_id_no_email">herhangi bir eposta bulunamadı</string>
<string name="error_key_needs_a_user_id">en az bir kimlik gerekli</string>
- <string name="error_no_signature_passphrase">parola verilmedi</string>
<string name="error_no_signature_key">imza anahtarı verilmedi</string>
<string name="error_invalid_data">Geçerli olan şifrelenmiş ya da imzalanmış OpenPGP içeriği yok!</string>
<string name="error_integrity_check_failed">Bütünlük kontrolü başarısız! Veri değiştirilmiş!</string>
- <string name="error_wrong_passphrase">yanlış parola</string>
<string name="error_could_not_extract_private_key">özel anahtar çıkarılamadı</string>
<!--errors without preceeding Error:-->
<string name="error_jelly_bean_needed">Android\'in NFC Beam özelliğini kullanabilmek için Android 4.1 kullanmalısınız!</string>
@@ -194,16 +170,10 @@
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">İmzalanmadı</string>
<string name="decrypt_result_invalid_signature">Geçersiz imza!</string>
- <string name="decrypt_result_signature_uncertified">İmzalayan (tasdik edilmemiş!):</string>
- <string name="decrypt_result_signature_certified">İmzalayan:</string>
- <string name="decrypt_result_signature_expired_key">Anahtar zaman aşımına uğramış!</string>
- <string name="decrypt_result_signature_revoked_key">Anahtar yürürlükten kaldırılmış!</string>
- <string name="decrypt_result_signature_missing_key">Bilinmeyen açık anahtar</string>
<string name="decrypt_result_encrypted">Şifrelendi</string>
<string name="decrypt_result_not_encrypted">Şifrelenmedi</string>
<string name="decrypt_result_action_show">Göster</string>
<string name="decrypt_result_action_Lookup">Araştır</string>
- <string name="decrypt_invalid_text">Ya imza geçersiz ya da anahtar yürürlükten kaldırılmış/zaman aşımına uğramış. Metni kimin yazdığından emin olamazsınız. Yine de görüntülemek istiyor musunuz?</string>
<string name="decrypt_invalid_button">Tehlikelerin farkındayım, görüntüle!</string>
<!--Add keys-->
<string name="add_keys_my_key">Anahtarım:</string>
@@ -231,7 +201,6 @@
<string name="progress_modify_subkeyrevoke">altanahtarlar yürürlükten kaldırılıyor...</string>
<string name="progress_modify_subkeystrip">altanahtarlar çıkarılıyor...</string>
<string name="progress_modify_subkeyadd">altanahtarlar ekleniyor...</string>
- <string name="progress_modify_passphrase">parola değiştiriliyor...</string>
<plurals name="progress_exporting_key">
<item quantity="one">anahtar dışa aktarılıyor...</item>
<item quantity="other">anahtarlar dışa aktarılıyor...</item>
@@ -294,8 +263,8 @@
<string name="import_tab_qr_code">QR Kodu/NFC</string>
<string name="import_import">Seçili anahtarları içe aktar</string>
<string name="import_qr_code_wrong">QR Kodu hatalı! Lütfen tekrar deneyin!</string>
- <string name="import_qr_code_too_short_fingerprint">Parmak izi çok kısa (&lt; 16 karakter)</string>
<string name="import_qr_code_button">QR Kodu Tara</string>
+ <!--Import from URL-->
<!--Generic result toast-->
<string name="with_warnings">, uyarılarla</string>
<string name="with_cancelled">, iptal edilene kadar</string>
@@ -313,6 +282,7 @@
<!--Delete result toast-->
<string name="delete_nothing">Silinecek bir şey yok.</string>
<string name="delete_cancelled">Silme işlemi iptal edildi.</string>
+ <!--Revoke result toast (snackbar)-->
<!--Certify result toast-->
<!--Intent labels-->
<string name="intent_decrypt_file">OpenKeychain ile Dosya Çözümle</string>
@@ -320,10 +290,6 @@
<string name="intent_send_encrypt">OpenKeychain ile Şifrele</string>
<string name="intent_send_decrypt">OpenKeychain ile Çözümle</string>
<!--Remote API-->
- <string name="api_settings_show_info">Gelişmiş bilgiyi göster</string>
- <string name="api_settings_hide_info">Gelişmiş bilgiyi gizle</string>
- <string name="api_settings_show_advanced">Gelişmiş ayarları göster</string>
- <string name="api_settings_hide_advanced">Gelişmiş ayarları gizle</string>
<string name="api_settings_no_key">Anahtar seçilmedi</string>
<string name="api_settings_select_key">Anahtar seç</string>
<string name="api_settings_save">Kaydet</string>
@@ -337,13 +303,14 @@
<string name="api_register_allow">Erişime izin ver</string>
<string name="api_register_disallow">Erişime izin verme</string>
<string name="api_register_error_select_key">Lütfen bir anahtar seçin!</string>
- <string name="api_select_pub_keys_missing_text">Bu kimlikler için anahtar bulunamadı:</string>
- <string name="api_select_pub_keys_dublicates_text">Bu kimlikler için birden fazla anahtar var:</string>
<string name="api_select_pub_keys_text">Lütfen alıcıların listesini gözden geçirin!</string>
<string name="api_select_pub_keys_text_no_user_ids">Lütfen alıcıları seçin!</string>
<!--Share-->
<string name="share_qr_code_dialog_title">QR Kod ile Paylaş</string>
<string name="share_nfc_dialog">NFC ile Paylaş</string>
+ <!--retry upload dialog-->
+ <!--Delete or revoke private key dialog-->
+ <!--Delete Or Revoke Dialog spinner-->
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">1 anahtar seçildi.</item>
@@ -374,7 +341,6 @@
<!--Key trust-->
<!--keybase proof stuff-->
<!--Edit key-->
- <string name="edit_key_action_change_passphrase">Parolayı Değiştir</string>
<string name="edit_key_action_add_identity">Kimlik Ekle</string>
<string name="edit_key_action_add_subkey">Alt anahtar Ekle</string>
<string name="edit_key_edit_user_id_title">Bir eylem seç!</string>
@@ -389,13 +355,13 @@
<string name="edit_key_error_add_subkey">En az bir alt anahtar ekleyin!</string>
<!--Create key-->
<string name="create_key_empty">Bu alan zorunludur</string>
- <string name="create_key_passphrases_not_equal">Parolalar eşleşmedi</string>
<string name="create_key_final_text">Şu kimliği girdiniz:</string>
<string name="create_key_final_robot_text">Anahtar oluşturma biraz zaman alabilir, bu sırada bir çay için...</string>
<string name="create_key_rsa">(3 alt anahtar, RSA, 4096 bit)</string>
<string name="create_key_custom">(özel anahtar yapılandırması)</string>
<string name="create_key_edit">Anahtar yapılandırmasını değiştir.</string>
<!--View key-->
+ <!--Add/Edit keyserver-->
<!--Navigation Drawer-->
<string name="nav_keys">Anahtarlar</string>
<string name="nav_apps">Uygulamalar</string>
@@ -451,10 +417,10 @@
<string name="msg_del_error_empty">Silinecek bir şey yok!</string>
<string name="msg_acc_saved">Hesap kaydedildi</string>
<string name="msg_download_success">Başarıyla indirildi!</string>
+ <!--Messages for Keybase Verification operation-->
<!--Messages for Export Log operation-->
<!--PassphraseCache-->
- <string name="passp_cache_notif_clear">Önbelleği Temizle</string>
- <string name="passp_cache_notif_pwd">Parola</string>
+ <!--Keyserver sync-->
<!--First Time-->
<string name="first_time_text1">Gizliliğinizi OpenKeychain ile geri alın!</string>
<string name="first_time_skip">Kurulumu Atla</string>
@@ -468,23 +434,20 @@
<string name="error_key_not_found">Anahtar bulunamadı!</string>
<string name="error_key_processing">Anahtar işlenirken hata!</string>
<string name="key_stripped">çıkartıldı</string>
- <string name="key_divert">AkıllıKart/NFC için yönlendir</string>
- <string name="key_no_passphrase">parola yok</string>
<string name="key_unavailable">mevcut değil</string>
<string name="secret_cannot_multiple">Kendi anahtarlarınız yalnızca teker teker silinebilir!</string>
<string name="title_view_cert">Sertifika Ayrıntılarını Görüntüle</string>
<string name="unknown_algorithm">bilinmeyen</string>
<string name="can_sign_not">imzalanamadı</string>
<string name="error_no_encrypt_subkey">Şifreleme için kullanılabilecek altanahtar mevcut değil!</string>
- <string name="info_no_manual_account_creation">OpenKeychain-hesaplarını kendiniz oluşturmayın.\nDaha fazla bilgi için Yardım bölümüne bakın.</string>
<string name="contact_show_key">Anahtarı göster (%s)</string>
<string name="swipe_to_update">Anahtar sunucudan güncelleme almak için parmağınızı aşağıya doğru kaydırın</string>
<string name="error_no_file_selected">Şifrelemek için en az bir dosya seçin!</string>
- <string name="error_multi_not_supported">Birden çok dosyanın kaydedilmesi desteklenmiyor. Bu şu anki Android\'in bir kısıtlamasıdır.</string>
<string name="key_colon">Anahtar:</string>
<string name="exchange_description">Anahtar değiş tokuşu başlatmak için sağ taraftan katılımcıların sayısını seçin ve \"Değiş tokuşu başlat\" tuşuna tıklayın.\n\nSadece istenilen katılımcıların değişim işleminde olduğundan ve parmak izlerinin doğruluğundan emin olmak için size iki soru daha sorulacak.</string>
+ <!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
</resources>
diff --git a/OpenKeychain/src/main/res/values-uk/strings.xml b/OpenKeychain/src/main/res/values-uk/strings.xml
index 58272c597..6c0420c0b 100644
--- a/OpenKeychain/src/main/res/values-uk/strings.xml
+++ b/OpenKeychain/src/main/res/values-uk/strings.xml
@@ -8,27 +8,22 @@
<string name="title_add_subkey">Додати підключ</string>
<string name="title_edit_key">Редагувати ключ</string>
<string name="title_api_registered_apps">Програми</string>
- <string name="title_change_passphrase">Змінити парольну фразу</string>
<string name="title_share_fingerprint_with">Поділитися відбитком із…</string>
<string name="title_share_key">Поділитися ключем з…</string>
<string name="title_share_file">Поширити файл з…</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_send_key">Завантажити на сервер ключів</string>
<string name="title_key_details">Подробиці про ключ</string>
<string name="title_help">Довідка</string>
<string name="title_log_display">Журнал</string>
<string name="title_exchange_keys">Обміняти ключі</string>
- <string name="title_advanced_key_info">Додаткова інформація про ключ</string>
<!--section-->
<string name="section_user_ids">Сутності</string>
<string name="section_keys">Підключі</string>
<string name="section_cloud_search">Хмарний пошук</string>
- <string name="section_passphrase_cache">Кеш парольної фрази</string>
<string name="section_actions">Дії</string>
<string name="section_share_key">Ключ</string>
<string name="section_key_server">Сервер ключів</string>
@@ -36,7 +31,6 @@
<!--button-->
<string name="btn_decrypt_verify_file">Розшифрувати, перевірити та зберегти файл</string>
<string name="btn_encrypt_share_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>
@@ -47,28 +41,20 @@
<string name="btn_view_cert_key">Переглянути ключ сертифікації</string>
<string name="btn_create_key">Створити ключ</string>
<string name="btn_add_files">Додати файл(и)</string>
- <string name="btn_add_share_decrypted_text">Поширити розшифрований текст</string>
- <string name="btn_decrypt_and_verify">і перевірити підписи</string>
- <string name="btn_decrypt_files">Розшифрувати файли</string>
<!--menu-->
<string name="menu_preferences">Параметри</string>
<string name="menu_help">Довідка</string>
- <string name="menu_export_key">Експорт до файлу</string>
<string name="menu_delete_key">Вилучити ключ</string>
<string name="menu_search">Пошук</string>
<string name="menu_beam_preferences">Налаштування променя</string>
<string name="menu_encrypt_to">Зашифрувати…</string>
<string name="menu_select_all">Вибрати усе</string>
<string name="menu_export_all_keys">Експортувати усі ключі</string>
- <string name="menu_advanced">Показати додаткову інформацію</string>
<!--label-->
<string name="label_file">Файл</string>
<string name="label_files">Файл(и)</string>
<string name="label_file_colon">Файл:</string>
- <string name="label_no_passphrase">Без парольної фрази</string>
- <string name="label_passphrase">Парольна фраза</string>
<string name="label_unlock">Розблоковується…</string>
- <string name="label_passphrase_again">Повторити пароль</string>
<string name="label_algorithm">Алгоритм</string>
<string name="label_ascii_armor">Файл ASCII Armor</string>
<string name="label_file_ascii_armor">Увімкнути ASCII Armor</string>
@@ -77,16 +63,11 @@
<string name="label_use_default_yubikey_pin">Вживати типовий YubiKey PIN</string>
<string name="label_use_num_keypad_for_yubikey_pin">Вживати цифрову клавіатуру для YubiKey PIN</string>
<string name="label_label_use_default_yubikey_pin_summary">Вживається типовий PIN (123456) для доступу до YubiKey чреез NFC</string>
- <string name="label_asymmetric_from">Підписано:</string>
<string name="label_to">Зашифрувати до:</string>
<string name="label_delete_after_decryption">Вилучити після розшифрування</string>
<string name="label_encryption_algorithm">Алгоритм шифрування</string>
<string name="label_hash_algorithm">Хеш алгоритм</string>
- <string name="label_symmetric">Зашифрувати з парольною фразою</string>
- <string name="label_passphrase_cache_ttl">Кешувати час</string>
- <string name="label_passphrase_cache_subs">Кешувати парольні фрази за підключем</string>
<string name="label_file_compression">Стиснення файлу</string>
- <string name="label_keyservers">Сервери ключів</string>
<string name="label_key_id">ІД ключа</string>
<string name="label_creation">Створення</string>
<string name="label_expiry">Закінчення</string>
@@ -100,8 +81,13 @@
<string name="label_send_key">Синхронізувати із хмарою</string>
<string name="label_fingerprint">Відбиток</string>
<string name="expiry_date_dialog_title">Задати термін дії</string>
- <string name="label_first_keyserver_is_used">(Перший наведений сервер ключів є бажаний)</string>
<string name="label_preferred">бажаний</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <!--proxy type choices and values-->
+ <!--OrbotHelper strings-->
+ <!--InstallDialogFragment strings-->
+ <!--StartOrbotDialogFragment strings-->
<string name="user_id_no_name">&lt;без імені&gt;</string>
<string name="none">&lt;жоден&gt;</string>
<plurals name="n_keys">
@@ -143,17 +129,11 @@
<string name="flag_encrypt">Зашифрувати</string>
<string name="flag_authenticate">Перевірити справжність</string>
<!--sentences-->
- <string name="wrong_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="no_file_selected">Виберіть спершу файл.</string>
<string name="encrypt_sign_successful">Успішно підписано та/або перевірено.</string>
<string name="encrypt_sign_clipboard_successful">Успішно підписано та/або зашифровано до буфера обміну.</string>
<string name="select_encryption_key">Виберіть принаймні один ключ шифрування.</string>
- <string name="select_encryption_or_signature_key">Виберіть принаймні один ключ шифрування або ключ підпису.</string>
<string name="also_export_secret_keys">Також експортувати секретні ключі</string>
<string name="key_exported">Успішно експортовано 1 ключ.</string>
<string name="keys_exported">Успішно експортовано %d ключів.</string>
@@ -182,12 +162,9 @@
<string name="error_external_storage_not_ready">зовнішній носій не готовий</string>
<string name="error_key_size_minimum512bit">ключ має мати хоча б 512 біт</string>
<string name="error_unknown_algorithm_choice">вибір невідомого алгоритму</string>
- <string name="error_user_id_no_email">жодного листа не знайдено</string>
<string name="error_key_needs_a_user_id">хоча б одна сутність</string>
- <string name="error_no_signature_passphrase">не подано парольної фрази</string>
<string name="error_no_signature_key">не подано ключ підпису</string>
<string name="error_integrity_check_failed">Невдала перевірка цілісності! Дані вже змінено!</string>
- <string name="error_wrong_passphrase">помилкова парольна фраза</string>
<string name="error_could_not_extract_private_key">не можна витягти секретний ключ</string>
<!--errors without preceeding Error:-->
<string name="error_jelly_bean_needed">Вам потрібний Android 4.1 для використання функції Androids NFC промінь!</string>
@@ -196,10 +173,6 @@
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">Не підписано</string>
<string name="decrypt_result_invalid_signature">Невірний підпис!</string>
- <string name="decrypt_result_signature_uncertified">Підписано (не сертифіковано)</string>
- <string name="decrypt_result_signature_expired_key">Термін дії ключа минув!</string>
- <string name="decrypt_result_signature_revoked_key">Ключ вже відкликано!</string>
- <string name="decrypt_result_signature_missing_key">Невідомий відкритий ключ</string>
<string name="decrypt_result_encrypted">Зашифровано</string>
<string name="decrypt_result_not_encrypted">Незашифровано</string>
<string name="decrypt_result_action_show">Показати</string>
@@ -228,7 +201,6 @@
<string name="progress_modify_subkeychange">змінюються підключі…</string>
<string name="progress_modify_subkeyrevoke">відхилення підключів…</string>
<string name="progress_modify_subkeyadd">додаються підключі…</string>
- <string name="progress_modify_passphrase">змінюється пароль…</string>
<plurals name="progress_exporting_key">
<item quantity="one">експортується ключ…</item>
<item quantity="few">експортуються ключі…</item>
@@ -291,7 +263,7 @@
<string name="import_tab_qr_code">Код QR/NFC</string>
<string name="import_import">Імпортувати вибрані ключі</string>
<string name="import_qr_code_wrong">Невірний штрих-код! Спробуйте знову!</string>
- <string name="import_qr_code_too_short_fingerprint">Відбиток надто короткий (&lt; 16 символів)</string>
+ <!--Import from URL-->
<!--Generic result toast-->
<!--Import result toast-->
<plurals name="import_keys_added_and_updated_1">
@@ -319,6 +291,7 @@
<!--Delete result toast-->
<string name="delete_nothing">Нема що вилучати.</string>
<string name="delete_cancelled">Операція вилучення скасована.</string>
+ <!--Revoke result toast (snackbar)-->
<!--Certify result toast-->
<!--Intent labels-->
<string name="intent_decrypt_file">Розшифрувати файл з OpenKeychain</string>
@@ -326,10 +299,6 @@
<string name="intent_send_encrypt">Зашифрувати з OpenKeychain</string>
<string name="intent_send_decrypt">Розшифрувати з OpenKeychain</string>
<!--Remote API-->
- <string name="api_settings_show_info">Показати додаткову інформацію</string>
- <string name="api_settings_hide_info">Приховати додаткову інформацію</string>
- <string name="api_settings_show_advanced">Показати додаткові налаштування</string>
- <string name="api_settings_hide_advanced">Приховати додаткові налаштування</string>
<string name="api_settings_no_key">Не вибрано ключа</string>
<string name="api_settings_select_key">Вибрати ключ</string>
<string name="api_settings_save">Зберегти</string>
@@ -338,20 +307,20 @@
<string name="api_settings_start">Запустити програму</string>
<string name="api_settings_delete_account">Видалити профіль</string>
<string name="api_settings_package_name">Назва пакунку</string>
- <string name="api_settings_package_signature">SHA-256 підписку пакунку</string>
<string name="api_settings_settings">Параметри</string>
<string name="api_settings_accounts_empty">Немає облікового запису приєднаного до цієї програми.</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">Не знайдено жодного ключа для цих сутностей:</string>
- <string name="api_select_pub_keys_dublicates_text">Наявно більше одного ключа для цих сутностей:</string>
<string name="api_select_pub_keys_text">Будь ласка, перевірте список одержувачів!</string>
<string name="api_select_pub_keys_text_no_user_ids">Будь ласка, виберіть одержувачів!</string>
<string name="api_error_wrong_signature">Перевірка підпису пакету не вдалася! Може ви встановили програму з іншого джерела? Якщо ви впевнені, що це не атака, то відкличте реєстрацію програми у OpenKeychain та знову зареєструйте її.</string>
<!--Share-->
<string name="share_qr_code_dialog_title">Відправити як штрих-код</string>
<string name="share_nfc_dialog">Поділитися з NFC</string>
+ <!--retry upload dialog-->
+ <!--Delete or revoke private key dialog-->
+ <!--Delete Or Revoke Dialog spinner-->
<!--Key list-->
<plurals name="key_list_selected_keys">
<item quantity="one">1 ключ вибрано.</item>
@@ -381,7 +350,6 @@
<!--Key trust-->
<!--keybase proof stuff-->
<!--Edit key-->
- <string name="edit_key_action_change_passphrase">Змінити пароль</string>
<string name="edit_key_action_add_identity">Додати сутність</string>
<string name="edit_key_action_add_subkey">Додати підключ</string>
<string name="edit_key_edit_user_id_title">Виберіть дію!</string>
@@ -393,9 +361,9 @@
<string name="edit_key_error_add_subkey">Додати хоча б один підключ!</string>
<!--Create key-->
<string name="create_key_empty">Це поле - обов\'язкове</string>
- <string name="create_key_passphrases_not_equal">Паролі фрази не збігаються</string>
<string name="create_key_final_text">Ви ввели наступну сутність:</string>
<!--View key-->
+ <!--Add/Edit keyserver-->
<!--Navigation Drawer-->
<string name="nav_keys">Ключі</string>
<string name="nav_apps">Програми</string>
@@ -498,11 +466,11 @@
<string name="msg_mf_error_noexist_revoke">Вказаний поганий ІД первинного користувача!</string>
<string name="msg_mf_error_revoked_primary">ІД відхилених користувачів не може бути первинним!</string>
<string name="msg_mf_error_sig">Виняток підпису!</string>
+ <string name="msg_mf_error_subkey_missing">Спробували працювати із втраченим підключем %s!</string>
<string name="msg_mf_master">Змінюються основі сертифікації</string>
<string name="msg_mf_primary_replace_old">Замінюється сертифікат ІД попереднього первинного користувача</string>
<string name="msg_mf_primary_new">Генерується новий сертифікат для ІД нового первинного користувача</string>
<string name="msg_mf_subkey_change">Змінюється підключ %s</string>
- <string name="msg_mf_error_subkey_missing">Спробували працювати із втраченим підключем %s!</string>
<string name="msg_mf_subkey_new">Додається новий підключ типу %s</string>
<string name="msg_mf_subkey_new_id">Новий ід підключа: %s</string>
<string name="msg_mf_error_past_expiry">Дата завершення дії не може бути у минулому!</string>
@@ -538,12 +506,10 @@
<item quantity="few">частини завантаженого файлу є вірним об\'єктом OpenPGP, але не ключем OpenPGP</item>
<item quantity="other">частин завантаженого файлу є вірним об\'єктом OpenPGP, але не ключем OpenPGP</item>
</plurals>
+ <!--Messages for Keybase Verification operation-->
<!--Messages for Export Log operation-->
<!--PassphraseCache-->
- <string name="passp_cache_notif_click_to_clear">Клацніть для очищення кешованих парольних фраз</string>
- <string name="passp_cache_notif_n_keys">OpenKeychain має %d кешованих парольних фраз</string>
- <string name="passp_cache_notif_keys">Кешовані парольні фрази:</string>
- <string name="passp_cache_notif_clear">Очистити кеш</string>
+ <!--Keyserver sync-->
<!--First Time-->
<string name="first_time_text1">Заберіть вашу приватність із OpenKeychain!</string>
<string name="first_time_skip">Пропустити установку</string>
@@ -564,8 +530,9 @@
<string name="can_sign_not">не можна підписати</string>
<string name="error_no_encrypt_subkey">Жодний підключ шифрування недоступний!</string>
<string name="contact_show_key">Показати ключ (%s)</string>
+ <!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
</resources>
diff --git a/OpenKeychain/src/main/res/values-v21/themes.xml b/OpenKeychain/src/main/res/values-v21/themes.xml
index b47026e5b..a366e9ab2 100644
--- a/OpenKeychain/src/main/res/values-v21/themes.xml
+++ b/OpenKeychain/src/main/res/values-v21/themes.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <style name="KeychainTheme" parent="KeychainTheme.Base">
+ <style name="Theme.Keychain.Light" parent="Base.Theme.Keychain.Light">
<item name="android:windowTranslucentStatus">true</item>
<!-- enable window content transitions -->
@@ -12,4 +12,17 @@
<item name="android:windowSharedElementExitTransition">@android:transition/move</item>
</style>
-</resources> \ No newline at end of file
+
+ <style name="Theme.Keychain.Dark" parent="Base.Theme.Keychain.Dark">
+ <item name="android:windowTranslucentStatus">true</item>
+
+ <!-- enable window content transitions -->
+ <item name="android:windowContentTransitions">true</item>
+ <item name="android:windowAllowEnterTransitionOverlap">true</item>
+ <item name="android:windowAllowReturnTransitionOverlap">true</item>
+ <item name="android:windowSharedElementEnterTransition">@android:transition/move</item>
+ <item name="android:windowSharedElementExitTransition">@android:transition/move</item>
+
+ </style>
+
+</resources>
diff --git a/OpenKeychain/src/main/res/values-zh-rTW/strings.xml b/OpenKeychain/src/main/res/values-zh-rTW/strings.xml
index 95fa8a28c..74d1cd781 100644
--- a/OpenKeychain/src/main/res/values-zh-rTW/strings.xml
+++ b/OpenKeychain/src/main/res/values-zh-rTW/strings.xml
@@ -7,13 +7,12 @@
<string name="title_encrypt_text">加密</string>
<string name="title_encrypt_files">加密</string>
<string name="title_decrypt">解密</string>
- <string name="title_unlock">解鎖金鑰</string>
<string name="title_add_subkey">新增子金鑰</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_passphrase">變更口令</string>
+ <string name="title_key_server_preference">OpenPGP金鑰伺服器</string>
+ <string name="title_change_passphrase">修改金鑰密碼</string>
<string name="title_share_fingerprint_with">分享指紋…</string>
<string name="title_share_key">分享金鑰…</string>
<string name="title_share_file">分享檔案…</string>
@@ -22,36 +21,43 @@
<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_export_keys">匯出金鑰</string>
<string name="title_key_not_found">找不到金鑰</string>
<string name="title_send_key">上傳到金鑰伺服器</string>
- <string name="title_certify_key">確認金鑰</string>
- <string name="title_key_details">金鑰內容</string>
+ <string name="title_certify_key">認證金鑰</string>
+ <string name="title_key_details">詳細內容</string>
<string name="title_help">說明</string>
<string name="title_log_display">紀錄</string>
<string name="title_exchange_keys">交換金鑰</string>
- <string name="title_advanced_key_info">進階資訊</string>
+ <string name="title_advanced_key_info">延伸資訊</string>
<string name="title_delete_secret_key">刪除您的金鑰 \'%s\'?</string>
<string name="title_export_log">匯出記錄</string>
<string name="title_manage_my_keys">管理我的金鑰</string>
<!--section-->
<string name="section_user_ids">身份</string>
+ <string name="section_yubikey">YubiKey</string>
+ <string name="section_linked_system_contact">已關聯帳戶</string>
<string name="section_should_you_trust">您信任這把金鑰嗎?</string>
<string name="section_keys">子金鑰</string>
<string name="section_cloud_search">雲端查詢</string>
- <string name="section_passphrase_cache">口令快取</string>
+ <string name="section_passphrase_cache">金鑰密碼快取</string>
+ <string name="section_proxy_settings">代理伺服器設定</string>
<string name="section_certify">確認</string>
<string name="section_actions">操作</string>
<string name="section_share_key">金鑰</string>
<string name="section_key_server">金鑰伺服器</string>
<string name="section_fingerprint">指紋</string>
<string name="section_encrypt">加密</string>
- <string name="section_decrypt">解密</string>
+ <string name="section_decrypt">解密/驗證</string>
+ <string name="section_current_expiry">目前有效期限</string>
+ <string name="section_new_expiry">新的有效期限</string>
<!--button-->
<string name="btn_decrypt_verify_file">解密並驗證檔案</string>
<string name="btn_encrypt_share_file">加密並分享檔案</string>
<string name="btn_encrypt_save_file">加密並儲存檔案</string>
+ <string name="btn_save_file">儲存檔案</string>
<string name="btn_save">儲存</string>
+ <string name="btn_view_log">查看紀錄</string>
<string name="btn_do_not_save">取消</string>
<string name="btn_delete">刪除</string>
<string name="btn_no_date">永不過期</string>
@@ -60,19 +66,23 @@
<string name="btn_next">下一步</string>
<string name="btn_back">返回</string>
<string name="btn_no">不</string>
- <string name="btn_match">指紋符合</string>
+ <string name="btn_match">指紋相符</string>
<string name="btn_share_encrypted_signed">加密並分享文字</string>
<string name="btn_copy_encrypted_signed">加密並複製文字</string>
<string name="btn_view_cert_key">檢視簽署的金鑰</string>
<string name="btn_create_key">建立金鑰</string>
<string name="btn_add_files">加入檔案</string>
- <string name="btn_add_share_decrypted_text">解密並分享訊息</string>
- <string name="btn_decrypt_clipboard">解密剪貼簿裏的文字</string>
- <string name="btn_decrypt_and_verify">並且驗證簽名</string>
- <string name="btn_decrypt_files">解密檔案</string>
+ <string name="btn_share_decrypted_text">分享已解密的訊息</string>
+ <string name="btn_copy_decrypted_text">複製已解密的訊息</string>
+ <string name="btn_decrypt_clipboard">從剪貼簿中讀取</string>
+ <string name="btn_decrypt_files">選擇要解密的檔案</string>
<string name="btn_encrypt_files">加密檔案</string>
<string name="btn_encrypt_text">加密文字</string>
<string name="btn_add_email">增加額外的電子郵件信箱</string>
+ <string name="btn_unlock">解鎖</string>
+ <string name="btn_add_keyserver">加入新伺服器</string>
+ <string name="btn_save_default">設為預設值</string>
+ <string name="btn_saved">已儲存</string>
<!--menu-->
<string name="menu_preferences">設定</string>
<string name="menu_help">說明</string>
@@ -85,40 +95,43 @@
<string name="menu_encrypt_to">加密到...</string>
<string name="menu_select_all">全選</string>
<string name="menu_export_all_keys">匯出所有金鑰</string>
- <string name="menu_update_all_keys">上傳所有金鑰</string>
- <string name="menu_advanced">顯示進階資訊</string>
+ <string name="menu_update_all_keys">更新所有金鑰</string>
+ <string name="menu_advanced">附加訊息</string>
<string name="menu_certify_fingerprint">藉由指紋比對來認證</string>
<string name="menu_export_log">匯出記錄</string>
+ <string name="menu_keyserver_add">新增</string>
<!--label-->
<string name="label_message">文字</string>
<string name="label_file">檔案</string>
<string name="label_files">檔案</string>
<string name="label_file_colon">檔案:</string>
- <string name="label_no_passphrase">沒有口令</string>
- <string name="label_passphrase">口令</string>
+ <string name="label_no_passphrase">沒有密碼</string>
+ <string name="label_passphrase">密碼</string>
<string name="label_unlock">解鎖中...</string>
- <string name="label_passphrase_again">再次輸入口令</string>
- <string name="label_show_passphrase">顯示口令</string>
+ <string name="label_passphrase_again">再次輸入密碼</string>
+ <string name="label_show_passphrase">顯示密碼</string>
<string name="label_algorithm">演算法</string>
+ <string name="label_file_ascii_armor">以ASCII輸出</string>
<string name="label_write_version_header">讓別人知道我在使用OpenKeychain</string>
<string name="label_write_version_header_summary">在簽名、密文與匯出的金鑰裡寫入\'OpenKeychain v2.7\'</string>
<string name="label_use_default_yubikey_pin">使用預設的 Yubikey PIN</string>
<string name="label_use_num_keypad_for_yubikey_pin">輸入 YubiKey PIN 時使用數字鍵盤</string>
<string name="label_label_use_default_yubikey_pin_summary">使用預設的 PIN (123456) 來使用 NFC 存取 Yubikeys</string>
- <string name="label_asymmetric_from">簽名自:</string>
+ <string name="label_asymmetric_from">簽名:</string>
<string name="label_to">加密給:</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_symmetric">使用口令加密</string>
- <string name="label_passphrase_cache_ttl">快取時間</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_keyservers">金鑰伺服器</string>
+ <string name="label_keyservers">選擇OpenPGP金鑰伺服器</string>
<string name="label_key_id">金鑰ID</string>
+ <string name="label_key_created">金鑰 %s 已建立</string>
<string name="label_creation">建立</string>
- <string name="label_expiry">效期</string>
+ <string name="label_expiry">有效期限</string>
<string name="label_usage">用途</string>
<string name="label_key_size">金鑰長度</string>
<string name="label_ecc_curve">橢圓曲線</string>
@@ -128,16 +141,50 @@
<string name="label_email">電子郵件</string>
<string name="label_send_key">與雲端同步</string>
<string name="label_fingerprint">指紋</string>
- <string name="expiry_date_dialog_title">設定效期</string>
- <string name="label_first_keyserver_is_used">(先列出的金鑰伺服器爲優先)</string>
+ <string name="expiry_date_dialog_title">設定有效期限</string>
+ <string name="label_keyservers_title">金鑰伺服器</string>
+ <string name="label_keyserver_settings_hint">拖曳改變順序,點擊編輯或刪除。</string>
+ <string name="label_selected_keyserver_title">已選擇的金鑰伺服器</string>
<string name="label_preferred">優先</string>
<string name="label_enable_compression">啓用壓縮</string>
<string name="label_encrypt_filenames">加密檔名</string>
<string name="label_hidden_recipients">隱藏收件人</string>
- <string name="pref_keyserver">搜尋金鑰伺服器</string>
- <string name="pref_keyserver_summary">搜尋 HKP 金鑰伺服器</string>
- <string name="pref_keybase">搜尋 Keybase.io</string>
- <string name="pref_keybase_summary">搜尋 Keybase.io 索引</string>
+ <string name="label_verify_keyserver">驗證金鑰伺服器</string>
+ <string name="label_enter_keyserver_url">輸入金鑰伺服器網址</string>
+ <string name="label_keyserver_dialog_delete">刪除金鑰伺服器</string>
+ <string name="label_theme">主題</string>
+ <string name="pref_keyserver">OpenPGP金鑰伺服器</string>
+ <string name="pref_keyserver_summary">透過已選擇的金鑰伺服器搜尋金鑰(HKP 協定)</string>
+ <string name="pref_keybase">keybase.io</string>
+ <string name="pref_keybase_summary">在keybase.io上搜尋金鑰</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <string name="pref_proxy_tor_title">啟用洋蔥(Tor)網路</string>
+ <string name="pref_proxy_tor_summary">必須已安裝 Orbot</string>
+ <string name="pref_proxy_normal_title">啟用其他代理伺服器</string>
+ <string name="pref_proxy_host_title">Proxy主機位置</string>
+ <string name="pref_proxy_host_err_invalid">Proxy主機位置不能為空白</string>
+ <string name="pref_proxy_port_title">連接埠</string>
+ <string name="pref_proxy_port_err_invalid">無效的連結埠</string>
+ <string name="pref_proxy_type_title">Proxy類型</string>
+ <!--proxy type choices and values-->
+ <string name="pref_proxy_type_choice_http">HTTP</string>
+ <string name="pref_proxy_type_choice_socks">SOCKS</string>
+ <!--OrbotHelper strings-->
+ <string name="orbot_ignore_tor">不使用Tor</string>
+ <!--InstallDialogFragment strings-->
+ <string name="orbot_install_dialog_title">是否安裝Orbot以使用Tor?</string>
+ <string name="orbot_install_dialog_install">安裝</string>
+ <string name="orbot_install_dialog_content">您必須安裝Orbot並透過它進行網路代理。您是否要安裝Orbot?</string>
+ <string name="orbot_install_dialog_cancel">取消</string>
+ <string name="orbot_install_dialog_ignore_tor">不使用Tor</string>
+ <!--StartOrbotDialogFragment strings-->
+ <string name="orbot_start_dialog_title">啟動Orbot?</string>
+ <string name="orbot_start_dialog_content">Orbot尚未執行,您希望啟動它並連結到Tor嗎?</string>
+ <string name="orbot_start_btn">啟動Orbot</string>
+ <string name="orbot_start_dialog_start">啟動Orbot</string>
+ <string name="orbot_start_dialog_cancel">取消</string>
+ <string name="orbot_start_dialog_ignore_tor">不使用Tor</string>
<string name="user_id_no_name">&lt;無名&gt;</string>
<string name="none">&lt;無&gt;</string>
<plurals name="n_keys">
@@ -146,7 +193,7 @@
<plurals name="n_keyservers">
<item quantity="other">%d 金鑰伺服器</item>
</plurals>
- <string name="secret_key">密鑰:</string>
+ <string name="secret_key">私鑰:</string>
<!--choice-->
<string name="choice_none">無</string>
<string name="choice_15secs">15秒</string>
@@ -169,34 +216,39 @@
<string name="filemanager_title_open">開啟…</string>
<string name="error">錯誤</string>
<string name="error_message">錯誤: %s</string>
+ <string name="theme_dark">深色</string>
+ <string name="theme_light">亮色</string>
<!--key flags-->
<string name="flag_certify">簽署</string>
<string name="flag_sign">簽名</string>
<string name="flag_encrypt">加密</string>
<string name="flag_authenticate">驗證</string>
<!--sentences-->
- <string name="wrong_passphrase">口令錯誤。</string>
+ <string name="wrong_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="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="passphrase_for">輸入 %s 的密碼</string>
<string name="pin_for">輸入 \'%s\' 的 PIN</string>
<string name="yubikey_pin_for">輸入 PIN 來存取 \'%s\' 的 YubiKey</string>
<string name="file_delete_confirmation_title">刪除原始檔案?</string>
<string name="file_delete_confirmation">下列的檔案將會被刪除:%s</string>
<string name="file_delete_successful">%1$d 之 %2$d 的檔案已經被刪除。%3$s</string>
- <string name="no_file_selected">請先選擇檔案。</string>
+ <string name="no_file_selected">沒有選擇檔案。</string>
<string name="encrypt_sign_successful">成功簽名並/或加密。</string>
<string name="encrypt_sign_clipboard_successful">成功簽名並/或加密到剪貼簿。</string>
<string name="select_encryption_key">選擇至少一把加密金鑰。</string>
- <string name="select_encryption_or_signature_key">選擇至少一把加密或簽名金鑰。</string>
+ <string name="error_no_encryption_or_signature_key">請選擇至少一把加密用金鑰或簽名用金鑰。</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_backup_dest">將匯出不包含您自己金鑰的備份,請選擇輸出檔案位置。\n注意:已經存在的檔案將被覆蓋。</string>
+ <string name="specify_backup_dest_single">將匯出這把公鑰,請選擇輸出檔案位置。\n警告:已經存在的檔案將被覆蓋。</string>
+ <string name="specify_backup_dest_secret_single">將匯出公鑰及私鑰,請選擇輸出檔案位置。\n注意:已經存在的檔案將被覆蓋。</string>
+ <string name="specify_backup_dest_secret">將匯出所有金鑰,包含您自己的公鑰及私鑰,請選擇輸出檔案位置。\n注意:已經存在的檔案將被覆蓋。</string>
<string name="key_deletion_confirmation_multi">您真的想要刪除所有已選金鑰嗎?</string>
<string name="secret_key_deletion_confirmation">刪除之後您將無法閱讀以這把金鑰加密的訊息,而且所有用這把金鑰做的認證都會失效!</string>
- <string name="public_key_deletetion_confirmation">刪除金鑰 \'%s\' ?</string>
+ <string name="public_key_deletetion_confirmation">刪除金鑰 \'%s\' ?</string>
<string name="also_export_secret_keys">一併匯出私鑰</string>
<string name="reinstall_openkeychain">您遇到了一個已知的 Android 錯誤。如果您想要鏈接聯絡人和金鑰,請重新安裝 OpenKeychain。</string>
<string name="key_exported">成功匯出 1 把金鑰。</string>
@@ -227,32 +279,35 @@
<string name="error_unknown_algorithm_choice">不明的演算法選擇</string>
<string name="error_user_id_no_email">沒有找到電子郵件</string>
<string name="error_key_needs_a_user_id">需要至少一個身分識別</string>
- <string name="error_no_signature_passphrase">沒有提供口令</string>
<string name="error_no_signature_key">找不到簽名金鑰</string>
<string name="error_invalid_data">沒有有效的 OpenPGP 加密或簽署內容 !</string>
<string name="error_integrity_check_failed">完整性檢查失敗!資料已被修改!</string>
- <string name="error_wrong_passphrase">口令錯誤</string>
+ <string name="error_wrong_passphrase">密碼錯誤</string>
<string name="error_could_not_extract_private_key">無法抽取私鑰</string>
<!--errors without preceeding Error:-->
<string name="error_jelly_bean_needed">需要Android 4.1以上才能使用NFC Beam功能!</string>
<string name="error_nfc_needed">NFC 必須開啓 !</string>
<string name="error_beam_needed">Bean 必須開啓 !</string>
<string name="error_nothing_import">找不到金鑰!</string>
+ <string name="error_nothing_import_selected">沒有選擇匯入的金鑰!</string>
<string name="error_contacts_key_id_missing">從聯絡人取回金鑰 ID 失敗 !</string>
<string name="error_generic_report_bug">發生錯誤,請建立一個新的錯誤回報給 OpenKeychain。</string>
<!--results shown after decryption/verification-->
<string name="decrypt_result_no_signature">尚未簽名</string>
<string name="decrypt_result_invalid_signature">無效的簽章!</string>
- <string name="decrypt_result_signature_uncertified">簽署自 (未認證 !)</string>
- <string name="decrypt_result_signature_certified">簽署自</string>
- <string name="decrypt_result_signature_expired_key">金鑰已過期!</string>
- <string name="decrypt_result_signature_revoked_key">金鑰已被撤銷!</string>
- <string name="decrypt_result_signature_missing_key">未知的公鑰</string>
+ <string name="decrypt_result_insecure_cryptography">無效的簽名(此加密不安全)!</string>
+ <string name="decrypt_result_signature_uncertified">由<b>未確認</b>的金鑰所簽署</string>
+ <string name="decrypt_result_signature_secret">由您的金鑰所簽署</string>
+ <string name="decrypt_result_signature_certified">由已確認的金鑰所簽署</string>
+ <string name="decrypt_result_signature_expired_key">由<b>已過期</b>的金鑰所簽署!</string>
+ <string name="decrypt_result_signature_revoked_key">由<b>已撤銷</b>的金鑰所簽署!</string>
+ <string name="decrypt_result_signature_missing_key">由<b>未知的金鑰</b>所簽署</string>
<string name="decrypt_result_encrypted">已加密</string>
<string name="decrypt_result_not_encrypted">未加密</string>
+ <string name="decrypt_result_insecure">不安全的加密</string>
<string name="decrypt_result_action_show">顯示</string>
<string name="decrypt_result_action_Lookup">查詢</string>
- <string name="decrypt_invalid_text">簽名無效或是金鑰已經過期/被撤銷,無法確認訊息內容的作者。是否繼續顯示?</string>
+ <string name="decrypt_invalid_text">包含無效的簽名或已撤銷得金鑰,您並無法確保此加密的來源,您依舊要顯示內容嗎?</string>
<string name="decrypt_invalid_button">我明白這樣做的風險,顯示它!</string>
<!--Add keys-->
<string name="add_keys_my_key">我的金鑰:</string>
@@ -262,6 +317,7 @@
<string name="progress_cancelling">正在取消…</string>
<string name="progress_saving">正在儲存…</string>
<string name="progress_importing">匯入中…</string>
+ <string name="progress_revoking_uploading">正在撤銷並上傳金鑰...</string>
<string name="progress_updating">正在更新金鑰...</string>
<string name="progress_exporting">匯出中…</string>
<string name="progress_uploading">正在上傳…</string>
@@ -280,12 +336,15 @@
<string name="progress_modify_primaryuid">正在變更主要使用者身份…</string>
<string name="progress_modify_subkeychange">正在變更子金鑰…</string>
<string name="progress_modify_subkeyrevoke">正在撤銷子金鑰…</string>
- <string name="progress_modify_subkeystrip">正在卸下子金鑰...</string>
+ <string name="progress_modify_subkeystrip">正在卸除子金鑰...</string>
<string name="progress_modify_subkeyadd">正在新增子金鑰…</string>
- <string name="progress_modify_passphrase">正在變更口令…</string>
+ <string name="progress_modify_passphrase">正在變更金鑰密碼...</string>
+ <string name="progress_modify_pin">正在變更PIN碼...</string>
+ <string name="progress_modify_admin_pin">正在變更管理者PIN碼...</string>
<plurals name="progress_exporting_key">
<item quantity="other">正在匯出金鑰…</item>
</plurals>
+ <string name="progress_start">正在準備進行操作...</string>
<string name="progress_extracting_signature_key">正在抽取簽名金鑰…</string>
<string name="progress_extracting_key">正在抽取金鑰…</string>
<string name="progress_preparing_streams">正在準備串流…</string>
@@ -303,6 +362,8 @@
<string name="progress_verifying_integrity">正在驗證完整性…</string>
<string name="progress_deleting_securely">正在安全地刪除 \'%s\'...</string>
<string name="progress_deleting">正在刪除金鑰…</string>
+ <string name="progress_verifying_keyserver_url">正在驗證金鑰伺服器...</string>
+ <string name="progress_starting_orbot">正在啟動Orbot...</string>
<!--action strings-->
<string name="hint_cloud_search_hint">使用姓名,電子郵件尋找...</string>
<!--key bit length selections-->
@@ -344,11 +405,13 @@
<string name="import_tab_qr_code">二維條碼/NFC</string>
<string name="import_import">匯入選擇的金鑰</string>
<string name="import_qr_code_wrong">QR Code 異常 ! 請再試一次 !</string>
- <string name="import_qr_code_too_short_fingerprint">指紋過短 (&lt; 16 個字元)</string>
+ <string name="import_qr_code_fp">指紋異常或長度不足!</string>
+ <string name="import_qr_code_too_short_fingerprint">長度不足!</string>
<string name="import_qr_code_button">掃描二維條碼</string>
<string name="import_qr_code_text">將您的相機對準 QR Code !</string>
+ <!--Import from URL-->
<!--Generic result toast-->
- <string name="view_log">詳細</string>
+ <string name="snackbar_details">詳細資訊</string>
<string name="with_warnings">,包含警告</string>
<string name="with_cancelled">,直到被取消</string>
<!--Import result toast-->
@@ -387,6 +450,11 @@
</plurals>
<string name="delete_nothing">沒有東西可以刪除。</string>
<string name="delete_cancelled">刪除已取消。</string>
+ <!--Revoke result toast (snackbar)-->
+ <string name="revoke_ok">成功撤銷金鑰。</string>
+ <string name="revoke_fail">錯誤的撤銷金鑰!</string>
+ <string name="revoke_nothing">沒有任何金鑰被撤銷。</string>
+ <string name="revoke_cancelled">撤銷動作被取消。</string>
<!--Certify result toast-->
<plurals name="certify_keys_ok">
<item quantity="other">成功認證 %1$d 金鑰%2$s。</item>
@@ -403,10 +471,10 @@
<string name="intent_send_encrypt">使用OpenKeychain加密</string>
<string name="intent_send_decrypt">使用OpenKeychain解密</string>
<!--Remote API-->
- <string name="api_settings_show_info">顯示進階資訊</string>
- <string name="api_settings_hide_info">隱藏進階資訊</string>
- <string name="api_settings_show_advanced">顯示進階設定</string>
- <string name="api_settings_hide_advanced">隱藏進階設定</string>
+ <string name="api_settings_show_info">顯示延伸資訊</string>
+ <string name="api_settings_hide_info">隱藏延伸資訊</string>
+ <string name="api_settings_show_advanced">顯示延伸設定</string>
+ <string name="api_settings_hide_advanced">隱藏延伸設定</string>
<string name="api_settings_no_key">沒有選擇金鑰</string>
<string name="api_settings_select_key">選擇金鑰</string>
<string name="api_settings_create_key">建立新金鑰</string>
@@ -417,7 +485,10 @@
<string name="api_settings_start">開啟應用程式</string>
<string name="api_settings_delete_account">移除帳戶</string>
<string name="api_settings_package_name">打包名稱</string>
- <string name="api_settings_package_signature">SHA-256 所打包的簽章</string>
+ <string name="api_settings_package_certificate">封裝證書的SHA-256</string>
+ <string name="api_settings_accounts">帳戶(舊版API)</string>
+ <string name="api_settings_advanced">延伸資訊</string>
+ <string name="api_settings_allowed_keys">允許的金鑰</string>
<string name="api_settings_settings">設定</string>
<string name="api_settings_key">帳戶金鑰:</string>
<string name="api_settings_accounts_empty">沒有帳戶被指派給這個應用程式。</string>
@@ -426,10 +497,24 @@
<string name="api_register_text">應用程式試圖以您的名義加/解密訊息並簽名,是否允許存取?\n\n警告:如果你不知道為什麼出現這個畫面,請拒絕存取!你可以稍後在〝應用程式〞畫面撤銷存取。</string>
<string name="api_register_allow">允許存取</string>
<string name="api_register_disallow">拒絕存取</string>
- <string name="api_error_wrong_signature">簽名檢查失敗!你是否從其他來源安裝此應用?如果你確定這不是個攻擊,撤銷並重新註冊應用程式到OpenKeychain。</string>
+ <string name="api_register_error_select_key">請選擇一個金鑰!</string>
+ <string name="api_select_pub_keys_text">請檢查收件人清單!</string>
+ <string name="api_select_pub_keys_text_no_user_ids">請選擇一份清單!</string>
+ <string name="api_error_wrong_signature">簽名檢查失敗!你是否從其他來源安裝此應用程式?如果你確定這不是個攻擊,撤銷並重新註冊應用程式到OpenKeychain。</string>
+ <string name="api_select_sign_key_text">請選擇一個您的金鑰或建立一個新的金鑰。</string>
+ <string name="api_select_keys_text">沒有被允許的金鑰可以解密內容,請選擇被允許的金鑰。</string>
<!--Share-->
<string name="share_qr_code_dialog_title">以二維條碼分享</string>
<string name="share_nfc_dialog">以NFC分享</string>
+ <!--retry upload dialog-->
+ <string name="retry_up_dialog_title">上傳失敗</string>
+ <string name="retry_up_dialog_message">上傳失敗。是否再次嘗試上傳?</string>
+ <string name="retry_up_dialog_btn_reupload">重試</string>
+ <string name="retry_up_dialog_btn_cancel">取消</string>
+ <!--Delete or revoke private key dialog-->
+ <string name="del_rev_dialog_btn_delete">只刪除金鑰</string>
+ <!--Delete Or Revoke Dialog spinner-->
+ <string name="del_rev_dialog_choice_delete">只刪除金鑰</string>
<!--Key list-->
<string name="key_list_empty_text1">找不到金鑰!</string>
<!--Key view-->
@@ -442,6 +527,8 @@
<string name="key_view_tab_main">摘要</string>
<string name="key_view_tab_share">分享</string>
<string name="key_view_tab_keys">子金鑰</string>
+ <string name="key_view_tab_certs">簽署</string>
+ <string name="key_view_tab_keybase">Keybase.io</string>
<string name="user_id_info_revoked_title">已撤銷</string>
<string name="user_id_info_revoked_text">這個身分識別被金鑰持有人撤銷,已不再有效。</string>
<string name="user_id_info_certified_title">已認證</string>
@@ -449,50 +536,175 @@
<string name="user_id_info_uncertified_title">未認證</string>
<string name="user_id_info_uncertified_text">這個身分識別尚未經過認證,你不能確認這個身分識別是否屬於真的某個人。</string>
<string name="user_id_info_invalid_title">無效</string>
+ <string name="user_id_info_invalid_text">這個身份有些錯誤!</string>
<!--Key trust-->
+ <string name="key_trust_already_verified">您已經確認這把金鑰!</string>
+ <string name="key_trust_it_is_yours">這把是您的金鑰!</string>
+ <string name="key_trust_maybe">這把金鑰未被撤銷且尚未過期。\n金鑰尚未進行確認,但你可以選擇信任它。</string>
+ <string name="key_trust_revoked">這把金鑰已被持有者撤銷。您不應該信任它。</string>
+ <string name="key_trust_expired">這把金鑰已經過期。您不應該信任它。</string>
+ <string name="key_trust_old_keys">用來解密在金鑰失效前加密的訊息還勉強可以接受。</string>
+ <string name="key_trust_no_cloud_evidence">雲端上沒有證據能證明這把金鑰的可信度。</string>
+ <string name="key_trust_start_cloud_search">開始搜尋</string>
+ <string name="key_trust_results_prefix">Keybase.io提供“證據”證明這把金鑰的持有人:</string>
+ <string name="key_trust_header_text">注意:Keybase.io證據是個OpenKeychain實驗性功能。我們推薦你另外以QR條碼或是NFC交換金鑰進行確認。</string>
<!--keybase proof stuff-->
+ <string name="keybase_narrative_twitter">用下列身分在Twitter參與討論:%s</string>
+ <string name="keybase_proof_failure">很不幸地我們沒法驗證這些證據。</string>
+ <string name="keybase_unknown_proof_failure">檢查證據的過程遇到無法辨識的問題</string>
+ <string name="keybase_message_fetching_data">取得證據中</string>
+ <string name="keybase_proof_succeeded">這項證據已通過驗證!</string>
+ <string name="keybase_a_post">一則文章</string>
+ <string name="keybase_fetched_from">取得自</string>
+ <string name="keybase_for_the_domain">從域名</string>
+ <string name="keybase_contained_signature">包含一則只有金鑰持有人才能建立的訊息。</string>
+ <string name="keybase_twitter_proof">一則推文</string>
+ <string name="keybase_dns_proof">一筆DNS TXT記錄</string>
+ <string name="keybase_web_site_proof">一個文字檔案</string>
+ <string name="keybase_reddit_proof">一個JSON檔案</string>
+ <string name="keybase_verify">驗證</string>
<!--Edit key-->
- <string name="edit_key_action_change_passphrase">變更口令</string>
+ <string name="edit_key_action_change_passphrase">變更金鑰密碼</string>
<string name="edit_key_action_add_identity">新增身分識別</string>
<string name="edit_key_action_add_subkey">新增子金鑰</string>
+ <string name="edit_key_edit_user_id_title">選擇一個動作!</string>
<string-array name="edit_key_edit_user_id">
<item>變更為主身分識別</item>
<item>撤銷身分識別</item>
</string-array>
+ <string-array name="edit_key_edit_user_id_revert_revocation">
+ <item>還原撤銷</item>
+ </string-array>
<string name="edit_key_edit_user_id_revoked">這個身分識別已被撤銷。此動作無法還原。</string>
- <string name="edit_key_edit_subkey_title">選擇操作動作!</string>
+ <string name="edit_key_edit_subkey_title">選擇一個動作!</string>
<string-array name="edit_key_edit_subkey">
- <item>變更到期日</item>
+ <item>變更效期</item>
<item>撤銷子金鑰</item>
- <item>Strip Subkey</item>
+ <item>卸除子金鑰</item>
+ <item>移動子金鑰至YubiKey / 智慧卡</item>
</string-array>
<string name="edit_key_new_subkey">新增子金鑰</string>
+ <string name="edit_key_select_flag">請至少選擇一個用途!</string>
<string name="edit_key_error_add_identity">新增至少一組身分識別!</string>
<string name="edit_key_error_add_subkey">新增至少一組子金鑰!</string>
+ <string name="edit_key_error_bad_nfc_algo">智慧卡不支援該算法!</string>
+ <string name="edit_key_error_bad_nfc_size">智慧卡不支援該金鑰長度!</string>
+ <string name="edit_key_error_bad_nfc_stripped">無法移動金鑰至智慧卡(已移動或被卸除)!</string>
<!--Create key-->
+ <string name="create_key_upload">與雲端同步</string>
<string name="create_key_empty">必填欄位</string>
- <string name="create_key_passphrases_not_equal">口令不相符</string>
+ <string name="create_key_passphrases_not_equal">密碼不相符</string>
+ <string name="create_key_final_text">您輸入了以下資訊:</string>
<string name="create_key_final_robot_text">建立金鑰可能需要一點時間,來杯咖啡吧…</string>
+ <string name="create_key_rsa">(3把子金鑰,RSA,4096位元)</string>
+ <string name="create_key_custom">(自定義金鑰組合)</string>
<string name="create_key_name_text">選擇一個名字有關這個金鑰。 這可以是全名John Doe或是暱稱Johnny。</string>
- <string name="create_key_passphrase_text">選擇一個安全性較佳的口令,當手機遺失或遭竊時可以保護你的金鑰。</string>
+ <string name="create_key_email_text">輸入您的主要電子郵件,這提供您安全的通訊。</string>
+ <string name="create_key_passphrase_text">請選擇一個強度足夠的金鑰密碼,當您的裝置遺失時它將保護您的金鑰。</string>
<string name="create_key_hint_full_name">全名或暱稱</string>
+ <string name="create_key_edit">變更金鑰設置</string>
<string name="create_key_add_email">新增電子郵件</string>
+ <string name="create_key_add_email_text">附加的電子郵件同樣與您的金鑰有所關聯,可以提供您安全的通訊。</string>
+ <string name="create_key_email_already_exists_text">電子郵件地址已經存在</string>
+ <string name="create_key_email_invalid_email">電子郵件地址無效</string>
+ <string name="create_key_yubi_key_pin_text">請記住您的PIN碼,等會兒您的YubiKey需要它。請紀錄下管理者PIN碼並且將它保存在安全的地方。</string>
+ <string name="create_key_yubi_key_pin">PIN碼</string>
+ <string name="create_key_yubi_key_admin_pin">管理者PIN碼</string>
+ <string name="create_key_yubi_key_pin_repeat_text">請輸入PIN碼及管理者PIN碼以繼續執行。</string>
+ <string name="create_key_yubi_key_pin_repeat">重複PIN碼</string>
+ <string name="create_key_yubi_key_admin_pin_repeat">重複管理者PIN碼</string>
+ <string name="create_key_yubi_key_pin_not_correct">PIN碼不正確!</string>
<!--View key-->
+ <string name="view_key_revoked">已撤銷:不該再使用此金鑰!</string>
+ <string name="view_key_expired">已過期:聯絡人需要去展延金鑰的效期!</string>
+ <string name="view_key_expired_secret">已過期:你可以從編輯畫面展延金鑰效期!</string>
<string name="view_key_my_key">我的金鑰:</string>
+ <string name="view_key_verified">已確認金鑰</string>
+ <string name="view_key_unverified">未確認:掃描QR條碼來確認該金鑰!</string>
+ <string name="view_key_fragment_no_system_contact">&lt;無&gt;</string>
+ <!--Add/Edit keyserver-->
+ <string name="add_keyserver_dialog_title">新增金鑰伺服器</string>
+ <string name="edit_keyserver_dialog_title">編輯金鑰伺服器</string>
+ <string name="add_keyserver_verified">已驗證金鑰伺服器!</string>
+ <string name="add_keyserver_without_verification">已新增金鑰伺服器但並未進行驗證。</string>
+ <string name="add_keyserver_invalid_url">URL無效!</string>
+ <string name="add_keyserver_connection_failed">連線到金鑰伺服器失敗。請確認金鑰伺服器網址及網路連線。</string>
+ <string name="keyserver_preference_deleted">已刪除 %s</string>
+ <string name="keyserver_preference_cannot_delete_last">無法刪除金鑰伺服器。請至少保留一個金鑰伺服器!</string>
<!--Navigation Drawer-->
<string name="nav_keys">金鑰</string>
<string name="nav_encrypt_decrypt">加密/解密</string>
<string name="nav_apps">應用程式</string>
+ <string name="drawer_open">開啟導航列</string>
+ <string name="drawer_close">關閉導航列</string>
<string name="my_keys">我的金鑰</string>
+ <string name="nav_backup">備份</string>
<!--hints-->
+ <string name="encrypt_content_edit_text_hint">輸入文字</string>
<!--certs-->
<string name="cert_default">預設</string>
<string name="cert_none">無</string>
+ <string name="cert_casual">隨意的</string>
+ <string name="cert_positive">積極的</string>
+ <string name="cert_revoke">已撤銷</string>
<string name="cert_verify_ok">OK</string>
<string name="cert_verify_failed">失敗!</string>
<string name="cert_verify_error">錯誤!</string>
+ <string name="cert_verify_unavailable">金鑰無法使用</string>
<!--LogType log messages. Errors should have _ERROR_ in their name and end with a !-->
+ <string name="msg_internal_error">內部錯誤!</string>
+ <string name="msg_cancelled">操作已取消。</string>
<!--Import Public log entries-->
+ <string name="msg_ip_bad_type_secret">嘗試將密鑰匯入公鑰鑰匙圈。這不正常,麻煩回報問題!</string>
+ <string name="msg_ip_encode_fail">操作失敗,因為編碼錯誤</string>
+ <string name="msg_ip_error_io_exc">操作失敗,因為I/O錯誤</string>
+ <string name="msg_ip_error_op_exc">操作失敗,因為資料庫錯誤</string>
+ <string name="msg_ip_error_remote_ex">操作失敗,因為內部錯誤</string>
+ <string name="msg_ip">匯入公鑰鑰匙圈 %s</string>
+ <string name="msg_ip_insert_keys">正在解析金鑰</string>
+ <string name="msg_ip_prepare">正在準備資料庫</string>
+ <string name="msg_ip_master_flags_unspecified">主要標記:未指定(全用途)</string>
+ <string name="msg_ip_master_flags_cesa">主要標記:認證、加密、簽署、驗證</string>
+ <string name="msg_ip_master_flags_cesx">主要標記:認證、加密、簽署</string>
+ <string name="msg_ip_master_flags_cexa">主要標記:認證、加密、驗證</string>
+ <string name="msg_ip_master_flags_cexx">主要標記:認證、加密</string>
+ <string name="msg_ip_master_flags_cxsa">主要標記:認證、簽署、驗證</string>
+ <string name="msg_ip_master_flags_cxsx">主要標記:認證、簽署</string>
+ <string name="msg_ip_master_flags_cxxa">主要標記:認證、驗證</string>
+ <string name="msg_ip_master_flags_cxxx">主要標記:認證</string>
+ <string name="msg_ip_master_flags_xesa">主要標記:加密、簽署、驗證</string>
+ <string name="msg_ip_master_flags_xesx">主要標記:加密、簽署</string>
+ <string name="msg_ip_master_flags_xexa">主要標記:加密、驗證</string>
+ <string name="msg_ip_master_flags_xexx">主要標記:加密</string>
+ <string name="msg_ip_master_flags_xxsa">主要標記:簽署、驗證</string>
+ <string name="msg_ip_master_flags_xxsx">主要標記:簽署</string>
+ <string name="msg_ip_master_flags_xxxa">主要標記:驗證</string>
+ <string name="msg_ip_master_flags_xxxx">主要標記:無</string>
+ <string name="msg_ip_merge_public">將匯入的資料合併至公鑰鑰匙圈</string>
+ <string name="msg_ip_merge_secret">將匯入的資料合併至密鑰鑰匙圈</string>
+ <string name="msg_ip_subkey">正在處理子金鑰%s</string>
+ <string name="msg_ip_subkey_expired">子金鑰已於%s過期</string>
+ <string name="msg_ip_subkey_expires">子金鑰將於%s過期</string>
+ <string name="msg_ip_subkey_flags_unspecified">子金鑰標記:未指定(全用途)</string>
+ <string name="msg_ip_subkey_flags_cesa">子金鑰標記:認證、加密、簽署、驗證</string>
+ <string name="msg_ip_subkey_flags_cesx">子金鑰標記:認證、加密、簽署</string>
+ <string name="msg_ip_subkey_flags_cexa">子金鑰標記:認證、加密、驗證</string>
+ <string name="msg_ip_subkey_flags_cexx">子金鑰標記:認證、加密</string>
+ <string name="msg_ip_subkey_flags_cxsa">子金鑰標記:認證、簽署、驗證</string>
+ <string name="msg_ip_subkey_flags_cxsx">子金鑰標記:認證、簽署</string>
+ <string name="msg_ip_subkey_flags_cxxa">子金鑰標記:認證、驗證</string>
+ <string name="msg_ip_subkey_flags_cxxx">子金鑰標記:認證</string>
+ <string name="msg_ip_subkey_flags_xesa">子金鑰標記:加密、簽署、驗證</string>
+ <string name="msg_ip_subkey_flags_xesx">子金鑰標記:加密、簽署</string>
+ <string name="msg_ip_subkey_flags_xexa">子金鑰標記:加密、驗證</string>
+ <string name="msg_ip_subkey_flags_xexx">子金鑰標記:加密</string>
+ <string name="msg_ip_subkey_flags_xxsa">子金鑰標記:簽署、驗證</string>
+ <string name="msg_ip_subkey_flags_xxsx">子金鑰標記:簽署</string>
+ <string name="msg_ip_subkey_flags_xxxa">子金鑰標記:驗證</string>
+ <string name="msg_ip_subkey_flags_xxxx">子金鑰標記:無</string>
+ <string name="msg_ip_success">成功匯入公鑰鑰匙圈</string>
+ <string name="msg_ip_success_identical">鑰匙圈沒有新資料,不必進行操作</string>
+ <string name="msg_ip_reinsert_secret">重新插入密鑰</string>
<!--Import Secret log entries-->
<string name="msg_is_db_exception">資料庫錯誤!</string>
<string name="msg_is_merge_public">將匯入的資料合併至公鑰鑰匙圈</string>
@@ -504,13 +716,14 @@
<!--Keyring merging log entries-->
<!--createSecretKeyRing-->
<!--modifySecretKeyRing-->
+ <string name="msg_mf_subkey_strip">正在卸除子金鑰%s</string>
<!--Consolidate-->
<!--Edit Key (higher level than modify)-->
<!--Promote key-->
<!--Other messages used in OperationLogs-->
<string name="msg_ek_error_not_found">找不到金鑰!</string>
<!--Messages for DecryptVerify operation-->
- <string name="msg_dc_error_invalid_siglist">找不到有效的簽名資訊!</string>
+ <string name="msg_dc_error_invalid_data">沒有找到有效的OpenPGP加密或簽署內容!</string>
<string name="msg_dc">開始解密…</string>
<!--Messages for VerifySignedLiteralData operation-->
<!--Messages for SignEncrypt operation-->
@@ -521,7 +734,6 @@
<string name="msg_crt_upload_success">成功上傳金鑰到伺服器</string>
<string name="msg_import_fetch_error">無法取得金鑰!(網路問題?)</string>
<string name="msg_import_fetch_keybase">從keybase.io取回:%s</string>
- <string name="msg_import_fetch_keyserver_error">無法從keybase取得金鑰!</string>
<string name="msg_import_fetch_keyserver">從金鑰伺服器取回:%s</string>
<string name="msg_import_fetch_keyserver_ok">成功取得金鑰</string>
<string name="msg_import_keyserver">使用金鑰伺服器%s</string>
@@ -531,37 +743,39 @@
<string name="msg_export_error_io">輸入/輸出錯誤!</string>
<string name="msg_export_success">匯出處理成功</string>
<string name="msg_del_error_empty">沒有東西可以刪除!</string>
- <string name="msg_del_error_multi_secret">密鑰只能分別刪除!</string>
+ <string name="msg_del_error_multi_secret">私鑰只能分別刪除!</string>
<string name="msg_acc_saved">帳戶已儲存</string>
<string name="msg_download_success">下載成功!</string>
<string name="msg_download_no_valid_keys">在檔案/剪貼簿中找不到有效的金鑰!</string>
<string name="msg_download_query_failed">查詢金鑰的時候發生錯誤。</string>
+ <!--Messages for Keybase Verification operation-->
<!--Messages for Export Log operation-->
<!--PassphraseCache-->
- <string name="passp_cache_notif_n_keys">OpenKeychain快取了%d個口令</string>
- <string name="passp_cache_notif_keys">已快取的口令:</string>
- <string name="passp_cache_notif_clear">清除快取</string>
- <string name="passp_cache_notif_pwd">口令</string>
+ <!--Keyserver sync-->
<!--First Time-->
+ <string name="first_time_create_key">建立金鑰</string>
+ <string name="first_time_import_key">從檔案匯入金鑰</string>
+ <string name="first_time_yubikey">使用YubiKey NEO</string>
+ <string name="first_time_skip">跳過設置</string>
<!--unsorted-->
+ <string name="unknown_uid">&lt;未知&gt;</string>
<string name="empty_certs">這把金鑰未經過認證</string>
<string name="certs_text">只有有效的自簽署認證以及經由你的金鑰簽署的有效認證會被顯示在這。</string>
<string name="label_revocation">撤銷原因</string>
<string name="error_key_not_found">找不到金鑰!</string>
- <string name="key_no_passphrase">沒有口令</string>
<string name="key_unavailable">無法使用</string>
+ <string name="secret_cannot_multiple">您的金鑰只能個別地刪除!</string>
<string name="title_view_cert">查看認證內容</string>
<string name="unknown_algorithm">未知</string>
<string name="error_no_encrypt_subkey">沒有可供加密的子金鑰!</string>
- <string name="info_no_manual_account_creation">請不要自行建立OpenKeychain帳戶。\n更多資訊請參考說明。</string>
<string name="exchange_description">要發起金鑰交換,先在右邊選擇與會人數,然後點選〝開始交換〞。\n\n接下來會詢問你兩個問題,以確保會議成員與交換的指紋是正確的。</string>
<string name="btn_start_exchange">開始交換</string>
+ <!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<string name="passphrase_again">再一次</string>
<string name="unlock_method">解鎖方法</string>
- <string name="set_passphrase">設定口令</string>
<string name="nfc_title">NFC</string>
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
<string name="nfc_wrong_tag">錯誤的標籤。請再試一次。</string>
diff --git a/OpenKeychain/src/main/res/values-zh/strings.xml b/OpenKeychain/src/main/res/values-zh/strings.xml
index f43cb3ebd..ad85fd8ce 100644
--- a/OpenKeychain/src/main/res/values-zh/strings.xml
+++ b/OpenKeychain/src/main/res/values-zh/strings.xml
@@ -4,12 +4,14 @@
http://developer.android.com/guide/topics/resources/string-resource.html (scroll down to "Escaping apostrophes and quotes").-->
<string name="app_name">OpenKeychain</string>
<!--title-->
+ <string name="title_encrypt_text">加密</string>
+ <string name="title_encrypt_files">加密</string>
<string name="title_decrypt">解密</string>
<string name="title_add_subkey">添加子密钥</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_key_server_preference">OenPGP 密钥服务器</string>
<string name="title_change_passphrase">变更密码</string>
<string name="title_share_fingerprint_with">分享签名</string>
<string name="title_share_key">分享密钥</string>
@@ -26,21 +28,39 @@
<string name="title_key_details">密钥详情</string>
<string name="title_help">帮助</string>
<string name="title_log_display">日志</string>
- <string name="title_advanced_key_info">更多密钥详情</string>
+ <string name="title_exchange_keys">交换密钥</string>
+ <string name="title_advanced_key_info">更多信息</string>
+ <string name="title_delete_secret_key">删除你的密钥 \'%s\' ?</string>
<string name="title_export_log">导出日志</string>
+ <string name="title_manage_my_keys">管理我的密钥</string>
<!--section-->
<string name="section_user_ids">用户名</string>
+ <string name="section_yubikey">YubiKey</string>
+ <string name="section_linked_system_contact">关联系统联系人</string>
<string name="section_should_you_trust">应该相信此密钥?</string>
+ <string name="section_proof_details">验证</string>
+ <string name="section_cloud_evidence">云端验证</string>
<string name="section_keys">子密钥</string>
<string name="section_cloud_search">在线搜索</string>
- <string name="section_passphrase_cache">密语缓存</string>
+ <string name="section_passphrase_cache">密码缓存</string>
+ <string name="section_proxy_settings">代理服务器设置</string>
+ <string name="section_gui">界面</string>
<string name="section_certify">确认</string>
+ <string name="section_actions">动作</string>
+ <string name="section_share_key">分享密钥</string>
<string name="section_key_server">密钥服务器</string>
<string name="section_fingerprint">签名</string>
+ <string name="section_encrypt">加密</string>
+ <string name="section_decrypt">解密/验证</string>
+ <string name="section_current_expiry">当前有效期</string>
+ <string name="section_new_expiry">新有效期</string>
<!--button-->
<string name="btn_decrypt_verify_file">解密并验证文件</string>
<string name="btn_encrypt_share_file">加密并分享文件</string>
+ <string name="btn_encrypt_save_file">加密至文件</string>
+ <string name="btn_save_file">保存文件</string>
<string name="btn_save">保存</string>
+ <string name="btn_view_log">查看日志</string>
<string name="btn_do_not_save">取消</string>
<string name="btn_delete">删除</string>
<string name="btn_no_date">没有日期</string>
@@ -48,38 +68,88 @@
<string name="btn_export_to_server">导出到服务器</string>
<string name="btn_next">下一步</string>
<string name="btn_back">返回</string>
+ <string name="btn_no">否</string>
+ <string name="btn_match">密钥指纹符合</string>
+ <string name="btn_share_encrypted_signed">加密并分享</string>
+ <string name="btn_copy_encrypted_signed">加密并复制</string>
<string name="btn_view_cert_key">显示密钥</string>
<string name="btn_create_key">创建密钥</string>
<string name="btn_add_files">添加密钥</string>
- <string name="btn_add_share_decrypted_text">添加并分享加密后的文本</string>
- <string name="btn_decrypt_and_verify">解密并验证</string>
- <string name="btn_decrypt_files">解密文件</string>
+ <string name="btn_share_decrypted_text">分享解密文本</string>
+ <string name="btn_copy_decrypted_text">复制解密文本</string>
+ <string name="btn_decrypt_clipboard">从剪贴板导入</string>
+ <string name="btn_decrypt_files">选择导入文件</string>
+ <string name="btn_encrypt_files">加密文件</string>
+ <string name="btn_encrypt_text">加密文本</string>
+ <string name="btn_add_email">添加额外的邮件地址</string>
+ <string name="btn_unlock">解锁</string>
+ <string name="btn_add_keyserver">添加</string>
+ <string name="btn_save_default">保存为默认值</string>
+ <string name="btn_saved">已保存</string>
<!--menu-->
<string name="menu_preferences">参数设置</string>
<string name="menu_help">帮助</string>
- <string name="menu_export_key">导出密钥</string>
+ <string name="menu_export_key">导出到文件</string>
<string name="menu_delete_key">删除密钥</string>
+ <string name="menu_manage_keys">管理我的密钥</string>
<string name="menu_search">搜索</string>
+ <string name="menu_nfc_preferences">NFC设置</string>
<string name="menu_beam_preferences">参数</string>
<string name="menu_encrypt_to">加密到...</string>
<string name="menu_select_all">选择全部</string>
<string name="menu_export_all_keys">导出全部密钥</string>
- <string name="menu_advanced">高级</string>
+ <string name="menu_update_all_keys">更新所有密钥</string>
+ <string name="menu_advanced">更多信息</string>
+ <string name="menu_certify_fingerprint">通过密钥指纹比较确认</string>
+ <string name="menu_export_log">导出日志</string>
+ <string name="menu_keyserver_add">添加</string>
<!--label-->
+ <string name="label_message">文本</string>
<string name="label_file">文件</string>
<string name="label_files">多个文件</string>
+ <string name="label_file_colon">文件:</string>
<string name="label_no_passphrase">没有密码</string>
<string name="label_passphrase">密码</string>
- <string name="label_passphrase_again">确认密码</string>
+ <string name="label_unlock">解锁中...</string>
+ <string name="label_passphrase_again">重复密码</string>
+ <string name="label_show_passphrase">显示密码</string>
<string name="label_algorithm">算法</string>
+ <string name="label_ascii_armor">文件ASCII文本化</string>
+ <string name="label_file_ascii_armor">启用ASCII文本化</string>
<string name="label_write_version_header">写入文件头信息</string>
+ <string name="label_write_version_header_summary">在OpenPGP签名、加密文本和导出密钥中写入 \'OpenKeychain v2.7\' 标记。</string>
+ <string name="label_use_default_yubikey_pin">使用默认YubiKey PIN</string>
+ <string name="label_use_num_keypad_for_yubikey_pin">为YubiKey PIN使用数字键盘</string>
+ <string name="label_label_use_default_yubikey_pin_summary">使用默认PIN (123456)访问YubiKeys</string>
+ <string name="label_message_compression">文本压缩</string>
+ <string name="label_file_compression">文件压缩</string>
+ <string name="label_keyservers">选择OpenPGP 密钥服务器</string>
+ <string name="label_key_id">密钥ID</string>
+ <string name="label_expiry">过期时间</string>
+ <string name="label_usage">用途</string>
+ <string name="label_key_size">密钥大小</string>
<string name="label_main_user_id">用户名</string>
<string name="label_name">姓名</string>
<string name="label_comment">注解</string>
<string name="label_email">电子邮件</string>
<string name="label_fingerprint">指纹</string>
<string name="expiry_date_dialog_title">有效期</string>
- <string name="label_first_keyserver_is_used">首选服务器</string>
+ <string name="label_encrypt_filenames">加密文件名</string>
+ <string name="pref_keyserver">OpenPGP 密钥服务器</string>
+ <!--label shown in Android settings under the OpenKeychain account-->
+ <!--Proxy Preferences-->
+ <string name="pref_proxy_normal_title">启用其它代理服务器</string>
+ <string name="pref_proxy_host_title">服务器地址</string>
+ <string name="pref_proxy_host_err_invalid">服务器地址不能为空</string>
+ <string name="pref_proxy_port_title">端口</string>
+ <string name="pref_proxy_port_err_invalid">不正确的端口</string>
+ <string name="pref_proxy_type_title">类型</string>
+ <!--proxy type choices and values-->
+ <string name="pref_proxy_type_choice_http">HTTP</string>
+ <string name="pref_proxy_type_choice_socks">SOCKS</string>
+ <!--OrbotHelper strings-->
+ <!--InstallDialogFragment strings-->
+ <!--StartOrbotDialogFragment strings-->
<plurals name="n_keys">
<item quantity="other">其他</item>
</plurals>
@@ -101,21 +171,27 @@
<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="ecdh">ECDH</string>
+ <string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">打开...</string>
<string name="error">错误</string>
+ <string name="error_message">错误:%s</string>
<!--key flags-->
+ <string name="flag_certify">验证</string>
+ <string name="flag_sign">签名</string>
+ <string name="flag_encrypt">加密</string>
<!--sentences-->
<string name="wrong_passphrase">密码错误</string>
<string name="no_filemanager_installed">安装了不匹配的文件管理器</string>
<string name="passphrases_do_not_match">密码不匹配</string>
- <string name="passphrase_must_not_be_empty">密码不能为空</string>
+ <string name="passphrase_must_not_be_empty">请输入一个密码</string>
<string name="passphrase_for_symmetric_encryption">对称加密</string>
- <string name="passphrase_for">密码</string>
- <string name="no_file_selected">先选择一个文件</string>
<string name="encrypt_sign_successful">加密并签名成功</string>
<string name="encrypt_sign_clipboard_successful">加密签名并复制到剪贴板成功</string>
<string name="select_encryption_key">选择至少一个加密密钥</string>
- <string name="select_encryption_or_signature_key">选择至少一个加密密钥或者签名密钥</string>
<string name="also_export_secret_keys">同时导出密钥</string>
<string name="key_exported">成功地导出了1个密钥</string>
<string name="keys_exported">成功导出多个密钥</string>
@@ -125,24 +201,24 @@
<item quantity="other">其他</item>
</plurals>
<string name="list_empty">这个列表是空的!</string>
- <string name="nfc_successful">NFC成功</string>
+ <string name="nfc_successful">成功通过NFC发送密钥</string>
<string name="key_copied_to_clipboard">复制密钥到剪贴板</string>
<string name="fingerprint_copied_to_clipboard">复制签名到剪贴板</string>
+ <string name="select_key_to_certify">选择验证密钥</string>
<string name="key_too_big_for_sharing">密钥太长</string>
<string name="text_copied_to_clipboard">复制文本到剪贴板</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">还未被删除. 手动删除它们!</string>
+ <string name="error_file_added_already">%s 已经被添加了</string>
<string name="error_file_not_found">没有找到文件</string>
<string name="error_no_secret_key_found">没有找到私钥</string>
<string name="error_external_storage_not_ready">外置存储没有准备好</string>
<string name="error_key_size_minimum512bit">密钥的大小必须至少512位</string>
<string name="error_unknown_algorithm_choice">位置的算法选择</string>
- <string name="error_user_id_no_email">缺少电子邮件</string>
- <string name="error_no_signature_passphrase">没有提供密码</string>
<string name="error_no_signature_key">没有提供签名密钥</string>
<string name="error_integrity_check_failed">检查失败</string>
- <string name="error_wrong_passphrase">错误的密语</string>
<!--errors without preceeding Error:-->
<!--results shown after decryption/verification-->
<!--Add keys-->
@@ -175,14 +251,14 @@
<string name="help_tab_about">关于</string>
<string name="help_about_version">版本:</string>
<!--Import-->
+ <!--Import from URL-->
<!--Generic result toast-->
<!--Import result toast-->
<!--Delete result toast-->
+ <!--Revoke result toast (snackbar)-->
<!--Certify result toast-->
<!--Intent labels-->
<!--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>
@@ -193,6 +269,9 @@
<string name="api_select_pub_keys_text">请重审收件人列表</string>
<!--Share-->
<string name="share_nfc_dialog">使用NFC分享</string>
+ <!--retry upload dialog-->
+ <!--Delete or revoke private key dialog-->
+ <!--Delete Or Revoke Dialog spinner-->
<!--Key list-->
<!--Key view-->
<!--Key trust-->
@@ -200,6 +279,7 @@
<!--Edit key-->
<!--Create key-->
<!--View key-->
+ <!--Add/Edit keyserver-->
<!--Navigation Drawer-->
<!--hints-->
<!--certs-->
@@ -218,12 +298,15 @@
<!--Messages for VerifySignedLiteralData operation-->
<!--Messages for SignEncrypt operation-->
<!--Messages for PgpSignEncrypt operation-->
+ <!--Messages for Keybase Verification operation-->
<!--Messages for Export Log operation-->
<!--PassphraseCache-->
+ <!--Keyserver sync-->
<!--First Time-->
<!--unsorted-->
+ <!--Android Account-->
<!--Passphrase wizard-->
<!--TODO: rename all the things!-->
- <!--<string name="enter_passphrase_twice">Enter passphrase twice</string>-->
+ <!--<string name="enter_passphrase_twice">Enter password twice</string>-->
<!--<string name="nfc_text">Please place a NFC tag near your device</string>-->
</resources>
diff --git a/OpenKeychain/src/main/res/values/arrays.xml b/OpenKeychain/src/main/res/values/arrays.xml
index 44bbe00cc..28b6fcd78 100644
--- a/OpenKeychain/src/main/res/values/arrays.xml
+++ b/OpenKeychain/src/main/res/values/arrays.xml
@@ -29,6 +29,18 @@
<item>28800</item>
<item>-1</item>
</string-array>
+ <string-array name="pref_proxy_type_entries" translatable="false">
+ <item>@string/pref_proxy_type_choice_http</item>
+ <item>@string/pref_proxy_type_choice_socks</item>
+ </string-array>
+ <string-array name="pref_proxy_type_values" translatable="false">
+ <item>"proxyHttp"</item>
+ <item>"proxySocks"</item>
+ </string-array>
+ <string-array name="rev_del_dialog_entries" translatable="true">
+ <item>@string/del_rev_dialog_choice_rev_upload</item>
+ <item>@string/del_rev_dialog_choice_delete</item>
+ </string-array>
<string-array name="rsa_key_size_spinner_values" translatable="false">
<item>@string/key_size_2048</item>
<item>@string/key_size_4096</item>
@@ -49,4 +61,12 @@
<item>@string/key_size_custom</item>
</string-array>
+ <string-array name="theme_entries" translatable="false">
+ <item>@string/theme_dark</item>
+ <item>@string/theme_light</item>
+ </string-array>
+ <string-array name="theme_values" translatable="false">
+ <item>"dark"</item>
+ <item>"light"</item>
+ </string-array>
</resources>
diff --git a/OpenKeychain/src/main/res/values/attr.xml b/OpenKeychain/src/main/res/values/attr.xml
index 74584a1e5..1a414e6d9 100644
--- a/OpenKeychain/src/main/res/values/attr.xml
+++ b/OpenKeychain/src/main/res/values/attr.xml
@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
+ <declare-styleable name="ToolableViewAnimator">
+ <attr name="initialView" format="integer" />
+ </declare-styleable>
+
<declare-styleable name="FoldableLinearLayout">
<attr name="foldedLabel" format="string" />
<attr name="unFoldedLabel" format="string" />
diff --git a/OpenKeychain/src/main/res/values/attrs.xml b/OpenKeychain/src/main/res/values/attrs.xml
new file mode 100644
index 000000000..b36c6ae3e
--- /dev/null
+++ b/OpenKeychain/src/main/res/values/attrs.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <declare-styleable name="CustomTheme">
+ <attr name="colorFab" format="color" />
+ <attr name="colorFabPressed" format="color" />
+ <attr name="fabLabelBackgroundDrawable" format="reference" />
+ <attr name="colorFabText" format="color" />
+ <attr name="colorEmphasis" format="color" />
+ <attr name="colorHeaderText" format="color" />
+ <attr name="colorTertiaryText" format="color" />
+ <attr name="colorButtonRow" format="color" />
+ <attr name="colorLogBackground" format="color" />
+ <attr name="cardViewHeaderDrawable" format="reference" />
+ <attr name="colorText" format="color" />
+ <attr name="colorBrightToolbar" format="color" />
+ <attr name="colorCardViewBackground" format="color" />
+ <attr name="colorTabText" format="color" />
+ <attr name="colorTabTextSelected" format="color" />
+ <attr name="colorTabIndicator" format="color" />
+ <attr name="sectionHeaderDrawable" format="reference" />
+ </declare-styleable>
+</resources>
diff --git a/OpenKeychain/src/main/res/values/colors.xml b/OpenKeychain/src/main/res/values/colors.xml
index ead006a63..06fe2a9cd 100644
--- a/OpenKeychain/src/main/res/values/colors.xml
+++ b/OpenKeychain/src/main/res/values/colors.xml
@@ -1,54 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <!-- Main theme colors -->
-
- <!-- green colors from OpenKeychain logo bottom right -->
- <!-- your app branding color for the app bar -->
<color name="primary">#7bad45</color>
- <!-- darker variant for the status bar and contextual app bars -->
- <color name="primary_dark">#6c983d</color>
- <!-- theme UI controls like checkboxes and text fields -->
- <color name="accent">#2196F3</color>
-
- <!-- Other colors -->
-
- <color name="black">#000000</color>
-
- <color name="primary_light">#C8E6C9</color>
- <color name="fab">@color/accent</color>
- <color name="fab_pressed">#1976D2</color>
- <color name="primary_text">#212121</color>
- <color name="secondary_text">#727272</color>
- <color name="icons">#FFFFFF</color>
- <color name="divider">#B6B6B6</color>
- <color name="transparent">#00FFFFFF</color>
- <color name="header_text">#212121</color>
- <!-- item selection, search highlight -->
- <color name="emphasis">@color/accent</color>
+ <color name="icons">#ffffff</color>
+ <color name="transparent">#00ffffff</color>
- <color name="bg_gray">#cecbce</color>
- <color name="tertiary_text_light">#808080</color>
- <color name="alert">#ffdd3333</color>
+ <color name="key_flag_gray">#808080</color>
+ <color name="key_flag_red">#f44336</color>
+ <color name="key_flag_orange">#ff9800</color>
+ <color name="key_flag_green">#7bad45</color>
- <color name="holo_gray_light">#33999999</color>
- <color name="holo_gray_bright">#33CCCCCC</color>
-
- <!-- tabs -->
- <color name="tab_text">#70FFFFFF</color>
- <color name="tab_text_selected">#FFFFFF</color>
- <color name="tab_indicator">#FFFFFF</color>
-
-
- <!-- floating action buttons -->
- <color name="black_semi_transparent">#B2000000</color>
- <color name="background">#e5e5e5</color>
- <color name="half_black">#808080</color>
- <color name="white">#fafafa</color>
- <color name="white_pressed">#f1f1f1</color>
+ <color name="password_strength_low">#f44336</color>
+ <color name="password_strength_medium">#ff9800</color>
+ <color name="password_strength_high">#7bad45</color>
<!--
+ Standard Android colors:
http://www.google.com/design/spec/style/color.html#color-color-palette
light = normal color
dark = 900
@@ -58,10 +26,11 @@
<color name="android_red_light">#f44336</color>
<color name="android_red_dark">#b71c1c</color>
<color name="android_orange_light">#ff9800</color>
- <color name="android_orange_dark">#e65100</color>
- <color name="android_green_light">@color/primary</color>
- <color name="android_green_dark">@color/primary_dark</color>
- <color name="android_purple_light">#673ab7</color>
- <color name="android_purple_dark">#311b92</color>
+ <color name="android_green_light">#7bad45</color>
+ <color name="android_green_dark">#6c983d</color>
+
+ <!-- text protection scrim colors -->
+ <color name="translucent_scrim_bottom">#2A000000</color>
+ <color name="translucent_scrim_bottom_center">#2A000000</color>
</resources>
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index 3de83f9d5..df360bb49 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -17,7 +17,7 @@
<string name="title_linked_create">"Create a Linked Identity"</string>
<string name="title_preferences">"Settings"</string>
<string name="title_api_registered_apps">"Apps"</string>
- <string name="title_key_server_preference">"Keyservers"</string>
+ <string name="title_key_server_preference">"OpenPGP keyservers"</string>
<string name="title_change_passphrase">"Change Password"</string>
<string name="title_share_fingerprint_with">"Share fingerprint with…"</string>
<string name="title_share_key">"Share key with…"</string>
@@ -26,8 +26,8 @@
<string name="title_encrypt_to_file">"Encrypt To File"</string>
<string name="title_decrypt_to_file">"Decrypt To File"</string>
<string name="title_import_keys">"Import Keys"</string>
- <string name="title_export_key">"Export Key"</string>
- <string name="title_export_keys">"Export Keys"</string>
+ <string name="title_export_key">"Backup Key"</string>
+ <string name="title_export_keys">"Backup Keys"</string>
<string name="title_key_not_found">"Key Not Found"</string>
<string name="title_send_key">"Upload to Keyserver"</string>
<string name="title_certify_key">"Confirm Key"</string>
@@ -44,19 +44,27 @@
<string name="section_user_ids">"Identities"</string>
<string name="section_yubikey">"YubiKey"</string>
<string name="section_linked_system_contact">"Linked System Contact"</string>
+ <string name="section_keybase_proofs">"Keybase.io Proofs"</string>
<string name="section_should_you_trust">"Should you trust this key?"</string>
<string name="section_proof_details">Proof verification</string>
- <string name="section_cloud_evidence">"Proofs from the cloud"</string>
<string name="section_keys">"Subkeys"</string>
- <string name="section_cloud_search">"Cloud search"</string>
- <string name="section_passphrase_cache">"Password Cache"</string>
+ <string name="section_cloud_search">"Key Search"</string>
+ <string name="section_cloud_search_summary">"Keyserver, keybase.io"</string>
+ <string name="section_passphrase_cache">"Passwords and PINs"</string>
+ <string name="section_passphrase_cache_summary">"Handling, user interface, remember time"</string>
+ <string name="section_proxy_settings">"Network Anonymity"</string>
+ <string name="section_proxy_settings_summary">"Tor, Proxy Settings"</string>
+ <string name="section_gui">"Interface"</string>
+ <string name="section_sync_settings">"Synchronization"</string>
+ <string name="section_sync_settings_summary">"Automatic key updates, contact linking"</string>
+ <string name="section_experimental_features">"Experimental Features"</string>
<string name="section_certify">"Confirm"</string>
<string name="section_actions">"Actions"</string>
<string name="section_share_key">"Key"</string>
<string name="section_key_server">"Keyserver"</string>
<string name="section_fingerprint">"Fingerprint"</string>
- <string name="section_encrypt">"Encrypt and/or sign"</string>
- <string name="section_decrypt">"Decrypt and verify signatures"</string>
+ <string name="section_encrypt">"Encrypt"</string>
+ <string name="section_decrypt">"Decrypt / Verify"</string>
<string name="section_current_expiry">"Current expiry"</string>
<string name="section_new_expiry">"New expiry"</string>
@@ -64,7 +72,9 @@
<string name="btn_decrypt_verify_file">"Decrypt, verify, and save file"</string>
<string name="btn_encrypt_share_file">"Encrypt and share file"</string>
<string name="btn_encrypt_save_file">"Encrypt and save file"</string>
+ <string name="btn_save_file">"Save file"</string>
<string name="btn_save">"Save"</string>
+ <string name="btn_view_log">"View log"</string>
<string name="btn_do_not_save">"Cancel"</string>
<string name="btn_delete">"Delete"</string>
<string name="btn_no_date">"No Expiry"</string>
@@ -81,19 +91,20 @@
<string name="btn_add_files">"Add file(s)"</string>
<string name="btn_share_decrypted_text">"Share decrypted text"</string>
<string name="btn_copy_decrypted_text">"Copy decrypted text"</string>
- <string name="btn_decrypt_clipboard">"Decrypt text from clipboard"</string>
- <string name="btn_decrypt_and_verify">""</string>
- <string name="btn_decrypt_files">"Decrypt files"</string>
+ <string name="btn_decrypt_clipboard">"Read from clipboard"</string>
+ <string name="btn_decrypt_files">"Select input file"</string>
<string name="btn_encrypt_files">"Encrypt files"</string>
<string name="btn_encrypt_text">"Encrypt text"</string>
<string name="btn_add_email">"Add additional email address"</string>
<string name="btn_unlock">"Unlock"</string>
<string name="btn_add_keyserver">"Add"</string>
+ <string name="btn_save_default">"Save as default"</string>
+ <string name="btn_saved">"Saved!"</string>
<!-- menu -->
<string name="menu_preferences">"Settings"</string>
<string name="menu_help">"Help"</string>
- <string name="menu_export_key">"Export to file"</string>
+ <string name="menu_export_key">"Backup to file"</string>
<string name="menu_delete_key">"Delete key"</string>
<string name="menu_manage_keys">"Manage my keys"</string>
<string name="menu_search">"Search"</string>
@@ -104,9 +115,12 @@
<string name="menu_export_all_keys">"Export all keys"</string>
<string name="menu_update_all_keys">"Update all keys"</string>
<string name="menu_advanced">"Extended information"</string>
- <string name="menu_certify_fingerprint">"Confirm via fingerprint comparison"</string>
+ <string name="menu_certify_fingerprint">"Confirm via fingerprint"</string>
+ <string name="menu_certify_fingerprint_word">"Confirm via words"</string>
<string name="menu_export_log">"Export Log"</string>
+ <string name="menu_keyserver_add">"Add"</string>
+
<!-- label -->
<string name="label_message">"Text"</string>
<string name="label_file">"File"</string>
@@ -125,18 +139,18 @@
<string name="label_use_default_yubikey_pin">"Use default YubiKey PIN"</string>
<string name="label_use_num_keypad_for_yubikey_pin">Use number keypad for YubiKey PIN</string>
<string name="label_label_use_default_yubikey_pin_summary">"Uses default PIN (123456) to access YubiKeys over NFC"</string>
- <string name="label_asymmetric_from">"Signed by:"</string>
+ <string name="label_asymmetric_from">"Sign with:"</string>
<string name="label_to">"Encrypt to:"</string>
<string name="label_delete_after_encryption">"Delete files after encryption"</string>
<string name="label_delete_after_decryption">"Delete after decryption"</string>
<string name="label_encryption_algorithm">"Encryption algorithm"</string>
<string name="label_hash_algorithm">"Hash algorithm"</string>
<string name="label_symmetric">"Encrypt with password"</string>
- <string name="label_passphrase_cache_ttl">"Cache time"</string>
- <string name="label_passphrase_cache_subs">"Cache passwords by subkey"</string>
+ <string name="label_passphrase_cache_ttl">"Remember time"</string>
+ <string name="label_passphrase_cache_subs">"Remember passwords by subkey"</string>
<string name="label_message_compression">"Text compression"</string>
<string name="label_file_compression">"File compression"</string>
- <string name="label_keyservers">"Keyservers"</string>
+ <string name="label_keyservers">"Select OpenPGP keyservers"</string>
<string name="label_key_id">"Key ID"</string>
<string name="label_key_created">"Key created %s"</string>
<string name="label_creation">"Creation"</string>
@@ -148,24 +162,80 @@
<string name="label_name">"Name"</string>
<string name="label_comment">"Comment"</string>
<string name="label_email">"Email"</string>
- <string name="label_send_key">"Synchronize with the cloud"</string>
+ <string name="label_send_key">"Synchronize with the Internet"</string>
<string name="label_fingerprint">"Fingerprint"</string>
<string name="expiry_date_dialog_title">"Set expiry date"</string>
- <string name="label_first_keyserver_is_used">"(First keyserver listed is preferred)"</string>
+ <string name="label_keyservers_title">"Keyservers"</string>
+ <string name="label_keyserver_settings_hint">"Drag to change order, tap to edit/delete"</string>
+ <string name="label_selected_keyserver_title">"Selected keyserver"</string>
<string name="label_preferred">"preferred"</string>
<string name="label_enable_compression">"Enable compression"</string>
<string name="label_encrypt_filenames">"Encrypt filenames"</string>
<string name="label_hidden_recipients">"Hide recipients"</string>
- <string name="label_verify_keyserver">"Verify Keyserver"</string>
- <string name="label_enter_keyserver_url">"Enter Keyserver URL"</string>
-
- <string name="pref_keyserver">"Search Keyserver"</string>
- <string name="pref_keyserver_summary">"Search HKP keyserver"</string>
- <string name="pref_keybase">"Search Keybase.io"</string>
- <string name="pref_keybase_summary">"Search Keybase.io index"</string>
- <string name="user_id_no_name">"&lt;no name&gt;"</string>
- <string name="none">"&lt;none&gt;"</string>
+ <string name="label_verify_keyserver">"Verify keyserver"</string>
+ <string name="label_enter_keyserver_url">"Enter keyserver URL"</string>
+ <string name="label_keyserver_dialog_delete">"Delete keyserver"</string>
+ <string name="label_theme">"Theme"</string>
+
+ <string name="pref_keyserver">"OpenPGP keyservers"</string>
+ <string name="pref_keyserver_summary">"Search keys on selected OpenPGP keyservers (HKP protocol)"</string>
+ <string name="pref_keybase">"keybase.io"</string>
+ <string name="pref_keybase_summary">"Search keys on keybase.io"</string>
+
+ <string name="label_sync_settings_keyserver_title">"Automatically update keys"</string>
+ <string name="label_sync_settings_keyserver_summary_on">"Keys older than a week are updated from the preferred keyserver"</string>
+ <string name="label_sync_settings_keyserver_summary_off">"Keys not automatically updated"</string>
+ <string name="label_sync_settings_contacts_title">"Link keys to contacts"</string>
+ <string name="label_sync_settings_contacts_summary_on">"Link keys to contacts based on names and email addresses. This happens completely offline on your device."</string>
+ <string name="label_sync_settings_contacts_summary_off">"New keys will not be linked to contacts"</string>
+ <!-- label shown in Android settings under the OpenKeychain account -->
+ <string name="keyserver_sync_settings_title">"Automatically update keys"</string>
+
+ <string name="label_experimental_settings_desc_summary">"These features are mostly results of security/UX research. Don't rely on their security!"</string>
+
+ <string name="label_experimental_settings_word_confirm_title">"Word Confirm"</string>
+ <string name="label_experimental_settings_word_confirm_summary">"Allows to compare fingerprints with words instead of hexadecimal representation."</string>
+ <string name="label_experimental_settings_linked_identities_title">"Linked Identities"</string>
+ <string name="label_experimental_settings_linked_identities_summary">"Linked Identities"</string>
+ <string name="label_experimental_settings_keybase_title">"Keybase.io Proofs"</string>
+ <string name="label_experimental_settings_keybase_summary">"Every time a key is displayed, this will contact keybase.io for key proofs"</string>
+
+ <!-- Proxy Preferences -->
+ <string name="pref_proxy_tor_title">"Enable Tor"</string>
+ <string name="pref_proxy_tor_summary">"Requires Orbot to be installed"</string>
+ <string name="pref_proxy_normal_title">"Enable other proxy"</string>
+ <string name="pref_proxy_host_title">"Proxy Host"</string>
+ <string name="pref_proxy_host_err_invalid">"Proxy host cannot be empty"</string>
+ <string name="pref_proxy_port_title">"Proxy Port"</string>
+ <string name="pref_proxy_port_err_invalid">"Invalid port number entered"</string>
+ <string name="pref_proxy_type_title">"Proxy Type"</string>
+
+ <!-- proxy type choices and values -->
+ <string name="pref_proxy_type_choice_http">"HTTP"</string>
+ <string name="pref_proxy_type_choice_socks">"SOCKS"</string>
+
+ <!-- OrbotHelper strings -->
+ <string name="orbot_ignore_tor">"Don\'t use Tor"</string>
+
+ <!-- InstallDialogFragment strings -->
+ <string name="orbot_install_dialog_title">Install Orbot to use Tor?</string>
+ <string name="orbot_install_dialog_install">"Install"</string>
+ <string name="orbot_install_dialog_content">You must have Orbot installed and activated to proxy traffic through it. Would you like to install it?</string>
+ <string name="orbot_install_dialog_cancel">"Cancel"</string>
+ <string name="orbot_install_dialog_ignore_tor">"Don\'t use Tor"</string>
+
+ <!-- StartOrbotDialogFragment strings -->
+ <string name="orbot_start_dialog_title">Start Orbot?</string>
+ <string name="orbot_start_dialog_content">"Orbot doesn\'t appear to be running. Would you like to start it up and connect to Tor?"</string>
+ <string name="orbot_start_btn">"Start Orbot"</string>
+ <string name="orbot_start_dialog_start">"Start Orbot"</string>
+ <string name="orbot_start_dialog_cancel">"Cancel"</string>
+ <string name="orbot_start_dialog_ignore_tor">"Don\'t use Tor"</string>
+
+
+ <string name="user_id_no_name"><![CDATA[<no name>]]></string>
+ <string name="none"><![CDATA[<none>]]></string>
<plurals name="n_keys">
<item quantity="one">"1 key"</item>
@@ -202,6 +272,8 @@
<string name="filemanager_title_open">"Open…"</string>
<string name="error">"Error"</string>
<string name="error_message">"Error: %s"</string>
+ <string name="theme_dark">"Dark"</string>
+ <string name="theme_light">"Light"</string>
<!-- key flags -->
<string name="flag_certify">"Certify"</string>
@@ -218,18 +290,24 @@
<string name="passphrase_for">"Enter password for '%s'"</string>
<string name="pin_for">"Enter PIN for '%s'"</string>
<string name="yubikey_pin_for">"Enter PIN to access YubiKey for '%s'"</string>
- <string name="nfc_text">"Hold YubiKey against the back of your device."</string>
+ <string name="nfc_text">"Hold YubiKey against the NFC marker at the back of your device."</string>
+ <string name="nfc_wait">"Keep the YubiKey at the back!"</string>
+ <string name="nfc_finished">"Take away the YubiKey now."</string>
+ <string name="nfc_try_again_text">"Take away the YubiKey now and press TRY AGAIN."</string>
<string name="file_delete_confirmation_title">"Delete original files?"</string>
<string name="file_delete_confirmation">"The following files will be deleted:%s"</string>
<string name="file_delete_successful">"%1$d out of %2$d files have been deleted.%3$s"</string>
- <string name="no_file_selected">"Select a file first."</string>
+ <string name="no_file_selected">"No file selected."</string>
<string name="encrypt_sign_successful">"Successfully signed and/or encrypted."</string>
<string name="encrypt_sign_clipboard_successful">"Successfully signed and/or encrypted to clipboard."</string>
<string name="select_encryption_key">"Select at least one encryption key."</string>
- <string name="select_encryption_or_signature_key">"Select at least one encryption key or a signature key."</string>
+ <string name="error_no_encryption_or_signature_key">"Select at least one encryption key or a signature key."</string>
<string name="specify_file_to_encrypt_to">"Please specify which file to encrypt to.\nWARNING: File will be overwritten if it exists!"</string>
<string name="specify_file_to_decrypt_to">"Please specify which file to decrypt to.\nWARNING: File will be overwritten if it exists!"</string>
- <string name="specify_file_to_export_to">"Please specify which file to export to.\nWARNING: File will be overwritten if it exists!"</string>
+ <string name="specify_backup_dest">"A backup excluding your keys will be made, please specify a destination file.\nWARNING: File will be overwritten if it exists!"</string>
+ <string name="specify_backup_dest_single">"This key will be shared, please specify a destination file.\nWARNING: File will be overwritten if it exists!"</string>
+ <string name="specify_backup_dest_secret_single">"A full backup of your key will be made, please specify a destination file.\nWARNING: File will be overwritten if it exists!"</string>
+ <string name="specify_backup_dest_secret">"A full backup of all keys including yours will be made, please specify a destination file.\nWARNING: File will be overwritten if it exists!"</string>
<string name="key_deletion_confirmation_multi">"Do you really want to delete all selected keys?"</string>
<string name="secret_key_deletion_confirmation">"After deletion you will not be able to read messages encrypted with this key and lose all key confirmations done with it!"</string>
<string name="public_key_deletetion_confirmation">"Delete key '%s'?"</string>
@@ -283,12 +361,14 @@
<string name="error_nfc_needed">"NFC must be enabled!"</string>
<string name="error_beam_needed">"Beam must be enabled!"</string>
<string name="error_nothing_import">"No keys found!"</string>
+ <string name="error_nothing_import_selected">"No keys selected for import!"</string>
<string name="error_contacts_key_id_missing">"Retrieving the key ID from contacts failed!"</string>
<string name="error_generic_report_bug">"A generic error occurred, please create a new bug report for OpenKeychain."</string>
<!-- results shown after decryption/verification -->
<string name="decrypt_result_no_signature">"Not Signed"</string>
<string name="decrypt_result_invalid_signature">"Invalid signature!"</string>
+ <string name="decrypt_result_insecure_cryptography">"Invalid signature (Insecure Cryptography)!"</string>
<string name="decrypt_result_signature_uncertified">"Signed by <b>unconfirmed</b> key"</string>
<string name="decrypt_result_signature_secret">"Signed by your key"</string>
<string name="decrypt_result_signature_certified">"Signed by confirmed key"</string>
@@ -297,6 +377,7 @@
<string name="decrypt_result_signature_missing_key">"Signed by <b>unknown public key</b>"</string>
<string name="decrypt_result_encrypted">"Encrypted"</string>
<string name="decrypt_result_not_encrypted">"Not Encrypted"</string>
+ <string name="decrypt_result_insecure">"Insecure Encryption"</string>
<string name="decrypt_result_action_show">"Show"</string>
<string name="decrypt_result_action_Lookup">"Lookup"</string>
<string name="decrypt_invalid_text">"Either the signature is invalid or the key has been revoked. You cannot be sure who wrote the text. Do you still want to display it?"</string>
@@ -311,6 +392,7 @@
<string name="progress_cancelling">"cancelling…"</string>
<string name="progress_saving">"saving…"</string>
<string name="progress_importing">"importing…"</string>
+ <string name="progress_revoking_uploading">"Revoking and uploading key…"</string>
<string name="progress_updating">"Updating keys…"</string>
<string name="progress_exporting">"exporting…"</string>
<string name="progress_uploading">"uploading…"</string>
@@ -333,13 +415,16 @@
<string name="progress_modify_subkeyrevoke">"revoking subkeys…"</string>
<string name="progress_modify_subkeystrip">"stripping subkeys…"</string>
<string name="progress_modify_subkeyadd">"adding subkeys…"</string>
- <string name="progress_modify_passphrase">"changing passwords…"</string>
+ <string name="progress_modify_passphrase">"changing password…"</string>
+ <string name="progress_modify_pin">"changing PIN…"</string>
+ <string name="progress_modify_admin_pin">"changing Admin PIN…"</string>
<plurals name="progress_exporting_key">
<item quantity="one">"exporting key…"</item>
<item quantity="other">"exporting keys…"</item>
</plurals>
+ <string name="progress_start">"preparing operation…"</string>
<string name="progress_extracting_signature_key">"extracting signature key…"</string>
<string name="progress_extracting_key">"extracting key…"</string>
<string name="progress_preparing_streams">"preparing streams…"</string>
@@ -363,6 +448,8 @@
<string name="progress_verifying_keyserver_url">"verifying keyserver…"</string>
+ <string name="progress_starting_orbot">"Starting Orbot…"</string>
+
<!-- action strings -->
<string name="hint_cloud_search_hint">"Search via Name, Email…"</string>
@@ -405,20 +492,21 @@
<!-- Import -->
<string name="import_tab_keyserver">"Keyserver"</string>
- <string name="import_tab_cloud">"Search Cloud"</string>
+ <string name="import_tab_cloud">"Key Search"</string>
<string name="import_tab_direct">"File/Clipboard"</string>
<string name="import_tab_qr_code">"QR Code/NFC"</string>
<string name="import_import">"Import selected keys"</string>
<string name="import_qr_code_wrong">"QR Code malformed! Please try again!"</string>
- <string name="import_qr_code_too_short_fingerprint">"Fingerprint is too short (&lt; 16 characters)"</string>
+ <string name="import_qr_code_fp">"Fingerprint is malformed or too short!"</string>
+ <string name="import_qr_code_too_short_fingerprint">"Fingerprint is too short!"</string>
<string name="import_qr_code_button">"Scan QR Code"</string>
<string name="import_qr_code_text">"Place your camera over the QR Code!"</string>
<!-- Import from URL -->
- <string name="import_url_warn_no_search_parameter">"No search parameter found. You may still attempt manually searching the keyserver."</string>
+ <string name="import_url_warn_no_search_parameter">"No search query defined. You can still manually search on this keyserver."</string>
<!-- Generic result toast -->
- <string name="view_log">"Details"</string>
+ <string name="snackbar_details">"Details"</string>
<string name="with_warnings">", with warnings"</string>
<string name="with_cancelled">", until cancelled"</string>
@@ -470,10 +558,16 @@
<string name="delete_nothing">"Nothing to delete."</string>
<string name="delete_cancelled">"Delete operation cancelled."</string>
+ <!-- Revoke result toast (snackbar) -->
+ <string name="revoke_ok">"Successfully revoked key."</string>
+ <string name="revoke_fail">"Error revoking key!"</string>
+ <string name="revoke_nothing">"Nothing to revoke."</string>
+ <string name="revoke_cancelled">"Revoke operation cancelled."</string>
+
<!-- Certify result toast -->
<plurals name="certify_keys_ok">
- <item quantity="one">"Successfully certified key%2$s."</item>
- <item quantity="other">"Successfully certified %1$d keys%2$s."</item>
+ <item quantity="one">"Successfully confirmed key%2$s."</item>
+ <item quantity="other">"Successfully confirmed %1$d keys%2$s."</item>
</plurals>
<plurals name="certify_keys_with_errors">
<item quantity="one">"Certification failed!"</item>
@@ -505,7 +599,7 @@
<string name="api_settings_start">"Start application"</string>
<string name="api_settings_delete_account">"Delete account"</string>
<string name="api_settings_package_name">"Package Name"</string>
- <string name="api_settings_package_signature">"SHA-256 of Package Signature"</string>
+ <string name="api_settings_package_certificate">"SHA-256 of Package Certificate"</string>
<string name="api_settings_accounts">"Accounts (old API)"</string>
<string name="api_settings_advanced">"Extended Information"</string>
<string name="api_settings_allowed_keys">"Allowed Keys"</string>
@@ -518,8 +612,8 @@
<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 keys were found for these identities:"</string>
- <string name="api_select_pub_keys_dublicates_text">"More than one key exist for these identities:"</string>
+ <string name="api_select_pub_keys_missing_text">"No keys were found for these email addresses:"</string>
+ <string name="api_select_pub_keys_dublicates_text">"More than one key exist for these email addresses:"</string>
<string name="api_select_pub_keys_text">"Please review the list of recipients!"</string>
<string name="api_select_pub_keys_text_no_user_ids">"Please select the 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 OpenKeychain and then register the app again."</string>
@@ -530,6 +624,23 @@
<string name="share_qr_code_dialog_title">"Share with QR Code"</string>
<string name="share_nfc_dialog">"Share with NFC"</string>
+ <!-- retry upload dialog -->
+ <string name="retry_up_dialog_title">"Upload failed"</string>
+ <string name="retry_up_dialog_message">"Upload failed. Would you like to retry the operation?"</string>
+ <string name="retry_up_dialog_btn_reupload">"Retry Operation"</string>
+ <string name="retry_up_dialog_btn_cancel">"Cancel Operation"</string>
+
+ <!-- Delete or revoke private key dialog -->
+ <string name="del_rev_dialog_message">"If you would no longer like to use this key, it should be revoked and uploaded. Select 'DELETE ONLY' if you wish to remove the key from OpenKeychain but continue to use it from somewhere else."</string>
+ <string name="del_rev_dialog_title">"Revoke/Delete key '%s'"</string>
+ <string name="del_rev_dialog_btn_revoke">"Revoke and upload"</string>
+ <string name="del_rev_dialog_btn_delete">"Delete only"</string>
+
+ <!-- Delete Or Revoke Dialog spinner -->
+ <string name="del_rev_dialog_choice_delete">"Delete only"</string>
+ <string name="del_rev_dialog_choice_rev_upload">"Revoke and Upload"</string>
+
+
<!-- Key list -->
<plurals name="key_list_selected_keys">
<item quantity="one">"1 key selected."</item>
@@ -538,7 +649,10 @@
<string name="key_list_empty_text1">"No keys found!"</string>
<string name="key_list_filter_show_all">"Show all keys"</string>
- <string name="key_list_filter_show_certified">"Show certified keys only"</string>
+ <string name="key_list_filter_show_certified">"Show confirmed keys only"</string>
+ <string name="key_list_fab_qr_code">"Scan QR Code"</string>
+ <string name="key_list_fab_search">"Key Search"</string>
+ <string name="key_list_fab_import">"Import from File"</string>
<!-- Key view -->
<string name="key_view_action_edit">"Edit key"</string>
@@ -556,34 +670,28 @@
<string name="key_view_tab_keybase">"Keybase.io"</string>
<string name="user_id_info_revoked_title">"Revoked"</string>
<string name="user_id_info_revoked_text">"This identity has been revoked by the key owner. It is no longer valid."</string>
- <string name="user_id_info_certified_title">"Certified"</string>
- <string name="user_id_info_certified_text">"This identity has been certified by you."</string>
- <string name="user_id_info_uncertified_title">"Not certified"</string>
- <string name="user_id_info_uncertified_text">"This identity has not been certified yet. You cannot be sure if the identity really corresponds to a specific person."</string>
+ <string name="user_id_info_certified_title">"Confirmed"</string>
+ <string name="user_id_info_certified_text">"This identity has been confirmed by you."</string>
+ <string name="user_id_info_uncertified_title">"Not confirmed"</string>
+ <string name="user_id_info_uncertified_text">"This identity has not been confirmed yet. You cannot be sure if the identity really corresponds to a specific person."</string>
<string name="user_id_info_invalid_title">"Invalid"</string>
<string name="user_id_info_invalid_text">"Something is wrong with this identity!"</string>
<!-- Key trust -->
- <string name="key_trust_already_verified">"You have already confirmed this key!"</string>
- <string name="key_trust_it_is_yours">"This is one of your keys!"</string>
- <string name="key_trust_maybe">"This key is neither revoked nor expired.\nYou haven’t confirmed it, but you may choose to trust it."</string>
- <string name="key_trust_revoked">"This key has been revoked by its owner. You should not trust it."</string>
- <string name="key_trust_expired">"This key has expired. You should not trust it."</string>
- <string name="key_trust_old_keys">"It may be OK to use this to decrypt an old message dating from the time when this key was valid."</string>
- <string name="key_trust_no_cloud_evidence">"No proof from the cloud on this key’s trustworthiness."</string>
+ <string name="key_trust_no_cloud_evidence">"No proof from the Internet on this key’s trustworthiness."</string>
<string name="key_trust_start_cloud_search">"Start search"</string>
<string name="key_trust_results_prefix">"Keybase.io offers “proofs” which assert that the owner of this key: "</string>
<string name="key_trust_header_text">"Note: Keybase.io proofs are an experimental feature of OpenKeychain. We encourage you to scan QR Codes or exchange keys via NFC in addition to confirming them."</string>
<!-- keybase proof stuff -->
- <string name="keybase_narrative_twitter">"Posts to Twitter as"</string>
- <string name="keybase_narrative_github">"Is known on GitHub as"</string>
- <string name="keybase_narrative_dns">"Controls the domain name(s)"</string>
- <string name="keybase_narrative_web_site">"Can post to the Web site(s)"</string>
- <string name="keybase_narrative_reddit">"Posts to Reddit as"</string>
- <string name="keybase_narrative_coinbase">"Is known on Coinbase as"</string>
- <string name="keybase_narrative_hackernews">"Posts to Hacker News as"</string>
- <string name="keybase_narrative_unknown">"Unknown proof type"</string>
+ <string name="keybase_narrative_twitter">"Posts to Twitter as %s"</string>
+ <string name="keybase_narrative_github">"Is known on GitHub as %s"</string>
+ <string name="keybase_narrative_dns">"Controls the domain name(s) %s"</string>
+ <string name="keybase_narrative_web_site">"Can post to the Web site(s) %s"</string>
+ <string name="keybase_narrative_reddit">"Posts to Reddit as %s"</string>
+ <string name="keybase_narrative_coinbase">"Is known on Coinbase as %s"</string>
+ <string name="keybase_narrative_hackernews">"Posts to Hacker News as %s"</string>
+ <string name="keybase_narrative_unknown">"Unknown proof type %s"</string>
<string name="keybase_proof_failure">"Unfortunately this proof cannot be verified."</string>
<string name="keybase_unknown_proof_failure">"Unrecognized problem with proof checker"</string>
<string name="keybase_problem_fetching_evidence">"Problem with proof"</string>
@@ -623,14 +731,18 @@
<item>"Change Expiry"</item>
<item>"Revoke Subkey"</item>
<item>"Strip Subkey"</item>
+ <item>"Move Subkey to YubiKey / Smart Card"</item>
</string-array>
<string name="edit_key_new_subkey">"new subkey"</string>
<string name="edit_key_select_flag">"Please select at least one flag!"</string>
<string name="edit_key_error_add_identity">"Add at least one identity!"</string>
<string name="edit_key_error_add_subkey">"Add at least one subkey!"</string>
+ <string name="edit_key_error_bad_nfc_algo">"Algorithm not supported by smart card!"</string>
+ <string name="edit_key_error_bad_nfc_size">"Key size not supported by smart card!"</string>
+ <string name="edit_key_error_bad_nfc_stripped">"Cannot move key to smart card (either stripped or 'divert-to-card')!"</string>
<!-- Create key -->
- <string name="create_key_upload">"Synchronize with the cloud"</string>
+ <string name="create_key_upload">"Synchronize with the Internet"</string>
<string name="create_key_empty">"This field is required"</string>
<string name="create_key_passphrases_not_equal">"Passwords do not match"</string>
<string name="create_key_final_text">"You entered the following identity:"</string>
@@ -646,6 +758,13 @@
<string name="create_key_add_email_text">"Additional email addresses are also associated to this key and can be used for secure communication."</string>
<string name="create_key_email_already_exists_text">"Email address has already been added"</string>
<string name="create_key_email_invalid_email">"Email address format is invalid"</string>
+ <string name="create_key_yubi_key_pin_text">"Please remember the PIN, it is required to use your YubiKey later. Please write down the Admin PIN and store it in a safe place."</string>
+ <string name="create_key_yubi_key_pin">"PIN"</string>
+ <string name="create_key_yubi_key_admin_pin">"Admin PIN"</string>
+ <string name="create_key_yubi_key_pin_repeat_text">"Please enter the PIN and Admin PIN to proceed."</string>
+ <string name="create_key_yubi_key_pin_repeat">"Repeat PIN"</string>
+ <string name="create_key_yubi_key_admin_pin_repeat">"Repeat Admin PIN"</string>
+ <string name="create_key_yubi_key_pin_not_correct">"PIN is not correct!"</string>
<!-- View key -->
<string name="view_key_revoked">"Revoked: Key must not be used anymore!"</string>
@@ -657,12 +776,15 @@
<string name="view_key_fragment_no_system_contact">"&lt;none&gt;"</string>
- <!-- Add keyserver -->
- <string name="add_keyserver_dialog_title">"Add Keyserver"</string>
+ <!-- Add/Edit keyserver -->
+ <string name="add_keyserver_dialog_title">"Add keyserver"</string>
+ <string name="edit_keyserver_dialog_title">"Edit keyserver"</string>
<string name="add_keyserver_verified">"Keyserver verified!"</string>
<string name="add_keyserver_without_verification">"Keyserver added without verification."</string>
<string name="add_keyserver_invalid_url">"Invalid URL!"</string>
- <string name="add_keyserver_connection_failed">"Failed to connect to keyserver. Please check the URL and your internet connection."</string>
+ <string name="add_keyserver_connection_failed">"Failed to connect to keyserver. Please check the URL and your Internet connection."</string>
+ <string name="keyserver_preference_deleted">"%s deleted"</string>
+ <string name="keyserver_preference_cannot_delete_last">"Cannot delete last keyserver. At least one is required!"</string>
<!-- Navigation Drawer -->
<string name="nav_keys">"Keys"</string>
@@ -671,6 +793,7 @@
<string name="drawer_open">"Open navigation drawer"</string>
<string name="drawer_close">"Close navigation drawer"</string>
<string name="my_keys">"My Keys"</string>
+ <string name="nav_backup">"Backup"</string>
<!-- hints -->
<string name="encrypt_content_edit_text_hint">"Type text"</string>
@@ -725,7 +848,7 @@
<string name="msg_ip_master_flags_xxxa">"Master flags: authenticate"</string>
<string name="msg_ip_master_flags_xxxx">"Master flags: none"</string>
<string name="msg_ip_merge_public">"Merging imported data into existing public keyring"</string>
- <string name="msg_ip_merge_secret">"Merging imported data into existing public keyring"</string>
+ <string name="msg_ip_merge_secret">"Merging imported data into existing secret keyring"</string>
<string name="msg_ip_subkey">"Processing subkey %s"</string>
<string name="msg_ip_subkey_expired">"Subkey expired on %s"</string>
<string name="msg_ip_subkey_expires">"Subkey expires on %s"</string>
@@ -794,7 +917,7 @@
<string name="msg_is_importing_subkeys">"Processing secret subkeys"</string>
<string name="msg_is_error_io_exc">"Error encoding keyring"</string>
<string name="msg_is_merge_public">"Merging imported data into existing public keyring"</string>
- <string name="msg_is_merge_secret">"Merging imported data into existing public keyring"</string>
+ <string name="msg_is_merge_secret">"Merging imported data into existing secret keyring"</string>
<string name="msg_is_merge_special">"Merging in self-certificates data from public keyring"</string>
<string name="msg_is_pubring_generate">"Generating public keyring from secret keyring"</string>
<string name="msg_is_subkey_nonexistent">"Subkey %s unavailable in secret key"</string>
@@ -802,7 +925,7 @@
<string name="msg_is_subkey_empty">"Marked secret subkey %s as available, with empty password"</string>
<string name="msg_is_subkey_pin">"Marked secret subkey %s as available, with PIN"</string>
<string name="msg_is_subkey_stripped">"Marked secret subkey %s as stripped"</string>
- <string name="msg_is_subkey_divert">"Marked secret subkey %s as 'divert to smartcard/NFC'"</string>
+ <string name="msg_is_subkey_divert">"Marked secret subkey %s as 'divert-to-card'"</string>
<string name="msg_is_success_identical">"Keyring contains no new data, nothing to do"</string>
<string name="msg_is_success">"Successfully imported secret keyring"</string>
@@ -830,6 +953,7 @@
<string name="msg_kc_sub_bad_local">"Removing subkey binding certificate with 'local' flag"</string>
<string name="msg_kc_sub_bad_keyid">"Subkey binding issuer id mismatch"</string>
<string name="msg_kc_sub_bad_time">"Removing subkey binding certificate with future timestamp"</string>
+ <string name="msg_kc_sub_bad_time_early">"Subkey binding certificate has earlier timestamp than its key!"</string>
<string name="msg_kc_sub_bad_type">"Unknown subkey certificate type: %s"</string>
<string name="msg_kc_sub_dup">"Removing redundant subkey binding certificate"</string>
<string name="msg_kc_sub_primary_bad">"Removing subkey binding certificate due to invalid primary binding certificate"</string>
@@ -909,8 +1033,9 @@
<!-- modifySecretKeyRing -->
<string name="msg_mr">"Modifying keyring %s"</string>
- <string name="msg_mf_divert">"Will divert to card/nfc for crypto operations"</string>
- <string name="msg_mf_error_divert_serial">"The serial number of a divert-to-card key must be 16 bytes! This is a programming error, please file a bug report!"</string>
+ <string name="msg_mf_divert">"Will divert to smart card for crypto operations"</string>
+ <string name="msg_mf_error_divert_newsub">"Creation of new subkeys is not supported for 'divert-to-card' primary keys!"</string>
+ <string name="msg_mf_error_divert_serial">"The serial number of a 'divert-to-card' key must be 16 bytes! This is a programming error, please file a bug report!"</string>
<string name="msg_mf_error_encode">"Encoding exception!"</string>
<string name="msg_mf_error_fingerprint">"Actual key fingerprint does not match the expected one!"</string>
<string name="msg_mf_error_keyid">"No key ID. This is an internal error, please file a bug report!"</string>
@@ -925,11 +1050,17 @@
<string name="msg_mf_error_passphrase_master">"Fatal error decrypting master key! This is likely a programming error, please file a bug report!"</string>
<string name="msg_mf_error_pgp">"Internal OpenPGP error!"</string>
<string name="msg_mf_error_sig">"Signature exception!"</string>
+ <string name="msg_mf_error_sub_stripped">"Cannot modify stripped subkey %s!"</string>
<string name="msg_mf_error_subkey_missing">"Tried to operate on missing subkey %s!"</string>
+ <string name="msg_mf_error_conflicting_nfc_commands">"Cannot move key to smart card in same operation that creates an on-card signature."</string>
+ <string name="msg_mf_error_duplicate_keytocard_for_slot">"Smart card supports only one slot per key type."</string>
+ <string name="msg_mf_error_invalid_flags_for_keytocard">"Inappropriate key flags for smart card key."</string>
<string name="msg_mf_master">"Modifying master certifications"</string>
<string name="msg_mf_notation_empty">"Adding empty notation packet"</string>
<string name="msg_mf_notation_pin">"Adding PIN notation packet"</string>
<string name="msg_mf_passphrase">"Changing password for keyring"</string>
+ <string name="msg_mf_pin">"Changing PIN on card"</string>
+ <string name="msg_mf_admin_pin">"Changing Admin PIN on card"</string>
<string name="msg_mf_passphrase_key">"Re-encrypting subkey %s with new password"</string>
<string name="msg_mf_passphrase_empty_retry">"Setting new password failed, trying again with empty old password"</string>
<string name="msg_mf_passphrase_fail">"Password for subkey could not be changed! (Does it have a different one from the other keys?)"</string>
@@ -937,13 +1068,15 @@
<string name="msg_mf_primary_new">"Generating new certificate for new primary user ID"</string>
<string name="msg_mf_restricted_mode">"Changing to restricted operation mode"</string>
<string name="msg_mf_subkey_change">"Modifying subkey %s"</string>
- <string name="msg_mf_require_divert">"Diverting to card/nfc for crypto operations"</string>
+ <string name="msg_mf_require_divert">"Diverting to smart card for crypto operations"</string>
<string name="msg_mf_require_passphrase">"Password required for operations"</string>
<string name="msg_mf_subkey_new">"Adding new subkey of type %s"</string>
<string name="msg_mf_subkey_new_id">"New subkey ID: %s"</string>
<string name="msg_mf_error_past_expiry">"Expiry date cannot be in the past!"</string>
<string name="msg_mf_subkey_revoke">"Revoking subkey %s"</string>
<string name="msg_mf_subkey_strip">"Stripping subkey %s"</string>
+ <string name="msg_mf_keytocard_start">"Moving subkey %s to smart card"</string>
+ <string name="msg_mf_keytocard_finish">"Moved %1$s to smart card %2$s"</string>
<string name="msg_mf_success">"Keyring successfully modified"</string>
<string name="msg_mf_uid_add">"Adding user ID %s"</string>
<string name="msg_mf_uid_primary">"Changing primary user ID to %s"</string>
@@ -993,13 +1126,17 @@
<string name="msg_ed_caching_new">"Caching new password"</string>
<string name="msg_ed_error_no_parcel">"Missing SaveKeyringParcel! (this is a bug, please report)"</string>
<string name="msg_ed_error_key_not_found">"Key not found!"</string>
+ <string name="msg_ed_error_extract_public_upload">"Error extracting public key for upload!"</string>
<string name="msg_ed_fetching">"Fetching key to modify (%s)"</string>
<string name="msg_ed_success">"Key operation successful"</string>
<!-- Promote key -->
<string name="msg_pr">"Promoting public key to secret key"</string>
+ <string name="msg_pr_all">"Promoting all subkeys"</string>
<string name="msg_pr_error_key_not_found">"Key not found!"</string>
<string name="msg_pr_fetching">"Fetching key to modify (%s)"</string>
+ <string name="msg_pr_subkey_match">"Promoting subkey: %s"</string>
+ <string name="msg_pr_subkey_nomatch">"Subkey not on YubiKey: %s"</string>
<string name="msg_pr_success">"Key successfully promoted"</string>
<!-- Other messages used in OperationLogs -->
@@ -1019,17 +1156,18 @@
<string name="msg_dc_clear_meta_size_unknown">"File size is unknown"</string>
<string name="msg_dc_clear_meta_time">"Modification time: %s"</string>
<string name="msg_dc_clear_signature_bad">"Signature check NOT OK!"</string>
- <string name="msg_dc_error_unsupported_hash_algo">"Unsupported and potentially insecure hash algorithm!"</string>
<string name="msg_dc_clear_signature_check">"Verifying signature data"</string>
<string name="msg_dc_clear_signature_ok">"Signature check OK"</string>
<string name="msg_dc_clear_signature">"Saving signature data for later"</string>
<string name="msg_dc_clear">"Processing cleartext data"</string>
<string name="msg_dc_error_bad_passphrase">"Error unlocking key, bad password!"</string>
+ <string name="msg_dc_error_sym_passphrase">"Error decrypting data! (Bad passphrase?)"</string>
+ <string name="msg_dc_error_corrupt_data">"Data is corrupt!"</string>
<string name="msg_dc_error_extract_key">"Unknown error unlocking key!"</string>
<string name="msg_dc_error_integrity_check">"Integrity check error!"</string>
- <string name="msg_dc_error_integrity_missing">"Missing integrity check! This can happen because the encrypting application is out of date, or from a downgrade attack."</string>
- <string name="msg_dc_error_invalid_siglist">"No valid signature data found!"</string>
- <string name="msg_dc_error_io">"Encountered IO Exception during operation!"</string>
+ <string name="msg_dc_error_invalid_data">"No valid OpenPGP encrypted or signed data found!"</string>
+ <string name="msg_dc_error_io">"Encountered an error reading input data!"</string>
+ <string name="msg_dc_error_input">"Error opening input data stream!"</string>
<string name="msg_dc_error_no_data">"No encrypted data found in stream!"</string>
<string name="msg_dc_error_no_key">"No encrypted data with known secret key found in stream!"</string>
<string name="msg_dc_error_pgp_exception">"Encountered OpenPGP Exception during operation!"</string>
@@ -1047,7 +1185,10 @@
<string name="msg_dc_trail_sym">"Encountered trailing, symmetrically encrypted data"</string>
<string name="msg_dc_trail_unknown">"Encountered trailing data of unknown type"</string>
<string name="msg_dc_unlocking">"Unlocking secret key"</string>
- <string name="msg_dc_old_symmetric_encryption_algo">"Potentially insecure encryption algorithm has been used!"</string>
+ <string name="msg_dc_insecure_symmetric_encryption_algo">"Insecure encryption algorithm has been used! This can happen because the application is out of date, or from an attack."</string>
+ <string name="msg_dc_insecure_hash_algo">"Insecure hash algorithm has been used! This can happen because the application is out of date, or from an attack."</string>
+ <string name="msg_dc_insecure_mdc_missing">"Missing the Modification Detection Code (MDC) packet! This can happen because the encrypting application is out of date, or from a downgrade attack."</string>
+ <string name="msg_dc_insecure_key">"Insecure key: Either the bit length of RSA/DSA/ElGamal is too short or the ECC curve/algorithm is considered insecure! This can happen because the application is out of date, or from an attack."</string>
<!-- Messages for VerifySignedLiteralData operation -->
<string name="msg_vl">"Starting signature check"</string>
@@ -1078,7 +1219,6 @@
<string name="msg_pse_compressing">"Preparing compression"</string>
<string name="msg_pse_encrypting">"Encrypting data"</string>
<string name="msg_pse_error_bad_passphrase">"Bad password!"</string>
- <string name="msg_pse_error_hash_algo">"Requested hashing algorithm is not supported by this key!"</string>
<string name="msg_pse_error_io">"Encountered IO Exception during operation!"</string>
<string name="msg_pse_error_key_sign">"Selected signing key cannot sign data!"</string>
<string name="msg_pse_error_sign_key">"Error fetching signing key!"</string>
@@ -1115,7 +1255,7 @@
<string name="msg_crt_error_unlock">"Error unlocking master key!"</string>
<string name="msg_crt">"Certifying keyrings"</string>
<string name="msg_crt_master_fetch">"Fetching certifying master key"</string>
- <string name="msg_crt_nfc_return">"Returning for NFC input"</string>
+ <string name="msg_crt_nfc_return">"Returning to NFC screen"</string>
<string name="msg_crt_save">"Saving certified key %s"</string>
<string name="msg_crt_saving">"Saving keyrings"</string>
<string name="msg_crt_unlock">"Unlocking master key"</string>
@@ -1123,6 +1263,7 @@
<string name="msg_crt_warn_not_found">"Key not found!"</string>
<string name="msg_crt_warn_cert_failed">"Certificate generation failed!"</string>
<string name="msg_crt_warn_save_failed">"Save operation failed!"</string>
+ <string name="msg_crt_warn_upload_failed">"Upload operation failed!"</string>
<string name="msg_crt_upload_success">"Successfully uploaded key to server"</string>
@@ -1140,6 +1281,7 @@
<string name="msg_import_fingerprint_error">"Fingerprint of fetched key didn't match expected!"</string>
<string name="msg_import_fingerprint_ok">"Fingerprint check OK"</string>
<string name="msg_import_merge">"Merging retrieved data"</string>
+ <string name="msg_import_merge_error">"Error merging retrieved data!"</string>
<string name="msg_import_error">"Import operation failed!"</string>
<string name="msg_import_error_io">"Import operation failed due to i/o error!"</string>
<string name="msg_import_partial">"Import operation successful, with errors!"</string>
@@ -1149,8 +1291,10 @@
<item quantity="one">"Exporting one key"</item>
<item quantity="other">"Exporting %d keys"</item>
</plurals>
+ <string name="msg_export_file_name">"Filename: %s"</string>
<string name="msg_export_all">"Exporting all keys"</string>
<string name="msg_export_public">"Exporting public key %s"</string>
+ <string name="msg_export_upload_public">"Uploading public key %s"</string>
<string name="msg_export_secret">"Exporting secret key %s"</string>
<string name="msg_export_error_no_file">"No filename specified!"</string>
<string name="msg_export_error_fopen">"Error opening file!"</string>
@@ -1160,7 +1304,9 @@
<string name="msg_export_error_db">"Database error!"</string>
<string name="msg_export_error_io">"Input/output error!"</string>
<string name="msg_export_error_key">"Error preprocessing key data!"</string>
+ <string name="msg_export_error_upload">"Error uploading key to server! Please check your Internet connection"</string>
<string name="msg_export_success">"Export operation successful"</string>
+ <string name="msg_export_upload_success">"Upload to keyserver successful"</string>
<string name="msg_del_error_empty">"Nothing to delete!"</string>
<string name="msg_del_error_multi_secret">"Secret keys can only be deleted individually!"</string>
@@ -1180,6 +1326,12 @@
<item quantity="other">"Failed to delete %d keys"</item>
</plurals>
+ <string name="msg_revoke_error_empty">"Nothing to revoke!"</string>
+ <string name="msg_revoke_error_not_found">"Cannot find key to revoke!"</string>
+ <string name="msg_revoke_key">"Revoking key %s"</string>
+ <string name="msg_revoke_key_fail">"Failed revoking key"</string>
+ <string name="msg_revoke_ok">"Successfully revoked key"</string>
+
<!-- Linked Identity verification -->
<string name="msg_lv">"Verifying linked identity…"</string>
<string name="msg_lv_match">"Searching for token"</string>
@@ -1202,6 +1354,7 @@
<string name="msg_lv_fetch_error_format">"Format error!"</string>
<string name="msg_lv_fetch_error_nothing">"Resource not found!"</string>
+
<string name="msg_acc_saved">"Account saved"</string>
<string name="msg_download_success">"Downloaded successfully!"</string>
@@ -1217,6 +1370,15 @@
<string name="msg_download_query_failed">"An error occurred when searching for keys."</string>
+ <!-- Messages for Keybase Verification operation -->
+ <string name="msg_keybase_verification">"Attempting keybase verification for %s"</string>
+ <string name="msg_keybase_error_no_prover">"No proof checker found for %s"</string>
+ <string name="msg_keybase_error_fetching_evidence">"Problem with fetching proof"</string>
+ <string name="msg_keybase_error_key_mismatch">"Key fingerprint doesn’t match that in proof post"</string>
+ <string name="msg_keybase_error_dns_fail">"DNS TXT Record retrieval failed"</string>
+ <string name="msg_keybase_error_specific">"%s"</string>
+ <string name="msg_keybase_error_msg_payload_mismatch">"Decrypted proof post does not match expected value"</string>
+
<!-- Messages for Export Log operation -->
<string name="msg_export_log_start">"Exporting log"</string>
<string name="msg_export_log_error_fopen">"Error opening file"</string>
@@ -1225,36 +1387,53 @@
<string name="msg_export_log_success">"Log exported successfully!"</string>
<!-- PassphraseCache -->
- <string name="passp_cache_notif_click_to_clear">"Click to clear cached passwords"</string>
- <string name="passp_cache_notif_n_keys">"OpenKeychain has cached %d passwords"</string>
- <string name="passp_cache_notif_keys">"Cached Passwords:"</string>
- <string name="passp_cache_notif_clear">"Clear Cache"</string>
+ <string name="passp_cache_notif_click_to_clear">"Touch to clear passwords."</string>
+ <plurals name="passp_cache_notif_n_keys">
+ <item quantity="one">"%d password remembered"</item>
+ <item quantity="other">"%d passwords remembered"</item>
+ </plurals>
+ <string name="passp_cache_notif_keys">"Remembered passwords"</string>
+ <string name="passp_cache_notif_clear">"Clear Passwords"</string>
<string name="passp_cache_notif_pwd">"Password"</string>
+ <!-- Keyserver sync -->
+ <string name="keyserver_sync_orbot_notif_title">"Sync From Servers requires Orbot"</string>
+ <string name="keyserver_sync_orbot_notif_msg">"Tap to start orbot"</string>
+ <string name="keyserver_sync_orbot_notif_start">"Start Orbot"</string>
+ <string name="keyserver_sync_orbot_notif_ignore">"Direct"</string>
+
<!-- First Time -->
<string name="first_time_text1">"Take back your privacy with OpenKeychain!"</string>
<string name="first_time_create_key">"Create my key"</string>
<string name="first_time_import_key">"Import key from file"</string>
<string name="first_time_yubikey">"Use YubiKey NEO"</string>
<string name="first_time_skip">"Skip Setup"</string>
+ <string name="first_time_blank_yubikey">"Would you like to use this blank YubiKey NEO with OpenKeychain?\n\nPlease take away the YubiKey now, you will be prompted when it is needed again!"</string>
+ <string name="first_time_blank_yubikey_yes">"Use this YubiKey"</string>
+
+ <string name="backup_text">"Backups that include your own keys must never be shared with other people!"</string>
+ <string name="backup_all">"All keys + your own keys"</string>
+ <string name="backup_public_keys">"All keys"</string>
+ <string name="backup_section">"Backup"</string>
<!-- unsorted -->
<string name="section_certifier_id">"Certifier"</string>
<string name="section_cert">"Certificate Details"</string>
<string name="label_user_id">"Identity"</string>
- <string name="unknown_uid">"&lt;unknown&gt;"</string>
+ <string name="unknown_uid"><![CDATA[<unknown>]]></string>
<string name="empty_certs">"No certificates for this key"</string>
<string name="certs_text">"Only validated self-certificates and validated certificates created with your keys are displayed here."</string>
<string name="section_uids_to_certify">"Identities for "</string>
<string name="certify_text">"The keys you are importing contain “identities”: names and email addresses. Select exactly those for confirmation which match what you expected."</string>
<string name="certify_fingerprint_text">"Compare the displayed fingerprint, character by character, with the one displayed on your partners device."</string>
- <string name="certify_fingerprint_text2">"Do the displayed fingerprints match?"</string>
+ <string name="certify_fingerprint_text_words">"Compare the displayed fingerprint, word by word, with the one displayed on your partners device."</string>
+ <string name="certify_fingerprint_text2">"Do the fingerprints match?"</string>
<string name="label_revocation">"Revocation Reason"</string>
<string name="label_cert_type">"Type"</string>
<string name="error_key_not_found">"Key not found!"</string>
<string name="error_key_processing">"Error processing key!"</string>
<string name="key_stripped">"stripped"</string>
- <string name="key_divert">"divert to smartcard/NFC"</string>
+ <string name="key_divert">"divert to smart card"</string>
<string name="key_no_passphrase">"no password"</string>
<string name="key_unavailable">"unavailable"</string>
<string name="secret_cannot_multiple">"Your own keys can only be deleted individually!"</string>
@@ -1262,17 +1441,23 @@
<string name="unknown_algorithm">"unknown"</string>
<string name="can_sign_not">"cannot sign"</string>
<string name="error_no_encrypt_subkey">"No encryption subkey available!"</string>
- <string name="info_no_manual_account_creation">"Do not create OpenKeychain-Accounts manually.\nFor more information, see Help."</string>
<string name="contact_show_key">"Show key (%s)"</string>
<string name="swipe_to_update">"Swipe down to update from keyserver"</string>
<string name="error_no_file_selected">"Select at least one file to encrypt!"</string>
- <string name="error_multi_not_supported">"Saving of multiple files not supported. This is a limitation on current Android."</string>
+ <string name="error_multi_files">"Saving of multiple files not supported. This is a limitation on current Android."</string>
+ <string name="error_multi_clipboard">"Encryption of multiple files to clipboard not supported."</string>
+ <string name="error_detached_signature">"Sign-only operation of binary files is not supported, select at least one encryption key."</string>
<string name="error_empty_text">"Type some text to encrypt!"</string>
<string name="key_colon">"Key:"</string>
<string name="exchange_description">"To start a key exchange, choose the number of participants on the right side, then hit the “Start exchange” button.\n\nYou will be asked two more questions to make sure only the right participants are in the exchange and their fingerprints are correct."</string>
<string name="btn_start_exchange">"Start exchange"</string>
<string name="user_id_none"><![CDATA[<none>]]></string>
+ <!-- Android Account -->
+ <string name="account_no_manual_account_creation">"You can not create OpenKeychain accounts manually."</string>
+ <string name="account_privacy_title">"Privacy"</string>
+ <string name="account_privacy_text">"OpenKeychain does not synchronize your contacts with the Internet. It only links contacts to keys based on names and email addresses. It does this offline on your device."</string>
+
<!-- Passphrase wizard -->
<!-- TODO: rename all the things! -->
<string name="title_unlock_method">Choose an unlock method</string>
@@ -1304,7 +1489,7 @@
<string name="button_bind_key">"Bind Key"</string>
<string name="yubikey_serno">"Serial No: %s"</string>
<string name="yubikey_key_holder">"Key holder: "</string>
- <string name="yubikey_key_holder_unset">"Key holder: &lt;unset&gt;"</string>
+ <string name="yubikey_key_holder_not_set"><![CDATA[Key holder: <not set>]]></string>
<string name="yubikey_status_bound">"YubiKey matches and is bound to key"</string>
<string name="yubikey_status_unbound">"YubiKey matches, can be bound to key"</string>
<string name="yubikey_status_partly">"YubiKey matches, partly bound to key"</string>
@@ -1312,8 +1497,52 @@
<string name="btn_import">"Import"</string>
<string name="snack_yubi_other">Different key stored on YubiKey!</string>
<string name="error_nfc">"NFC Error: %s"</string>
+ <plurals name="error_pin">
+ <item quantity="one">"Incorrect PIN!\n%d try remaining."</item>
+ <item quantity="other">"Incorrect PIN!\n%d tries remaining."</item>
+ </plurals>
+ <string name="error_nfc_terminated">"YubiKey in termination state."</string>
+ <string name="error_nfc_wrong_length">"Entered PIN is too short. PINs are at least 6 digits long."</string>
+ <string name="error_nfc_conditions_not_satisfied">"Conditions of use not satisfied."</string>
+ <string name="error_nfc_security_not_satisfied">"Security status not satisfied."</string>
+ <string name="error_nfc_authentication_blocked">"PIN blocked after too many attempts."</string>
+ <string name="error_nfc_data_not_found">"Key or object not found."</string>
+ <string name="error_nfc_unknown">"Unknown Error"</string>
+ <string name="error_nfc_bad_data">"YubiKey reported invalid data."</string>
+ <string name="error_nfc_chaining_error">"YubiKey expected last command in a chain."</string>
+ <string name="error_nfc_header">"YubiKey reported invalid %s byte."</string>
+ <string name="error_nfc_tag_lost">"YubiKey has been taken off too early. Keep the YubiKey at the back until the operation finishes."</string>
+ <string name="error_nfc_try_again">"Try again"</string>
<string name="error_pin_nodefault">Default PIN was rejected!</string>
- <string name="error_bluetooth_file">Error creating temporary file. Bluetooth sharing will fail.</string>
+ <string name="error_temp_file">Error creating temporary file.</string>
+ <string name="btn_delete_original">Delete original file</string>
+
+ <string name="snack_encrypt_filenames_on">"Filenames <b>are</b> encrypted."</string>
+ <string name="snack_encrypt_filenames_off">"Filenames <b>are not</b> encrypted."</string>
+ <string name="snack_armor_on">"Output encoded as Text."</string>
+ <string name="snack_armor_off">"Output encoded as Binary."</string>
+ <string name="snack_compression_on">"Compression <b>enabled</b>."</string>
+ <string name="snack_compression_off">"Compression <b>disabled</b>."</string>
+ <string name="error_loading_keys">"Error loading keys!"</string>
+ <string name="error_empty_log">"(error, empty log)"</string>
+ <string name="error_reading_text">"Could not read input to decrypt!"</string>
+ <string name="filename_unknown"><![CDATA[<no filename>]]></string>
+ <string name="filename_unknown_text"><![CDATA[<plain text data>]]></string>
+ <string name="intent_show">Show Signed/Encrypted Content</string>
+ <string name="view_internal">"View in OpenKeychain"</string>
+ <string name="error_preparing_data">"Error preparing data!"</string>
+ <string name="label_clip_title">"Encrypted Data"</string>
+ <string name="progress_processing">"Processing…"</string>
+ <string name="error_saving_file">"Error saving file!"</string>
+ <string name="file_saved">"File saved!"</string>
+ <string name="file_delete_ok">"Original file deleted."</string>
+ <string name="file_delete_none">"No file deleted! (Already deleted?)"</string>
+ <string name="file_delete_exception">"Original file could not be deleted!"</string>
+ <string name="error_clipboard_empty">"Clipboard is empty!"</string>
+ <string name="error_clipboard_copy">"Error copying data to clipboard!"</string>
+ <string name="error_scan_fp">"Error scanning fingerprint!"</string>
+ <string name="error_scan_match">"Fingerprints did not match!"</string>
+ <string name="error_expiry_past">"Expiry date is in the past!"</string>
<string name="linked_create_https_1_1">"By creating a Linked Identity of this type, you can link your key to a website you control."</string>
<string name="linked_create_https_1_2">"To do this, you publish a text file on this website, then create a Linked Identity which links to it."</string>
diff --git a/OpenKeychain/src/main/res/values/styles.xml b/OpenKeychain/src/main/res/values/styles.xml
index 9ac60c397..55c4e2220 100644
--- a/OpenKeychain/src/main/res/values/styles.xml
+++ b/OpenKeychain/src/main/res/values/styles.xml
@@ -2,28 +2,40 @@
<resources>
<style name="CardViewHeader">
- <item name="android:drawableBottom">@drawable/cardview_header</item>
+ <item name="android:drawableBottom">?attr/cardViewHeaderDrawable</item>
<item name="android:drawablePadding">16dp</item>
<item name="android:layout_marginTop">16dp</item>
<item name="android:paddingLeft">16dp</item>
<item name="android:textStyle">normal</item>
- <item name="android:textColor">@color/header_text</item>
+ <item name="android:textColor">?attr/colorHeaderText</item>
<item name="android:textSize">17sp</item>
</style>
<style name="SectionHeader">
- <item name="android:drawableBottom">@drawable/section_header</item>
+ <item name="android:drawableBottom">?attr/sectionHeaderDrawable</item>
<item name="android:drawablePadding">4dp</item>
<item name="android:layout_marginTop">8dp</item>
<item name="android:paddingLeft">8dp</item>
<item name="android:textStyle">bold</item>
- <item name="android:textColor">@color/header_text</item>
+ <item name="android:textColor">?attr/colorHeaderText</item>
<item name="android:textSize">14sp</item>
</style>
<style name="FabMenuStyle">
- <item name="android:background">@drawable/fab_label_background</item>
- <item name="android:textColor">@color/white</item>
+ <item name="android:background">?attr/fabLabelBackgroundDrawable</item>
+ <item name="android:textColor">?attr/colorFabText</item>
</style>
-</resources> \ No newline at end of file
+ <!-- This style is for use with our drag and drop RecyclerView since ItemDecoration did not
+ move with the drag -->
+ <style name="Divider">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">2dp</item>
+ <item name="android:background">?android:attr/listDivider</item>
+ </style>
+
+ <style name="MaterialFlatButton" parent="@style/Widget.AppCompat.Button.Borderless">
+ <item name="android:textColor">?attr/colorAccent</item>
+ </style>
+
+</resources>
diff --git a/OpenKeychain/src/main/res/values/themes.xml b/OpenKeychain/src/main/res/values/themes.xml
index 6ac09c5d7..38cf8a3db 100644
--- a/OpenKeychain/src/main/res/values/themes.xml
+++ b/OpenKeychain/src/main/res/values/themes.xml
@@ -1,23 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <style name="KeychainTheme" parent="KeychainTheme.Base" />
+ <style name="Base.Theme.Keychain.Light" parent="Theme.AppCompat.Light">
+ <item name="colorPrimary">#7bad45</item>
+ <item name="colorPrimaryDark">#6c983d</item>
+ <item name="colorAccent">#2196f3</item>
+ <item name="colorBrightToolbar">#dddddd</item>
- <style name="KeychainTheme.Base" parent="Theme.AppCompat.Light">
- <item name="colorPrimary">@color/primary</item>
- <item name="colorPrimaryDark">@color/primary_dark</item>
- <item name="colorAccent">@color/accent</item>
+ <item name="colorFab">#2196f3</item>
+ <item name="colorFabPressed">#1976d2</item>
+ <item name="colorFabText">#fafafa</item>
+
+ <!-- using an attribute for color will crash on API <= 20. Different drawable xmls are necessary for each theme. Refer to http://stackoverflow.com/a/13471695/3000919 -->
+ <item name="fabLabelBackgroundDrawable">@drawable/fab_label_background</item>
+
+ <item name="colorTabText">#70ffffff</item>
+ <item name="colorTabTextSelected">#ffffff</item>
+ <item name="colorTabIndicator">#ffffff</item>
+
+ <item name="colorEmphasis">#2196f3</item>
+ <item name="colorButtonRow">#33cccccc</item>
+ <item name="colorLogBackground">#cecbce</item>
+ <item name="colorCardViewBackground">#ffffff</item>
+ <!-- using an attribute for color will crash on API <= 20. Different drawable xmls are necessary for each theme. Refer to http://stackoverflow.com/a/13471695/3000919 -->
+ <item name="cardViewHeaderDrawable">@drawable/cardview_header</item>
+
+ <item name="colorText">#000000</item>
+ <item name="colorHeaderText">#212121</item>
+ <item name="colorTertiaryText">#808080</item>
+ <!-- using an attribute for color will crash on API <= 20. Different drawable xmls are necessary for each theme. Refer to http://stackoverflow.com/a/13471695/3000919 -->
+ <item name="sectionHeaderDrawable">@drawable/section_header_light</item>
+
+ <!-- remove actionbar and title, we use toolbar! -->
+ <item name="windowNoTitle">true</item>
+ <item name="windowActionBar">false</item>
+ <!-- multi selection should overlay Toolbar! http://stackoverflow.com/a/26450875 -->
+ <item name="windowActionModeOverlay">true</item>
+ <item name="searchViewStyle">@style/Widget.Keychain.SearchView</item>
+
+ <!-- style dialogs -->
+ <item name="alertDialogTheme">@style/Theme.Keychain.Light.Dialog.Alert</item>
+ </style>
+
+ <style name="Base.Theme.Keychain.Dark" parent="MaterialDrawerTheme.ActionBar">
+ <item name="colorPrimary">#268bd2</item>
+ <item name="colorPrimaryDark">#166bb2</item>
+ <item name="colorAccent">#2196f3</item>
+ <item name="colorBrightToolbar">#808080</item>
+
+ <item name="colorFab">#2196f3</item>
+ <item name="colorFabPressed">#1976d2</item>
+ <item name="colorFabText">#fafafa</item>
+
+ <!-- using an attribute for color will crash on API <= 20. Different drawable xmls are necessary for each theme. Refer to http://stackoverflow.com/a/13471695/3000919 -->
+ <item name="fabLabelBackgroundDrawable">@drawable/fab_label_background</item>
+
+ <item name="colorTabText">#70ffffff</item>
+ <item name="colorTabTextSelected">#ffffff</item>
+ <item name="colorTabIndicator">#ffffff</item>
+
+ <item name="colorEmphasis">#2196f3</item>
+ <item name="colorButtonRow">#33cccccc</item>
+ <item name="colorLogBackground">#303030</item>
+ <item name="colorCardViewBackground">#505050</item>
+ <!-- using an attribute for color will crash on API <= 20. Different drawable xmls are necessary for each theme. Refer to http://stackoverflow.com/a/13471695/3000919 -->
+ <item name="cardViewHeaderDrawable">@drawable/cardview_header</item>
+
+ <item name="colorText">#ffffff</item>
+ <item name="colorHeaderText">#d0d0d0</item>
+ <item name="colorTertiaryText">#808080</item>
+ <!-- using an attribute for color will crash on API <= 20. Different drawable xmls are necessary for each theme. Refer to http://stackoverflow.com/a/13471695/3000919 -->
+ <item name="sectionHeaderDrawable">@drawable/section_header_dark</item>
+
+ <item name="material_drawer_selected_text">#268bd2</item>
<!-- remove actionbar and title, we use toolbar! -->
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<!-- multi selection should overlay Toolbar! http://stackoverflow.com/a/26450875 -->
<item name="windowActionModeOverlay">true</item>
- <item name="searchViewStyle">@style/MySearchViewStyle</item>
+ <item name="searchViewStyle">@style/Widget.Keychain.SearchView</item>
+
+ <!-- style dialogs -->
+ <item name="alertDialogTheme">@style/Theme.Keychain.Dark.Dialog.Alert</item>
+ </style>
+
+ <style name="Theme.Keychain.Light" parent="Base.Theme.Keychain.Light">
+ </style>
+
+ <style name="Theme.Keychain.Dark" parent="Base.Theme.Keychain.Dark">
</style>
<!-- http://android-developers.blogspot.de/2014/10/appcompat-v21-material-design-for-pre.html -->
- <style name="MySearchViewStyle" parent="Widget.AppCompat.SearchView">
+ <style name="Widget.Keychain.SearchView" parent="Widget.AppCompat.SearchView">
<!-- Background for the search query section (e.g. EditText) -->
<!--<item name="queryBackground">...</item>-->
<!-- Background for the actions section (e.g. voice, submit) -->
@@ -35,4 +110,28 @@
<!-- Layout for query suggestion rows -->
<!--<item name="suggestionRowLayout">...</item>-->
</style>
-</resources> \ No newline at end of file
+
+ <style name="Theme.Keychain.Light.Dialog.Alert" parent="Base.Theme.AppCompat.Light.Dialog.Alert">
+ <item name="colorAccent">#7bad45</item>
+ </style>
+
+ <style name="Theme.Keychain.Dark.Dialog.Alert" parent="Base.Theme.AppCompat.Dialog.Alert">
+ <item name="colorAccent">#268bd2</item>
+ </style>
+
+ <style name="Theme.Keychain.Light.Dialog" parent="Theme.AppCompat.Light.Dialog.MinWidth">
+ <item name="colorAccent">#7bad45</item>
+ <item name="android:buttonStyle">@style/MaterialFlatButton</item>
+
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ </style>
+
+ <style name="Theme.Keychain.Dark.Dialog" parent="Theme.AppCompat.Dialog.MinWidth">
+ <item name="colorAccent">#268bd2</item>
+ <item name="android:buttonStyle">@style/MaterialFlatButton</item>
+
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ </style>
+</resources>
diff --git a/OpenKeychain/src/main/res/xml/account_desc.xml b/OpenKeychain/src/main/res/xml/account_desc.xml
index d29395202..e04160c5c 100644
--- a/OpenKeychain/src/main/res/xml/account_desc.xml
+++ b/OpenKeychain/src/main/res/xml/account_desc.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
- android:accountType="org.sufficientlysecure.keychain.account"
+ android:accountPreferences="@xml/account_preferences"
+ android:accountType="@string/account_type"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" />
diff --git a/OpenKeychain/src/main/res/xml/account_preferences.xml b/OpenKeychain/src/main/res/xml/account_preferences.xml
new file mode 100644
index 000000000..3fdb54f1e
--- /dev/null
+++ b/OpenKeychain/src/main/res/xml/account_preferences.xml
@@ -0,0 +1,10 @@
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <PreferenceCategory android:title="@string/account_privacy_title" />
+
+ <Preference
+ android:persistent="false"
+ android:selectable="false"
+ android:summary="@string/account_privacy_text" />
+
+</PreferenceScreen> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/xml/cloud_search_prefs.xml b/OpenKeychain/src/main/res/xml/cloud_search_prefs.xml
index 08855fa22..85d6a6bca 100644
--- a/OpenKeychain/src/main/res/xml/cloud_search_prefs.xml
+++ b/OpenKeychain/src/main/res/xml/cloud_search_prefs.xml
@@ -1,20 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
-
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
- <CheckBoxPreference
- android:title="@string/pref_keyserver"
+ <SwitchPreference
android:defaultValue="true"
+ android:key="search_keyserver_pref"
android:summary="@string/pref_keyserver_summary"
- android:key="search_keyserver_pref" />
-
- <CheckBoxPreference
- android:title="@string/pref_keybase"
- android:defaultValue="true"
- android:summary="@string/pref_keybase_summary"
- android:key="search_keybase_pref" />
+ android:title="@string/pref_keyserver" />
<PreferenceScreen
+ android:dependency="search_keyserver_pref"
android:key="keyServers"
android:persistent="false"
android:title="@string/label_keyservers" />
+
+ <SwitchPreference
+ android:defaultValue="true"
+ android:key="search_keybase_pref"
+ android:summary="@string/pref_keybase_summary"
+ android:title="@string/pref_keybase" />
+
</PreferenceScreen> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/xml/experimental_preferences.xml b/OpenKeychain/src/main/res/xml/experimental_preferences.xml
new file mode 100644
index 000000000..33ab92697
--- /dev/null
+++ b/OpenKeychain/src/main/res/xml/experimental_preferences.xml
@@ -0,0 +1,36 @@
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+ <Preference
+ android:persistent="false"
+ android:selectable="false"
+ android:summary="@string/label_experimental_settings_desc_summary" />
+
+ <SwitchPreference
+ android:defaultValue="false"
+ android:key="experimentalEnableWordConfirm"
+ android:persistent="false"
+ android:summary="@string/label_experimental_settings_word_confirm_summary"
+ android:title="@string/label_experimental_settings_word_confirm_title" />
+
+ <SwitchPreference
+ android:defaultValue="false"
+ android:key="experimentalEnableLinkedIdentities"
+ android:persistent="false"
+ android:summary="@string/label_experimental_settings_linked_identities_summary"
+ android:title="@string/label_experimental_settings_linked_identities_title" />
+
+ <SwitchPreference
+ android:defaultValue="false"
+ android:key="experimentalEnableKeybase"
+ android:persistent="false"
+ android:summary="@string/label_experimental_settings_keybase_summary"
+ android:title="@string/label_experimental_settings_keybase_title" />
+
+ <ListPreference
+ android:dialogTitle="@string/label_theme"
+ android:entries="@array/theme_entries"
+ android:entryValues="@array/theme_values"
+ android:key="theme"
+ android:persistent="false"
+ android:title="@string/label_theme" />
+
+</PreferenceScreen>
diff --git a/OpenKeychain/src/main/res/xml/gui_preferences.xml b/OpenKeychain/src/main/res/xml/gui_preferences.xml
new file mode 100644
index 000000000..cda7beeef
--- /dev/null
+++ b/OpenKeychain/src/main/res/xml/gui_preferences.xml
@@ -0,0 +1,10 @@
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+ <ListPreference
+ android:persistent="false"
+ android:key="theme"
+ android:title="@string/label_theme"
+ android:entries="@array/theme_entries"
+ android:entryValues="@array/theme_values"
+ android:dialogTitle="@string/label_theme" />
+
+</PreferenceScreen>
diff --git a/OpenKeychain/src/main/res/xml/keyserver_sync_adapter_desc.xml b/OpenKeychain/src/main/res/xml/keyserver_sync_adapter_desc.xml
new file mode 100644
index 000000000..3923fae59
--- /dev/null
+++ b/OpenKeychain/src/main/res/xml/keyserver_sync_adapter_desc.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+ android:contentAuthority="@string/provider_content_authority"
+ android:accountType="@string/account_type"
+ android:supportsUploading="false"
+ android:userVisible="true"
+ android:allowParallelSyncs="false"
+ android:isAlwaysSyncable="true" /> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/xml/adv_preferences.xml b/OpenKeychain/src/main/res/xml/passphrase_preferences.xml
index 7dd649e5d..7dd649e5d 100644
--- a/OpenKeychain/src/main/res/xml/adv_preferences.xml
+++ b/OpenKeychain/src/main/res/xml/passphrase_preferences.xml
diff --git a/OpenKeychain/src/main/res/xml/preference_headers.xml b/OpenKeychain/src/main/res/xml/preference_headers.xml
index e3447ff48..0c171467b 100644
--- a/OpenKeychain/src/main/res/xml/preference_headers.xml
+++ b/OpenKeychain/src/main/res/xml/preference_headers.xml
@@ -1,8 +1,29 @@
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
+ <!--<header-->
+ <!--android:fragment="org.sufficientlysecure.keychain.ui.SettingsActivity$GuiPrefsFragment"-->
+ <!--android:title="@string/section_gui" />-->
<header
android:fragment="org.sufficientlysecure.keychain.ui.SettingsActivity$CloudSearchPrefsFragment"
+ android:icon="@drawable/ic_cloud_black_24dp"
+ android:summary="Keyserver, keybase.io"
android:title="@string/section_cloud_search" />
<header
- android:fragment="org.sufficientlysecure.keychain.ui.SettingsActivity$AdvancedPrefsFragment"
+ android:fragment="org.sufficientlysecure.keychain.ui.SettingsActivity$PassphrasePrefsFragment"
+ android:icon="@drawable/ic_lock_black_24dp"
+ android:summary="@string/section_passphrase_cache_summary"
android:title="@string/section_passphrase_cache" />
+ <header
+ android:fragment="org.sufficientlysecure.keychain.ui.SettingsActivity$ProxyPrefsFragment"
+ android:icon="@drawable/ic_settings_ethernet_black_24dp"
+ android:summary="@string/section_proxy_settings_summary"
+ android:title="@string/section_proxy_settings" />
+ <header
+ android:fragment="org.sufficientlysecure.keychain.ui.SettingsActivity$SyncPrefsFragment"
+ android:icon="@drawable/ic_sync_black_24dp"
+ android:summary="@string/section_sync_settings_summary"
+ android:title="@string/section_sync_settings" />
+ <header
+ android:fragment="org.sufficientlysecure.keychain.ui.SettingsActivity$ExperimentalPrefsFragment"
+ android:icon="@drawable/ic_extension_black_24dp"
+ android:title="@string/section_experimental_features" />
</preference-headers>
diff --git a/OpenKeychain/src/main/res/xml/proxy_prefs.xml b/OpenKeychain/src/main/res/xml/proxy_prefs.xml
new file mode 100644
index 000000000..fbb83eb12
--- /dev/null
+++ b/OpenKeychain/src/main/res/xml/proxy_prefs.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+ <SwitchPreference
+ android:key="useTorProxy"
+ android:persistent="true"
+ android:title="@string/pref_proxy_tor_title"
+ android:summary="@string/pref_proxy_tor_summary" />
+ <SwitchPreference
+ android:key="useNormalProxy"
+ android:persistent="true"
+ android:title="@string/pref_proxy_normal_title" />
+ <EditTextPreference
+ android:key="proxyHost"
+ android:persistent="true"
+ android:defaultValue="127.0.0.1"
+ android:title="@string/pref_proxy_host_title"
+ android:cursorVisible="true"
+ android:textCursorDrawable="@null"
+ android:inputType="textEmailAddress"/>
+ <EditTextPreference
+ android:key="proxyPort"
+ android:defaultValue="8118"
+ android:persistent="true"
+ android:title="@string/pref_proxy_port_title"
+ android:textCursorDrawable="@null"
+ android:inputType="number" />
+ <ListPreference
+ android:entries="@array/pref_proxy_type_entries"
+ android:entryValues="@array/pref_proxy_type_values"
+ android:defaultValue="proxyHttp"
+ android:key="proxyType"
+ android:persistent="true"
+ android:title="@string/pref_proxy_type_title" />
+</PreferenceScreen>
diff --git a/OpenKeychain/src/main/res/xml/sync_adapter_desc.xml b/OpenKeychain/src/main/res/xml/sync_adapter_desc.xml
index 6871e1a5d..a134fdebe 100644
--- a/OpenKeychain/src/main/res/xml/sync_adapter_desc.xml
+++ b/OpenKeychain/src/main/res/xml/sync_adapter_desc.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.android.contacts"
- android:accountType="org.sufficientlysecure.keychain.account"
+ android:accountType="@string/account_type"
android:supportsUploading="false"
android:userVisible="true"
android:allowParallelSyncs="false"
diff --git a/OpenKeychain/src/main/res/xml/sync_preferences.xml b/OpenKeychain/src/main/res/xml/sync_preferences.xml
new file mode 100644
index 000000000..de41ff030
--- /dev/null
+++ b/OpenKeychain/src/main/res/xml/sync_preferences.xml
@@ -0,0 +1,10 @@
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+ <SwitchPreference
+ android:key="syncKeyserver"
+ android:persistent="false"
+ android:title="@string/label_sync_settings_keyserver_title"/>
+ <SwitchPreference
+ android:key="syncContacts"
+ android:persistent="false"
+ android:title="@string/label_sync_settings_contacts_title" />
+</PreferenceScreen>
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/WorkaroundBuildConfig.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/WorkaroundBuildConfig.java
new file mode 100644
index 000000000..5f1a20fe9
--- /dev/null
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/WorkaroundBuildConfig.java
@@ -0,0 +1,16 @@
+package org.sufficientlysecure.keychain;
+
+import org.sufficientlysecure.keychain.BuildConfig;
+
+/**
+ * Temporary workaround for https://github.com/robolectric/robolectric/issues/1747
+ */
+public final class WorkaroundBuildConfig {
+ public static final boolean DEBUG = BuildConfig.DEBUG;
+ // Workaround: Use real packageName not applicationId
+ public static final String APPLICATION_ID = "org.sufficientlysecure.keychain";
+ public static final String BUILD_TYPE = BuildConfig.BUILD_TYPE;
+ public static final String FLAVOR = BuildConfig.FLAVOR;
+ public static final int VERSION_CODE = BuildConfig.VERSION_CODE;
+ public static final String VERSION_NAME = BuildConfig.VERSION_NAME;
+}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java
index 37f46604f..3e6b74765 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java
@@ -22,22 +22,20 @@ import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RobolectricGradleTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.sufficientlysecure.keychain.BuildConfig;
+import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.operations.results.CertifyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
-import org.sufficientlysecure.keychain.operations.results.ExportResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
-import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
-import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow;
-import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
@@ -47,23 +45,18 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
-import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import org.sufficientlysecure.keychain.util.TestingUtils;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.OutputStream;
import java.io.PrintStream;
import java.security.Security;
import java.util.ArrayList;
-import java.util.Iterator;
+import java.util.Date;
import java.util.Random;
-
-@RunWith(RobolectricTestRunner.class)
-@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+@RunWith(RobolectricGradleTestRunner.class)
+@Config(constants = WorkaroundBuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")
public class CertifyOperationTest {
static UncachedKeyRing mStaticRing1, mStaticRing2;
@@ -128,7 +121,7 @@ public class CertifyOperationTest {
@Before
public void setUp() throws Exception {
- ProviderHelper providerHelper = new ProviderHelper(Robolectric.application);
+ ProviderHelper providerHelper = new ProviderHelper(RuntimeEnvironment.application);
// don't log verbosely here, we're not here to test imports
ShadowLog.stream = oldShadowStream;
@@ -143,7 +136,7 @@ public class CertifyOperationTest {
@Test
public void testSelfCertifyFlag() throws Exception {
- CanonicalizedPublicKeyRing ring = new ProviderHelper(Robolectric.application)
+ CanonicalizedPublicKeyRing ring = new ProviderHelper(RuntimeEnvironment.application)
.getCanonicalizedPublicKeyRing(mStaticRing1.getMasterKeyId());
Assert.assertEquals("secret key must be marked self-certified in database",
// TODO this should be more correctly be VERIFIED_SELF at some point!
@@ -153,11 +146,11 @@ public class CertifyOperationTest {
@Test
public void testCertifyId() throws Exception {
- CertifyOperation op = new CertifyOperation(Robolectric.application,
- new ProviderHelper(Robolectric.application), null, null);
+ CertifyOperation op = new CertifyOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null, null);
{
- CanonicalizedPublicKeyRing ring = new ProviderHelper(Robolectric.application)
+ CanonicalizedPublicKeyRing ring = new ProviderHelper(RuntimeEnvironment.application)
.getCanonicalizedPublicKeyRing(mStaticRing2.getMasterKeyId());
Assert.assertEquals("public key must not be marked verified prior to certification",
Certs.UNVERIFIED, ring.getVerified());
@@ -165,13 +158,13 @@ public class CertifyOperationTest {
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId());
actions.add(new CertifyAction(mStaticRing2.getMasterKeyId(),
- mStaticRing2.getPublicKey().getUnorderedUserIds(), null));
- CertifyResult result = op.certify(actions, new CryptoInputParcel(mKeyPhrase1), null);
+ mStaticRing2.getPublicKey().getUnorderedUserIds()));
+ CertifyResult result = op.execute(actions, new CryptoInputParcel(new Date(), mKeyPhrase1));
Assert.assertTrue("certification must succeed", result.success());
{
- CanonicalizedPublicKeyRing ring = new ProviderHelper(Robolectric.application)
+ CanonicalizedPublicKeyRing ring = new ProviderHelper(RuntimeEnvironment.application)
.getCanonicalizedPublicKeyRing(mStaticRing2.getMasterKeyId());
Assert.assertEquals("new key must be verified now",
Certs.VERIFIED_SECRET, ring.getVerified());
@@ -181,11 +174,11 @@ public class CertifyOperationTest {
@Test
public void testCertifyAttribute() throws Exception {
- CertifyOperation op = new CertifyOperation(Robolectric.application,
- new ProviderHelper(Robolectric.application), null, null);
+ CertifyOperation op = new CertifyOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null, null);
{
- CanonicalizedPublicKeyRing ring = new ProviderHelper(Robolectric.application)
+ CanonicalizedPublicKeyRing ring = new ProviderHelper(RuntimeEnvironment.application)
.getCanonicalizedPublicKeyRing(mStaticRing2.getMasterKeyId());
Assert.assertEquals("public key must not be marked verified prior to certification",
Certs.UNVERIFIED, ring.getVerified());
@@ -194,12 +187,12 @@ public class CertifyOperationTest {
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId());
actions.add(new CertifyAction(mStaticRing2.getMasterKeyId(), null,
mStaticRing2.getPublicKey().getUnorderedUserAttributes()));
- CertifyResult result = op.certify(actions, new CryptoInputParcel(mKeyPhrase1), null);
+ CertifyResult result = op.execute(actions, new CryptoInputParcel(new Date(), mKeyPhrase1));
Assert.assertTrue("certification must succeed", result.success());
{
- CanonicalizedPublicKeyRing ring = new ProviderHelper(Robolectric.application)
+ CanonicalizedPublicKeyRing ring = new ProviderHelper(RuntimeEnvironment.application)
.getCanonicalizedPublicKeyRing(mStaticRing2.getMasterKeyId());
Assert.assertEquals("new key must be verified now",
Certs.VERIFIED_SECRET, ring.getVerified());
@@ -210,14 +203,14 @@ public class CertifyOperationTest {
@Test
public void testCertifySelf() throws Exception {
- CertifyOperation op = new CertifyOperation(Robolectric.application,
- new ProviderHelper(Robolectric.application), null, null);
+ CertifyOperation op = new CertifyOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null, null);
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId());
actions.add(new CertifyAction(mStaticRing1.getMasterKeyId(),
mStaticRing2.getPublicKey().getUnorderedUserIds(), null));
- CertifyResult result = op.certify(actions, new CryptoInputParcel(mKeyPhrase1), null);
+ CertifyResult result = op.execute(actions, new CryptoInputParcel(new Date(), mKeyPhrase1));
Assert.assertFalse("certification with itself must fail!", result.success());
Assert.assertTrue("error msg must be about self certification",
@@ -227,8 +220,8 @@ public class CertifyOperationTest {
@Test
public void testCertifyNonexistent() throws Exception {
- CertifyOperation op = new CertifyOperation(Robolectric.application,
- new ProviderHelper(Robolectric.application), null, null);
+ CertifyOperation op = new CertifyOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null, null);
{
CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId());
@@ -236,7 +229,8 @@ public class CertifyOperationTest {
uids.add("nonexistent");
actions.add(new CertifyAction(1234L, uids, null));
- CertifyResult result = op.certify(actions, new CryptoInputParcel(mKeyPhrase1), null);
+ CertifyResult result = op.execute(actions, new CryptoInputParcel(new Date(),
+ mKeyPhrase1));
Assert.assertFalse("certification of nonexistent key must fail", result.success());
Assert.assertTrue("must contain error msg about not found",
@@ -248,7 +242,8 @@ public class CertifyOperationTest {
actions.add(new CertifyAction(mStaticRing1.getMasterKeyId(),
mStaticRing2.getPublicKey().getUnorderedUserIds(), null));
- CertifyResult result = op.certify(actions, new CryptoInputParcel(mKeyPhrase1), null);
+ CertifyResult result = op.execute(actions, new CryptoInputParcel(new Date(),
+ mKeyPhrase1));
Assert.assertFalse("certification of nonexistent key must fail", result.success());
Assert.assertTrue("must contain error msg about not found",
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java
index 23ea356c8..a659dc7da 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java
@@ -23,10 +23,14 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
+import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
@@ -48,8 +52,8 @@ import java.io.PrintStream;
import java.security.Security;
import java.util.Iterator;
-@RunWith(RobolectricTestRunner.class)
-@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+@RunWith(RobolectricGradleTestRunner.class)
+@Config(constants = WorkaroundBuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")
public class ExportTest {
static Passphrase mPassphrase = TestingUtils.genPassphrase(true);
@@ -108,7 +112,7 @@ public class ExportTest {
@Before
public void setUp() {
- ProviderHelper providerHelper = new ProviderHelper(Robolectric.application);
+ ProviderHelper providerHelper = new ProviderHelper(RuntimeEnvironment.application);
// don't log verbosely here, we're not here to test imports
ShadowLog.stream = oldShadowStream;
@@ -122,8 +126,8 @@ public class ExportTest {
@Test
public void testExportAll() throws Exception {
- ImportExportOperation op = new ImportExportOperation(Robolectric.application,
- new ProviderHelper(Robolectric.application), null);
+ ExportOperation op = new ExportOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null);
// make sure there is a local cert (so the later checks that there are none are meaningful)
Assert.assertTrue("second keyring has local certification", checkForLocal(mStaticRing2));
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java
index 617b5762c..a4854d7b9 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java
@@ -17,17 +17,24 @@
package org.sufficientlysecure.keychain.operations;
+
+import java.io.PrintStream;
+import java.security.Security;
+import java.util.Iterator;
+
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RobolectricGradleTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.util.encoders.Hex;
+import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
@@ -38,19 +45,17 @@ import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
+import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import org.sufficientlysecure.keychain.util.TestingUtils;
-import java.io.PrintStream;
-import java.security.Security;
-import java.util.Iterator;
-
-@RunWith(RobolectricTestRunner.class)
-@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+@RunWith(RobolectricGradleTestRunner.class)
+@Config(constants = WorkaroundBuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")
public class PromoteKeyOperationTest {
static UncachedKeyRing mStaticRing;
@@ -88,7 +93,7 @@ public class PromoteKeyOperationTest {
@Before
public void setUp() throws Exception {
- ProviderHelper providerHelper = new ProviderHelper(Robolectric.application);
+ ProviderHelper providerHelper = new ProviderHelper(RuntimeEnvironment.application);
// don't log verbosely here, we're not here to test imports
ShadowLog.stream = oldShadowStream;
@@ -101,15 +106,15 @@ public class PromoteKeyOperationTest {
@Test
public void testPromote() throws Exception {
- PromoteKeyOperation op = new PromoteKeyOperation(Robolectric.application,
- new ProviderHelper(Robolectric.application), null, null);
+ PromoteKeyOperation op = new PromoteKeyOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null, null);
- PromoteKeyResult result = op.execute(mStaticRing.getMasterKeyId(), null);
+ PromoteKeyResult result = op.execute(new PromoteKeyringParcel(mStaticRing.getMasterKeyId(), null, null), null);
Assert.assertTrue("promotion must succeed", result.success());
{
- CachedPublicKeyRing ring = new ProviderHelper(Robolectric.application)
+ CachedPublicKeyRing ring = new ProviderHelper(RuntimeEnvironment.application)
.getCachedPublicKeyRing(mStaticRing.getMasterKeyId());
Assert.assertTrue("key must have a secret now", ring.hasAnySecret());
@@ -125,17 +130,17 @@ public class PromoteKeyOperationTest {
@Test
public void testPromoteDivert() throws Exception {
- PromoteKeyOperation op = new PromoteKeyOperation(Robolectric.application,
- new ProviderHelper(Robolectric.application), null, null);
+ PromoteKeyOperation op = new PromoteKeyOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null, null);
byte[] aid = Hex.decode("D2760001240102000000012345670000");
- PromoteKeyResult result = op.execute(mStaticRing.getMasterKeyId(), aid);
+ PromoteKeyResult result = op.execute(new PromoteKeyringParcel(mStaticRing.getMasterKeyId(), aid, null), null);
Assert.assertTrue("promotion must succeed", result.success());
{
- CanonicalizedSecretKeyRing ring = new ProviderHelper(Robolectric.application)
+ CanonicalizedSecretKeyRing ring = new ProviderHelper(RuntimeEnvironment.application)
.getCanonicalizedSecretKeyRing(mStaticRing.getMasterKeyId());
for (CanonicalizedSecretKey key : ring.secretKeyIterator()) {
@@ -147,4 +152,40 @@ public class PromoteKeyOperationTest {
}
}
+
+ @Test
+ public void testPromoteDivertSpecific() throws Exception {
+ PromoteKeyOperation op = new PromoteKeyOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null, null);
+
+ byte[] aid = Hex.decode("D2760001240102000000012345670000");
+
+ // only promote the first, rest stays dummy
+ long keyId = KeyringTestingHelper.getSubkeyId(mStaticRing, 1);
+
+ PromoteKeyResult result = op.execute(new PromoteKeyringParcel(mStaticRing.getMasterKeyId(), aid, new long[] {
+ keyId
+ }), null);
+
+ Assert.assertTrue("promotion must succeed", result.success());
+
+ {
+ CanonicalizedSecretKeyRing ring = new ProviderHelper(RuntimeEnvironment.application)
+ .getCanonicalizedSecretKeyRing(mStaticRing.getMasterKeyId());
+
+ for (CanonicalizedSecretKey key : ring.secretKeyIterator()) {
+ if (key.getKeyId() == keyId) {
+ Assert.assertEquals("subkey must be divert-to-card",
+ SecretKeyType.DIVERT_TO_CARD, key.getSecretKeyType());
+ Assert.assertArrayEquals("subkey must have correct iv",
+ aid, key.getIv());
+ } else {
+ Assert.assertEquals("some subkeys must be gnu dummy",
+ SecretKeyType.GNU_DUMMY, key.getSecretKeyType());
+ }
+ }
+
+ }
+ }
+
}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/KeyRingTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/KeyRingTest.java
index 6656c5131..64316b2a6 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/KeyRingTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/KeyRingTest.java
@@ -20,10 +20,14 @@ package org.sufficientlysecure.keychain.pgp;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.sufficientlysecure.keychain.BuildConfig;
+import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
-@RunWith(RobolectricTestRunner.class)
-@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+@RunWith(RobolectricGradleTestRunner.class)
+@Config(constants = WorkaroundBuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")
public class KeyRingTest {
@Test
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java
new file mode 100644
index 000000000..041b660c2
--- /dev/null
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java
@@ -0,0 +1,845 @@
+/*
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.pgp;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Date;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openintents.openpgp.OpenPgpDecryptionResult;
+import org.openintents.openpgp.OpenPgpMetadata;
+import org.openintents.openpgp.OpenPgpSignatureResult;
+import org.robolectric.RobolectricGradleTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLog;
+import org.spongycastle.bcpg.BCPGInputStream;
+import org.spongycastle.bcpg.Packet;
+import org.spongycastle.bcpg.PacketTags;
+import org.spongycastle.bcpg.PublicKeyEncSessionPacket;
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
+import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType;
+import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
+import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
+import org.sufficientlysecure.keychain.util.InputData;
+import org.sufficientlysecure.keychain.util.Passphrase;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+import org.sufficientlysecure.keychain.util.TestingUtils;
+
+import static org.hamcrest.core.AnyOf.anyOf;
+import static org.hamcrest.core.Is.is;
+
+
+@RunWith(RobolectricGradleTestRunner.class)
+@Config(constants = WorkaroundBuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")
+public class PgpEncryptDecryptTest {
+
+ static Passphrase mSymmetricPassphrase = TestingUtils.genPassphrase(true);
+
+ static UncachedKeyRing mStaticRing1, mStaticRing2, mStaticRingInsecure;
+ static Passphrase mKeyPhrase1 = TestingUtils.genPassphrase(true);
+ static Passphrase mKeyPhrase2 = TestingUtils.genPassphrase(true);
+ static Passphrase mKeyPhraseInsecure = TestingUtils.genPassphrase(true);
+
+ static PrintStream oldShadowStream;
+
+ @BeforeClass
+ public static void setUpOnce() throws Exception {
+ Security.insertProviderAt(new BouncyCastleProvider(), 1);
+ oldShadowStream = ShadowLog.stream;
+ // ShadowLog.stream = System.out;
+
+ PgpKeyOperation op = new PgpKeyOperation(null);
+
+ {
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
+ parcel.mAddUserIds.add("bloom");
+ parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhrase1);
+
+ PgpEditKeyResult result = op.createSecretKeyRing(parcel);
+ Assert.assertTrue("initial test key creation must succeed", result.success());
+ Assert.assertNotNull("initial test key creation must succeed", result.getRing());
+
+ mStaticRing1 = result.getRing();
+ }
+
+ {
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
+ parcel.mAddUserIds.add("belle");
+ parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhrase2);
+
+ PgpEditKeyResult result = op.createSecretKeyRing(parcel);
+ Assert.assertTrue("initial test key creation must succeed", result.success());
+ Assert.assertNotNull("initial test key creation must succeed", result.getRing());
+
+ mStaticRing2 = result.getRing();
+ }
+
+ {
+ // insecure (1024 bit) RSA key
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Algorithm.RSA, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
+ parcel.mAddUserIds.add("eve");
+ parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhraseInsecure);
+
+ PgpEditKeyResult result = op.createSecretKeyRing(parcel);
+ Assert.assertTrue("initial test key creation must succeed", result.success());
+ Assert.assertNotNull("initial test key creation must succeed", result.getRing());
+
+ mStaticRingInsecure = result.getRing();
+ }
+
+ }
+
+ @Before
+ public void setUp() {
+ ProviderHelper providerHelper = new ProviderHelper(RuntimeEnvironment.application);
+
+ // don't log verbosely here, we're not here to test imports
+ ShadowLog.stream = oldShadowStream;
+
+ providerHelper.saveSecretKeyRing(mStaticRing1, new ProgressScaler());
+ providerHelper.saveSecretKeyRing(mStaticRing2, new ProgressScaler());
+
+ // ok NOW log verbosely!
+ ShadowLog.stream = System.out;
+ }
+
+ @Test
+ public void testSymmetricEncryptDecrypt() {
+
+ String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
+ byte[] ciphertext;
+
+ { // encrypt data with a given passphrase
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
+
+ PgpSignEncryptOperation op = new PgpSignEncryptOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null);
+
+ InputData data = new InputData(in, in.available());
+
+ PgpSignEncryptInputParcel b = new PgpSignEncryptInputParcel();
+ b.setSymmetricPassphrase(mSymmetricPassphrase);
+ b.setSymmetricEncryptionAlgorithm(
+ PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128);
+
+ PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(new Date()),
+ data, out);
+
+ Assert.assertTrue("encryption must succeed", result.success());
+
+ ciphertext = out.toByteArray();
+ }
+
+ { // decryption with same passphrase should yield the same plaintext
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null);
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
+ input.setAllowSymmetricDecryption(true);
+ DecryptVerifyResult result = op.execute(
+ input, new CryptoInputParcel(mSymmetricPassphrase), data, out);
+
+ Assert.assertTrue("decryption must succeed", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext should equal plaintext",
+ out.toByteArray(), plaintext.getBytes());
+ Assert.assertEquals("decryptionResult should be RESULT_ENCRYPTED",
+ OpenPgpDecryptionResult.RESULT_ENCRYPTED, result.getDecryptionResult().getResult());
+ Assert.assertEquals("signatureResult should be RESULT_NO_SIGNATURE",
+ OpenPgpSignatureResult.RESULT_NO_SIGNATURE, result.getSignatureResult().getResult());
+
+ CryptoInputParcel cryptoInput = result.getCachedCryptoInputParcel();
+ Assert.assertEquals("cached session keys must be empty",
+ 0, cryptoInput.getCryptoData().size());
+
+ OpenPgpMetadata metadata = result.getDecryptionMetadata();
+ Assert.assertEquals("filesize must be correct",
+ out.toByteArray().length, metadata.getOriginalSize());
+ }
+
+ { // decryption with a bad passphrase should fail
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null);
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
+ input.setAllowSymmetricDecryption(true);
+ DecryptVerifyResult result = op.execute(input,
+ new CryptoInputParcel(new Passphrase(new String(mSymmetricPassphrase.getCharArray()) + "x")),
+ data, out);
+
+ Assert.assertFalse("decryption must fail", result.success());
+ Assert.assertEquals("decrypted plaintext should be empty", 0, out.size());
+ Assert.assertNull("decryptionResult should be null",
+ result.getDecryptionResult());
+ Assert.assertNull("signatureResult should be null",
+ result.getSignatureResult());
+ }
+
+ { // decryption with an unset passphrase should fail
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null);
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
+ input.setAllowSymmetricDecryption(true);
+ DecryptVerifyResult result = op.execute(input,
+ new CryptoInputParcel(), data, out);
+
+ Assert.assertFalse("decryption must fail", result.success());
+ Assert.assertEquals("decrypted plaintext should be empty", 0, out.size());
+ Assert.assertNull("decryptionResult should be null",
+ result.getDecryptionResult());
+ Assert.assertNull("signatureResult should be null",
+ result.getSignatureResult());
+ }
+
+ { // decryption if symmetric decryption isn't allowed should fail
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null);
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
+ input.setAllowSymmetricDecryption(false);
+ DecryptVerifyResult result = op.execute(input,
+ new CryptoInputParcel(), data, out);
+
+ Assert.assertFalse("decryption must fail", result.success());
+ Assert.assertEquals("decrypted plaintext should be empty", 0, out.size());
+ Assert.assertNull("decryptionResult should be null",
+ result.getDecryptionResult());
+ Assert.assertNull("signatureResult should be null",
+ result.getSignatureResult());
+ }
+
+ }
+
+ @Test
+ public void testAsymmetricEncryptDecrypt() {
+
+ String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
+ byte[] ciphertext;
+
+ { // encrypt data with key
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
+
+ PgpSignEncryptOperation op = new PgpSignEncryptOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null);
+
+ InputData data = new InputData(in, in.available());
+ PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel();
+
+ input.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() });
+ input.setSymmetricEncryptionAlgorithm(
+ PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128);
+ PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(new Date()),
+ data, out);
+ Assert.assertTrue("encryption must succeed", result.success());
+
+ ciphertext = out.toByteArray();
+ }
+
+ { // decryption with provided passphrase should yield the same result
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
+ DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1), data, out);
+
+ Assert.assertTrue("decryption with provided passphrase must succeed", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext with provided passphrase should equal plaintext",
+ out.toByteArray(), plaintext.getBytes());
+ Assert.assertEquals("decryptionResult should be RESULT_ENCRYPTED",
+ OpenPgpDecryptionResult.RESULT_ENCRYPTED, result.getDecryptionResult().getResult());
+ Assert.assertEquals("signatureResult should be RESULT_NO_SIGNATURE",
+ OpenPgpSignatureResult.RESULT_NO_SIGNATURE, result.getSignatureResult().getResult());
+
+ CryptoInputParcel cryptoInput = result.getCachedCryptoInputParcel();
+ Assert.assertEquals("must have one cached session key",
+ 1, cryptoInput.getCryptoData().size());
+
+ OpenPgpMetadata metadata = result.getDecryptionMetadata();
+ Assert.assertEquals("filesize must be correct",
+ out.toByteArray().length, metadata.getOriginalSize());
+
+ }
+
+ { // decryption with passphrase cached should succeed
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
+ mKeyPhrase1, mStaticRing1.getMasterKeyId(), null);
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
+ DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out);
+
+ CryptoInputParcel cryptoInput = result.getCachedCryptoInputParcel();
+ Assert.assertEquals("must have one cached session key",
+ 1, cryptoInput.getCryptoData().size());
+
+ Assert.assertTrue("decryption with cached passphrase must succeed", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
+ out.toByteArray(), plaintext.getBytes());
+ Assert.assertEquals("decryptionResult should be RESULT_ENCRYPTED",
+ OpenPgpDecryptionResult.RESULT_ENCRYPTED, result.getDecryptionResult().getResult());
+ Assert.assertEquals("signatureResult should be RESULT_NO_SIGNATURE",
+ OpenPgpSignatureResult.RESULT_NO_SIGNATURE, result.getSignatureResult().getResult());
+ }
+
+ { // decryption with no passphrase provided should return status pending
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
+ null, mStaticRing1.getMasterKeyId(), null);
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
+ DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out);
+
+ Assert.assertFalse("decryption with no passphrase must return pending", result.success());
+ Assert.assertTrue("decryption with no passphrase should return pending", result.isPending());
+ Assert.assertEquals("decryption with no passphrase should return pending passphrase",
+ RequiredInputType.PASSPHRASE, result.getRequiredInputParcel().mType);
+ }
+
+ }
+
+ @Test
+ public void testAsymmetricMultiSubkeyEncrypt() throws Exception {
+
+ String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
+
+ { // encrypt data with key
+ byte[] ciphertext;
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
+
+ PgpSignEncryptOperation op = new PgpSignEncryptOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null);
+
+ InputData data = new InputData(in, in.available());
+ PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel();
+
+ input.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() });
+ input.setSymmetricEncryptionAlgorithm(
+ PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128);
+ PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(new Date()),
+ data, out);
+ Assert.assertTrue("encryption must succeed", result.success());
+
+ ciphertext = out.toByteArray();
+
+ Iterator<RawPacket> packets = KeyringTestingHelper.parseKeyring(ciphertext);
+
+ RawPacket enc1 = packets.next(), enc2 = packets.next();
+ Assert.assertEquals("last packet must be encrypted data packet",
+ PacketTags.SYM_ENC_INTEGRITY_PRO, packets.next().tag);
+ Assert.assertFalse("no further packets", packets.hasNext());
+
+ Packet p;
+ p = new BCPGInputStream(new ByteArrayInputStream(enc1.buf)).readPacket();
+ Assert.assertTrue("first packet must be session packet", p instanceof PublicKeyEncSessionPacket);
+ long encKeyId1 = ((PublicKeyEncSessionPacket) p).getKeyID();
+
+ p = new BCPGInputStream(new ByteArrayInputStream(enc2.buf)).readPacket();
+ Assert.assertTrue("second packet must be session packet", p instanceof PublicKeyEncSessionPacket);
+ long encKeyId2 = ((PublicKeyEncSessionPacket) p).getKeyID();
+
+ Assert.assertNotEquals("encrypted-to subkey ids must not be equal",
+ encKeyId1, encKeyId2);
+ Assert.assertThat("first packet must be encrypted to one of the subkeys",
+ KeyringTestingHelper.getSubkeyId(mStaticRing1, 2), anyOf(is(encKeyId1), is(encKeyId2)));
+ Assert.assertThat("second packet must be encrypted to one of the subkeys",
+ KeyringTestingHelper.getSubkeyId(mStaticRing1, 3), anyOf(is(encKeyId1), is(encKeyId2)));
+
+ }
+
+ { // revoke first encryption subkey of keyring in database
+ SaveKeyringParcel parcel = new SaveKeyringParcel(mStaticRing1.getMasterKeyId(), mStaticRing1.getFingerprint());
+ parcel.mRevokeSubKeys.add(KeyringTestingHelper.getSubkeyId(mStaticRing1, 2));
+ UncachedKeyRing modified = PgpKeyOperationTest.applyModificationWithChecks(parcel, mStaticRing1,
+ new ArrayList<RawPacket>(), new ArrayList<RawPacket>(),
+ new CryptoInputParcel(new Date(), mKeyPhrase1));
+
+ ProviderHelper providerHelper = new ProviderHelper(RuntimeEnvironment.application);
+ providerHelper.saveSecretKeyRing(modified, new ProgressScaler());
+ }
+
+ { // encrypt to this keyring, make sure it's not encrypted to the revoked subkey
+ byte[] ciphertext;
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
+
+ PgpSignEncryptOperation op = new PgpSignEncryptOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null);
+
+ InputData data = new InputData(in, in.available());
+ PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel();
+
+ input.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() });
+ input.setSymmetricEncryptionAlgorithm(
+ PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128);
+ PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(new Date()),
+ data, out);
+ Assert.assertTrue("encryption must succeed", result.success());
+
+ ciphertext = out.toByteArray();
+
+ Iterator<RawPacket> packets = KeyringTestingHelper.parseKeyring(ciphertext);
+
+ RawPacket enc1 = packets.next();
+ Assert.assertEquals("last packet must be encrypted data packet",
+ PacketTags.SYM_ENC_INTEGRITY_PRO, packets.next().tag);
+ Assert.assertFalse("no further packets", packets.hasNext());
+
+ Packet p;
+ p = new BCPGInputStream(new ByteArrayInputStream(enc1.buf)).readPacket();
+ Assert.assertTrue("first packet must be session packet", p instanceof PublicKeyEncSessionPacket);
+ Assert.assertEquals("first packet must be encrypted to second enc subkey",
+ KeyringTestingHelper.getSubkeyId(mStaticRing1, 3), ((PublicKeyEncSessionPacket) p).getKeyID());
+
+ }
+
+ }
+
+ @Test
+ public void testMultiAsymmetricEncryptDecrypt() {
+
+ String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
+ byte[] ciphertext;
+
+ { // encrypt data with a given passphrase
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
+
+ PgpSignEncryptOperation op = new PgpSignEncryptOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null);
+
+ InputData data = new InputData(in, in.available());
+
+ PgpSignEncryptInputParcel b = new PgpSignEncryptInputParcel();
+ b.setEncryptionMasterKeyIds(new long[] {
+ mStaticRing1.getMasterKeyId(),
+ mStaticRing2.getMasterKeyId()
+ });
+ b.setSymmetricEncryptionAlgorithm(
+ PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128);
+
+ PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(new Date()),
+ data, out);
+ Assert.assertTrue("encryption must succeed", result.success());
+
+ ciphertext = out.toByteArray();
+ }
+
+ { // decryption with passphrase cached should succeed for the first key
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
+ mKeyPhrase1, mStaticRing1.getMasterKeyId(), null);
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
+ DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out);
+
+ Assert.assertTrue("decryption with cached passphrase must succeed for the first key", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
+ out.toByteArray(), plaintext.getBytes());
+ Assert.assertEquals("decryptionResult should be RESULT_ENCRYPTED",
+ OpenPgpDecryptionResult.RESULT_ENCRYPTED, result.getDecryptionResult().getResult());
+ Assert.assertEquals("signatureResult should be RESULT_NO_SIGNATURE",
+ OpenPgpSignatureResult.RESULT_NO_SIGNATURE, result.getSignatureResult().getResult());
+
+ OpenPgpMetadata metadata = result.getDecryptionMetadata();
+ Assert.assertEquals("filesize must be correct",
+ out.toByteArray().length, metadata.getOriginalSize());
+ }
+
+ { // decryption should succeed if key is allowed
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ // allow only the second to decrypt
+ HashSet<Long> allowed = new HashSet<>();
+ allowed.add(mStaticRing2.getMasterKeyId());
+
+ // provide passphrase for the second, and check that the first is never asked for!
+ PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
+ mKeyPhrase2, mStaticRing2.getMasterKeyId(), null);
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
+ input.setAllowedKeyIds(allowed);
+ DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out);
+
+ Assert.assertTrue("decryption with cached passphrase must succeed for allowed key", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
+ out.toByteArray(), plaintext.getBytes());
+ Assert.assertTrue("other key was skipped", result.getLog().containsType(LogType.MSG_DC_ASKIP_NOT_ALLOWED));
+ Assert.assertEquals("decryptionResult should be RESULT_ENCRYPTED",
+ OpenPgpDecryptionResult.RESULT_ENCRYPTED, result.getDecryptionResult().getResult());
+ Assert.assertEquals("signatureResult should be RESULT_NO_SIGNATURE",
+ OpenPgpSignatureResult.RESULT_NO_SIGNATURE, result.getSignatureResult().getResult());
+ }
+
+ { // decryption should fail if no key is allowed
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ // provide passphrase for the second, and check that the first is never asked for!
+ PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
+ mKeyPhrase2, mStaticRing2.getMasterKeyId(), null);
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
+ input.setAllowedKeyIds(new HashSet<Long>());
+ DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out);
+
+ Assert.assertFalse("decryption must fail if no key allowed", result.success());
+ Assert.assertEquals("decryption must fail with key disllowed status",
+ DecryptVerifyResult.RESULT_KEY_DISALLOWED, result.getResult());
+
+ }
+
+ { // decryption with passphrase cached should succeed for the other key if first is gone
+
+ // delete first key from database
+ new ProviderHelper(RuntimeEnvironment.application).getContentResolver().delete(
+ KeyRingData.buildPublicKeyRingUri(mStaticRing1.getMasterKeyId()), null, null
+ );
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
+ mKeyPhrase2, mStaticRing2.getMasterKeyId(), null);
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
+ DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out);
+
+ Assert.assertTrue("decryption with cached passphrase must succeed", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
+ out.toByteArray(), plaintext.getBytes());
+ Assert.assertEquals("decryptionResult should be RESULT_ENCRYPTED",
+ OpenPgpDecryptionResult.RESULT_ENCRYPTED, result.getDecryptionResult().getResult());
+ Assert.assertEquals("signatureResult should be RESULT_NO_SIGNATURE",
+ OpenPgpSignatureResult.RESULT_NO_SIGNATURE, result.getSignatureResult().getResult());
+ }
+
+ }
+
+ @Test
+ public void testMultiAsymmetricSignEncryptDecryptVerify() {
+
+ String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
+ byte[] ciphertext;
+
+ { // encrypt data with a given passphrase
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
+
+ PgpSignEncryptOperation op = new PgpSignEncryptOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null);
+
+ InputData data = new InputData(in, in.available());
+ PgpSignEncryptInputParcel b = new PgpSignEncryptInputParcel();
+
+ b.setEncryptionMasterKeyIds(new long[] {
+ mStaticRing1.getMasterKeyId(),
+ mStaticRing2.getMasterKeyId()
+ });
+ b.setSignatureMasterKeyId(mStaticRing1.getMasterKeyId());
+ b.setSignatureSubKeyId(KeyringTestingHelper.getSubkeyId(mStaticRing1, 1));
+ b.setSymmetricEncryptionAlgorithm(
+ PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128);
+
+ PgpSignEncryptResult result = op.execute(b,
+ new CryptoInputParcel(new Date(), mKeyPhrase1), data, out);
+ Assert.assertTrue("encryption must succeed", result.success());
+
+ ciphertext = out.toByteArray();
+ }
+
+ { // decryption with passphrase cached should succeed for the first key
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
+ mKeyPhrase1, mStaticRing1.getMasterKeyId(), null);
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
+ DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out);
+
+ Assert.assertTrue("decryption with cached passphrase must succeed for the first key", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
+ out.toByteArray(), plaintext.getBytes());
+ Assert.assertEquals("signature should be verified and certified",
+ OpenPgpSignatureResult.RESULT_VALID_CONFIRMED, result.getSignatureResult().getResult());
+
+ OpenPgpMetadata metadata = result.getDecryptionMetadata();
+ Assert.assertEquals("filesize must be correct",
+ out.toByteArray().length, metadata.getOriginalSize());
+ }
+
+ { // decryption with passphrase cached should succeed for the other key if first is gone
+
+ // delete first key from database
+ new ProviderHelper(RuntimeEnvironment.application).getContentResolver().delete(
+ KeyRingData.buildPublicKeyRingUri(mStaticRing1.getMasterKeyId()), null, null
+ );
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(
+ mKeyPhrase2, mStaticRing2.getMasterKeyId(), null);
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
+ DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(), data, out);
+
+ Assert.assertTrue("decryption with cached passphrase must succeed", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
+ out.toByteArray(), plaintext.getBytes());
+ Assert.assertEquals("signature key should be missing",
+ OpenPgpSignatureResult.RESULT_KEY_MISSING,
+ result.getSignatureResult().getResult());
+ }
+
+ }
+
+ @Test
+ public void testForeignEncoding() throws Exception {
+ String plaintext = "ウィキペディア";
+ byte[] plaindata = plaintext.getBytes("iso-2022-jp");
+
+ { // some quick sanity checks
+ Assert.assertEquals(plaintext, new String(plaindata, "iso-2022-jp"));
+ Assert.assertNotEquals(plaintext, new String(plaindata, "utf-8"));
+ }
+
+ byte[] ciphertext;
+ { // encrypt data with a given passphrase
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(plaindata);
+
+ PgpSignEncryptOperation op = new PgpSignEncryptOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null);
+
+ InputData data = new InputData(in, in.available());
+ PgpSignEncryptInputParcel b = new PgpSignEncryptInputParcel();
+
+ b.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() });
+ b.setSymmetricEncryptionAlgorithm(
+ PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_128);
+ // this only works with ascii armored output!
+ b.setEnableAsciiArmorOutput(true);
+ b.setCharset("iso-2022-jp");
+ PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(new Date()),
+ data, out);
+ Assert.assertTrue("encryption must succeed", result.success());
+
+ ciphertext = out.toByteArray();
+ }
+
+ { // decryption with provided passphrase should yield the same result
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
+ DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(mKeyPhrase1), data, out);
+
+ Assert.assertTrue("decryption with provided passphrase must succeed", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext should equal plaintext bytes",
+ out.toByteArray(), plaindata);
+ Assert.assertEquals("charset should be read correctly",
+ "iso-2022-jp", result.getCharset());
+ Assert.assertEquals("decrypted ciphertext should equal plaintext",
+ new String(out.toByteArray(), result.getCharset()), plaintext);
+ Assert.assertEquals("decryptionResult should be RESULT_ENCRYPTED",
+ OpenPgpDecryptionResult.RESULT_ENCRYPTED, result.getDecryptionResult().getResult());
+ Assert.assertEquals("signatureResult should be RESULT_NO_SIGNATURE",
+ OpenPgpSignatureResult.RESULT_NO_SIGNATURE, result.getSignatureResult().getResult());
+ }
+
+ }
+
+ @Test
+ public void testAsymmetricInsecureEncryptDecrypt() {
+
+ // insecure symmetric algo
+ subtestInsecureEncryptDecrypt(mStaticRing1, mKeyPhrase1,
+ PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.DES, true);
+ // don't use MDC
+ subtestInsecureEncryptDecrypt(mStaticRing1, mKeyPhrase1,
+ PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_256, false);
+ // TODO: test not working!
+ // insecure key (1024 bit RSA)
+// subtestInsecureEncryptDecrypt(mStaticRingInsecure, mKeyPhraseInsecure,
+// PgpSecurityConstants.OpenKeychainSymmetricKeyAlgorithmTags.AES_256, true);
+
+ }
+
+ private void subtestInsecureEncryptDecrypt(UncachedKeyRing key, Passphrase passphrase,
+ int algorithm, boolean isIntegrityProtected) {
+ String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
+ byte[] ciphertext;
+
+ { // encrypt data with insecure key
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
+
+ PgpSignEncryptOperation op = new PgpSignEncryptOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null);
+
+ InputData data = new InputData(in, in.available());
+ PgpSignEncryptInputParcel input = new PgpSignEncryptInputParcel();
+
+ input.setEncryptionMasterKeyIds(new long[]{key.getMasterKeyId()})
+ .setSymmetricEncryptionAlgorithm(algorithm)
+ .setIntegrityProtected(isIntegrityProtected);
+ PgpSignEncryptResult result = op.execute(input, new CryptoInputParcel(new Date()),
+ data, out);
+ Assert.assertTrue("encryption must succeed", result.success());
+
+ ciphertext = out.toByteArray();
+ }
+
+ { // decryption with provided passphrase should yield insecure status
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerifyOperation op = operationWithFakePassphraseCache(null, null, null);
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel();
+ DecryptVerifyResult result = op.execute(input, new CryptoInputParcel(passphrase), data, out);
+
+ Assert.assertTrue("decryption with provided passphrase must succeed", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext with provided passphrase should equal plaintext",
+ out.toByteArray(), plaintext.getBytes());
+ Assert.assertEquals("decryptionResult should be RESULT_INSECURE",
+ OpenPgpDecryptionResult.RESULT_INSECURE, result.getDecryptionResult().getResult());
+ Assert.assertEquals("signatureResult should be RESULT_NO_SIGNATURE",
+ OpenPgpSignatureResult.RESULT_NO_SIGNATURE, result.getSignatureResult().getResult());
+
+ CryptoInputParcel cryptoInput = result.getCachedCryptoInputParcel();
+ Assert.assertEquals("must have one cached session key",
+ 1, cryptoInput.getCryptoData().size());
+
+ OpenPgpMetadata metadata = result.getDecryptionMetadata();
+ Assert.assertEquals("filesize must be correct",
+ out.toByteArray().length, metadata.getOriginalSize());
+
+ }
+ }
+
+ private PgpDecryptVerifyOperation operationWithFakePassphraseCache(
+ final Passphrase passphrase, final Long checkMasterKeyId, final Long checkSubKeyId) {
+
+ return new PgpDecryptVerifyOperation(RuntimeEnvironment.application,
+ new ProviderHelper(RuntimeEnvironment.application), null) {
+ @Override
+ public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId)
+ throws NoSecretKeyException {
+ if (checkMasterKeyId != null) {
+ Assert.assertEquals("requested passphrase should be for expected master key id",
+ (long) checkMasterKeyId, masterKeyId);
+ }
+ if (checkSubKeyId != null) {
+ Assert.assertEquals("requested passphrase should be for expected sub key id",
+ (long) checkSubKeyId, subKeyId);
+ }
+ if (passphrase == null) {
+ return null;
+ }
+ return passphrase;
+ }
+ };
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java
index 54ccccc3d..c0e28cd4b 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java
@@ -26,6 +26,7 @@ import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.robolectric.*;
+import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import org.spongycastle.bcpg.BCPGInputStream;
import org.spongycastle.bcpg.Packet;
@@ -40,6 +41,7 @@ import org.spongycastle.bcpg.UserIDPacket;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.openpgp.PGPSignature;
+import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
@@ -50,6 +52,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockPar
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType;
import org.sufficientlysecure.keychain.support.KeyringBuilder;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
@@ -69,8 +72,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Random;
-@RunWith(RobolectricTestRunner.class)
-@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+@RunWith(RobolectricGradleTestRunner.class)
+@Config(constants = WorkaroundBuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")
public class PgpKeyOperationTest {
static UncachedKeyRing staticRing;
@@ -91,14 +94,22 @@ public class PgpKeyOperationTest {
SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
+ Algorithm.DSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.DSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
+ Algorithm.RSA, 2048, null, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.ELGAMAL, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
+ Algorithm.RSA, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("twi");
parcel.mAddUserIds.add("pink");
+
+ {
+ int type = 42;
+ byte[] data = new byte[] { 0, 1, 2, 3, 4 };
+ WrappedUserAttribute uat = WrappedUserAttribute.fromSubpacket(type, data);
+ parcel.mAddUserAttribute.add(uat);
+ }
+
parcel.mNewUnlock = new ChangeUnlockParcel(passphrase);
PgpKeyOperation op = new PgpKeyOperation(null);
@@ -231,6 +242,17 @@ public class PgpKeyOperationTest {
Assert.assertEquals("number of user ids must be two",
2, ring.getPublicKey().getUnorderedUserIds().size());
+ ArrayList<WrappedUserAttribute> attributes =
+ ring.getPublicKey().getUnorderedUserAttributes();
+ Assert.assertEquals("number of user attributes must be one",
+ 1, attributes.size());
+ Assert.assertEquals("user attribute must be correct type",
+ 42, attributes.get(0).getType());
+ Assert.assertEquals("user attribute must have one subpacket",
+ 1, attributes.get(0).getSubpackets().length);
+ Assert.assertArrayEquals("user attribute must have correct data",
+ new byte[] { 0, 1, 2, 3, 4 }, attributes.get(0).getSubpackets()[0]);
+
List<UncachedPublicKey> subkeys = KeyringTestingHelper.itToList(ring.getPublicKeys());
Assert.assertEquals("number of subkeys must be three", 3, subkeys.size());
@@ -735,7 +757,7 @@ public class PgpKeyOperationTest {
public void testSubkeyStrip() throws Exception {
long keyId = KeyringTestingHelper.getSubkeyId(ring, 1);
- parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, null));
+ parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, false));
applyModificationWithChecks(parcel, ring, onlyA, onlyB);
Assert.assertEquals("one extra packet in original", 1, onlyA.size());
@@ -761,7 +783,7 @@ public class PgpKeyOperationTest {
public void testMasterStrip() throws Exception {
long keyId = ring.getMasterKeyId();
- parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, null));
+ parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, false));
applyModificationWithChecks(parcel, ring, onlyA, onlyB);
Assert.assertEquals("one extra packet in original", 1, onlyA.size());
@@ -788,9 +810,9 @@ public class PgpKeyOperationTest {
long keyId = KeyringTestingHelper.getSubkeyId(ring, 1);
UncachedKeyRing modified;
- { // we should be able to change the stripped/divert status of subkeys without passphrase
+ { // we should be able to change the stripped status of subkeys without passphrase
parcel.reset();
- parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, null));
+ parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, false));
modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB, new CryptoInputParcel());
Assert.assertEquals("one extra packet in modified", 1, onlyB.size());
Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
@@ -800,14 +822,66 @@ public class PgpKeyOperationTest {
S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY, ((SecretKeyPacket) p).getS2K().getProtectionMode());
}
- { // and again, changing to divert-to-card
+ { // trying to edit a subkey with signing capability should fail
parcel.reset();
+ parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true));
+
+ assertModifyFailure("subkey modification for signing-enabled but stripped subkey should fail",
+ modified, parcel, LogType.MSG_MF_ERROR_SUB_STRIPPED);
+ }
+
+ }
+
+ @Test
+ public void testKeyToCard() throws Exception {
+
+ UncachedKeyRing modified;
+
+ { // keytocard should fail with BAD_NFC_SIZE when presented with the RSA-1024 key
+ long keyId = KeyringTestingHelper.getSubkeyId(ring, 2);
+ parcel.reset();
+ parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, true));
+
+ assertModifyFailure("keytocard operation should fail on invalid key size", ring,
+ parcel, cryptoInput, LogType.MSG_MF_ERROR_BAD_NFC_SIZE);
+ }
+
+ { // keytocard should fail with BAD_NFC_ALGO when presented with the DSA-1024 key
+ long keyId = KeyringTestingHelper.getSubkeyId(ring, 0);
+ parcel.reset();
+ parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, true));
+
+ assertModifyFailure("keytocard operation should fail on invalid key algorithm", ring,
+ parcel, cryptoInput, LogType.MSG_MF_ERROR_BAD_NFC_ALGO);
+ }
+
+ long keyId = KeyringTestingHelper.getSubkeyId(ring, 1);
+
+ { // keytocard should return a pending NFC_MOVE_KEY_TO_CARD result when presented with the RSA-2048
+ // key, and then make key divert-to-card when it gets a serial in the cryptoInputParcel.
+ parcel.reset();
+ parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, true));
+
+ CanonicalizedSecretKeyRing secretRing =
+ new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ PgpKeyOperation op = new PgpKeyOperation(null);
+ PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, cryptoInput, parcel);
+ Assert.assertTrue("keytocard operation should be pending", result.isPending());
+ Assert.assertEquals("required input should be RequiredInputType.NFC_MOVE_KEY_TO_CARD",
+ result.getRequiredInputParcel().mType, RequiredInputType.NFC_MOVE_KEY_TO_CARD);
+
+ // Create a cryptoInputParcel that matches what the NFCOperationActivity would return.
+ byte[] keyIdBytes = new byte[8];
+ ByteBuffer buf = ByteBuffer.wrap(keyIdBytes);
+ buf.putLong(keyId).rewind();
byte[] serial = new byte[] {
0x6a, 0x6f, 0x6c, 0x6f, 0x73, 0x77, 0x61, 0x67,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
- parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, serial));
- modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB, new CryptoInputParcel());
+ CryptoInputParcel inputParcel = new CryptoInputParcel();
+ inputParcel.addCryptoData(keyIdBytes, serial);
+
+ modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB, inputParcel);
Assert.assertEquals("one extra packet in modified", 1, onlyB.size());
Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
Assert.assertEquals("new packet should have GNU_DUMMY S2K type",
@@ -816,7 +890,19 @@ public class PgpKeyOperationTest {
S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD, ((SecretKeyPacket) p).getS2K().getProtectionMode());
Assert.assertArrayEquals("new packet should have correct serial number as iv",
serial, ((SecretKeyPacket) p).getIV());
+ }
+ { // editing a signing subkey requires a primary key binding sig -> pendinginput
+ parcel.reset();
+ parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true));
+
+ CanonicalizedSecretKeyRing secretRing =
+ new CanonicalizedSecretKeyRing(modified.getEncoded(), false, 0);
+ PgpKeyOperation op = new PgpKeyOperation(null);
+ PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, cryptoInput, parcel);
+ Assert.assertTrue("keytocard operation should be pending", result.isPending());
+ Assert.assertEquals("required input should be RequiredInputType.NFC_SIGN",
+ RequiredInputType.NFC_SIGN, result.getRequiredInputParcel().mType);
}
}
@@ -950,7 +1036,7 @@ public class PgpKeyOperationTest {
parcel.reset();
Random r = new Random();
- int type = r.nextInt(110)+1;
+ int type = r.nextInt(110)+2; // any type except image attribute, to avoid interpretation of these
byte[] data = new byte[r.nextInt(2000)];
new Random().nextBytes(data);
@@ -1176,14 +1262,14 @@ public class PgpKeyOperationTest {
Assert.assertFalse("non-restricted operations should fail without passphrase", result.success());
}
- private static UncachedKeyRing applyModificationWithChecks(SaveKeyringParcel parcel,
+ public static UncachedKeyRing applyModificationWithChecks(SaveKeyringParcel parcel,
UncachedKeyRing ring,
ArrayList<RawPacket> onlyA,
ArrayList<RawPacket> onlyB) {
return applyModificationWithChecks(parcel, ring, onlyA, onlyB, cryptoInput, true, true);
}
- private static UncachedKeyRing applyModificationWithChecks(SaveKeyringParcel parcel,
+ public static UncachedKeyRing applyModificationWithChecks(SaveKeyringParcel parcel,
UncachedKeyRing ring,
ArrayList<RawPacket> onlyA,
ArrayList<RawPacket> onlyB,
@@ -1192,7 +1278,7 @@ public class PgpKeyOperationTest {
}
// applies a parcel modification while running some integrity checks
- private static UncachedKeyRing applyModificationWithChecks(SaveKeyringParcel parcel,
+ public static UncachedKeyRing applyModificationWithChecks(SaveKeyringParcel parcel,
UncachedKeyRing ring,
ArrayList<RawPacket> onlyA,
ArrayList<RawPacket> onlyB,
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java
index 2b184c075..5e552fecc 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java
@@ -23,7 +23,9 @@ import org.junit.runner.RunWith;
import org.junit.Assert;
import org.junit.Test;
import org.junit.Before;
+import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import org.spongycastle.bcpg.BCPGInputStream;
import org.spongycastle.bcpg.HashAlgorithmTags;
@@ -51,7 +53,9 @@ import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBu
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.spongycastle.util.Strings;
+import org.sufficientlysecure.keychain.BuildConfig;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
@@ -78,8 +82,8 @@ import java.util.Iterator;
* Test cases are made for all its assertions.
*/
-@RunWith(RobolectricTestRunner.class)
-@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+@RunWith(RobolectricGradleTestRunner.class)
+@Config(constants = WorkaroundBuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")
public class UncachedKeyringCanonicalizeTest {
static UncachedKeyRing staticRing;
@@ -98,11 +102,11 @@ public class UncachedKeyringCanonicalizeTest {
SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
+ Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
+ Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.RSA, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
+ Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("twi");
parcel.mAddUserIds.add("pink");
@@ -550,12 +554,13 @@ public class UncachedKeyringCanonicalizeTest {
CanonicalizedSecretKey masterSecretKey = canonicalized.getSecretKey();
masterSecretKey.unlock(new Passphrase());
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
- CryptoInputParcel cryptoInput = new CryptoInputParcel();
+ CryptoInputParcel cryptoInput = new CryptoInputParcel(new Date());
PGPSignature cert = PgpKeyOperation.generateSubkeyBindingSignature(
PgpKeyOperation.getSignatureGenerator(masterSecretKey.getSecretKey(), cryptoInput),
cryptoInput.getSignatureTime(),
- masterPublicKey, masterSecretKey.getPrivateKey(), masterSecretKey.getPrivateKey(),
- masterPublicKey, masterSecretKey.getKeyUsage(), 0);
+ masterPublicKey, masterSecretKey.getPrivateKey(),
+ PgpKeyOperation.getSignatureGenerator(masterSecretKey.getSecretKey(), null),
+ masterSecretKey.getPrivateKey(), masterPublicKey, masterSecretKey.getKeyUsage(), 0);
PGPPublicKey subPubKey = PGPPublicKey.addSubkeyBindingCertification(masterPublicKey, cert);
PGPSecretKey sKey;
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java
index 6d855ea93..90189e377 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java
@@ -23,7 +23,9 @@ import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import org.spongycastle.bcpg.BCPGInputStream;
import org.spongycastle.bcpg.PacketTags;
@@ -32,6 +34,8 @@ import org.spongycastle.bcpg.SecretKeyPacket;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.util.Strings;
+import org.sufficientlysecure.keychain.BuildConfig;
+import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
@@ -77,8 +81,8 @@ import java.util.Random;
* packet will be copied regardless. Filtering out bad packets is done with canonicalization.
*
*/
-@RunWith(RobolectricTestRunner.class)
-@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+@RunWith(RobolectricGradleTestRunner.class)
+@Config(constants = WorkaroundBuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")
public class UncachedKeyringMergeTest {
static UncachedKeyRing staticRingA, staticRingB;
@@ -97,9 +101,9 @@ public class UncachedKeyringMergeTest {
{
SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
+ Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
+ Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddUserIds.add("twi");
parcel.mAddUserIds.add("pink");
@@ -123,7 +127,7 @@ public class UncachedKeyringMergeTest {
{
SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
+ Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddUserIds.add("shy");
// passphrase is tested in PgpKeyOperationTest, just use empty here
@@ -190,11 +194,11 @@ public class UncachedKeyringMergeTest {
parcel.reset();
parcel.mAddUserIds.add("flim");
- modifiedA = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Passphrase()), parcel).getRing();
+ modifiedA = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing();
parcel.reset();
parcel.mAddUserIds.add("flam");
- modifiedB = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Passphrase()), parcel).getRing();
+ modifiedB = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing();
}
{ // merge A into base
@@ -231,8 +235,8 @@ public class UncachedKeyringMergeTest {
parcel.reset();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
- modifiedA = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Passphrase()), parcel).getRing();
- modifiedB = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Passphrase()), parcel).getRing();
+ modifiedA = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing();
+ modifiedB = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing();
subKeyIdA = KeyringTestingHelper.getSubkeyId(modifiedA, 2);
subKeyIdB = KeyringTestingHelper.getSubkeyId(modifiedB, 2);
@@ -273,7 +277,7 @@ public class UncachedKeyringMergeTest {
parcel.mRevokeSubKeys.add(KeyringTestingHelper.getSubkeyId(ringA, 1));
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
ringA.getEncoded(), false, 0);
- modified = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Passphrase()), parcel).getRing();
+ modified = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing();
}
{
@@ -372,7 +376,7 @@ public class UncachedKeyringMergeTest {
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
ringA.getEncoded(), false, 0);
- modified = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Passphrase()), parcel).getRing();
+ modified = op.modifySecretKeyRing(secretRing, new CryptoInputParcel(new Date(), new Passphrase()), parcel).getRing();
}
{
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java
index a6159de4e..4cdcf0117 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java
@@ -23,9 +23,13 @@ import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import org.spongycastle.bcpg.sig.KeyFlags;
+import org.sufficientlysecure.keychain.BuildConfig;
+import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
@@ -40,8 +44,8 @@ import java.io.IOException;
import java.util.Iterator;
import java.util.Random;
-@RunWith(RobolectricTestRunner.class)
-@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+@RunWith(RobolectricGradleTestRunner.class)
+@Config(constants = WorkaroundBuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")
public class UncachedKeyringTest {
static UncachedKeyRing staticRing, staticPubRing;
@@ -53,11 +57,11 @@ public class UncachedKeyringTest {
SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
+ Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
+ Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
- Algorithm.RSA, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
+ Algorithm.ECDH, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("twi");
parcel.mAddUserIds.add("pink");
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperKeyringTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperKeyringTest.java
index b0f47af29..6779784a2 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperKeyringTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperKeyringTest.java
@@ -26,22 +26,25 @@ import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.*;
+import org.robolectric.annotation.Config;
+import org.sufficientlysecure.keychain.BuildConfig;
+import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
-@RunWith(RobolectricTestRunner.class)
-@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+@RunWith(RobolectricGradleTestRunner.class)
+@Config(constants = WorkaroundBuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")
public class ProviderHelperKeyringTest {
@Test
public void testSavePublicKeyring() throws Exception {
- Assert.assertTrue(new KeyringTestingHelper(Robolectric.application).addKeyring(Collections.singleton(
+ Assert.assertTrue(new KeyringTestingHelper(RuntimeEnvironment.application).addKeyring(Collections.singleton(
"/public-key-for-sample.blob"
)));
}
// @Test
public void testSavePublicKeyringRsa() throws Exception {
- Assert.assertTrue(new KeyringTestingHelper(Robolectric.application).addKeyring(prependResourcePath(Arrays.asList(
+ Assert.assertTrue(new KeyringTestingHelper(RuntimeEnvironment.application).addKeyring(prependResourcePath(Arrays.asList(
"000001-006.public_key",
"000002-013.user_id",
"000003-002.sig",
@@ -62,7 +65,7 @@ public class ProviderHelperKeyringTest {
// @Test
public void testSavePublicKeyringDsa() throws Exception {
- Assert.assertTrue(new KeyringTestingHelper(Robolectric.application).addKeyring(prependResourcePath(Arrays.asList(
+ Assert.assertTrue(new KeyringTestingHelper(RuntimeEnvironment.application).addKeyring(prependResourcePath(Arrays.asList(
"000016-006.public_key",
"000017-002.sig",
"000018-012.ring_trust",
@@ -79,7 +82,7 @@ public class ProviderHelperKeyringTest {
// @Test
public void testSavePublicKeyringDsa2() throws Exception {
- Assert.assertTrue(new KeyringTestingHelper(Robolectric.application).addKeyring(prependResourcePath(Arrays.asList(
+ Assert.assertTrue(new KeyringTestingHelper(RuntimeEnvironment.application).addKeyring(prependResourcePath(Arrays.asList(
"000027-006.public_key",
"000028-002.sig",
"000029-012.ring_trust",
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperSaveTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperSaveTest.java
index 171ecc377..04851ebbb 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperSaveTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperSaveTest.java
@@ -23,10 +23,15 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
+import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.util.encoders.Hex;
+import org.sufficientlysecure.keychain.BuildConfig;
+import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
@@ -40,11 +45,11 @@ import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.util.Arrays;
import java.util.Iterator;
-@RunWith(RobolectricTestRunner.class)
-@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+@RunWith(RobolectricGradleTestRunner.class)
+@Config(constants = WorkaroundBuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")
public class ProviderHelperSaveTest {
- ProviderHelper mProviderHelper = new ProviderHelper(Robolectric.application);
+ ProviderHelper mProviderHelper = new ProviderHelper(RuntimeEnvironment.application);
@BeforeClass
public static void setUpOnce() throws Exception {
@@ -62,17 +67,17 @@ public class ProviderHelperSaveTest {
SaveKeyringResult result;
// insert both keys, second should fail
- result = new ProviderHelper(Robolectric.application).savePublicKeyRing(first);
+ result = new ProviderHelper(RuntimeEnvironment.application).savePublicKeyRing(first);
Assert.assertTrue("first keyring import should succeed", result.success());
- result = new ProviderHelper(Robolectric.application).savePublicKeyRing(second);
+ result = new ProviderHelper(RuntimeEnvironment.application).savePublicKeyRing(second);
Assert.assertFalse("second keyring import should fail", result.success());
- new KeychainDatabase(Robolectric.application).clearDatabase();
+ new KeychainDatabase(RuntimeEnvironment.application).clearDatabase();
// and the other way around
- result = new ProviderHelper(Robolectric.application).savePublicKeyRing(second);
+ result = new ProviderHelper(RuntimeEnvironment.application).savePublicKeyRing(second);
Assert.assertTrue("first keyring import should succeed", result.success());
- result = new ProviderHelper(Robolectric.application).savePublicKeyRing(first);
+ result = new ProviderHelper(RuntimeEnvironment.application).savePublicKeyRing(first);
Assert.assertFalse("second keyring import should fail", result.success());
}
@@ -91,13 +96,13 @@ public class ProviderHelperSaveTest {
SaveKeyringResult result;
// insert secret, this should fail because of missing self-cert
- result = new ProviderHelper(Robolectric.application).saveSecretKeyRing(seckey, new ProgressScaler());
+ result = new ProviderHelper(RuntimeEnvironment.application).saveSecretKeyRing(seckey, new ProgressScaler());
Assert.assertFalse("secret keyring import before pubring import should fail", result.success());
// insert pubkey, then seckey - both should succeed
- result = new ProviderHelper(Robolectric.application).savePublicKeyRing(pubkey);
+ result = new ProviderHelper(RuntimeEnvironment.application).savePublicKeyRing(pubkey);
Assert.assertTrue("public keyring import should succeed", result.success());
- result = new ProviderHelper(Robolectric.application).saveSecretKeyRing(seckey, new ProgressScaler());
+ result = new ProviderHelper(RuntimeEnvironment.application).saveSecretKeyRing(seckey, new ProgressScaler());
Assert.assertTrue("secret keyring import after pubring import should succeed", result.success());
}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringBuilder.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/support/KeyringBuilder.java
index 94193bbcb..94193bbcb 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringBuilder.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/support/KeyringBuilder.java
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java
index 4ac994c99..4ac994c99 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/ProviderHelperStub.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/support/ProviderHelperStub.java
index 0573f07fb..0573f07fb 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/ProviderHelperStub.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/support/ProviderHelperStub.java
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/TestDataUtil.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/support/TestDataUtil.java
index f2b3c0996..f2b3c0996 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/TestDataUtil.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/support/TestDataUtil.java
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/Iso7816TLVTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/util/Iso7816TLVTest.java
index fccecc3cc..f6888588e 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/Iso7816TLVTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/util/Iso7816TLVTest.java
@@ -19,13 +19,17 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import org.spongycastle.util.encoders.Hex;
+import org.sufficientlysecure.keychain.BuildConfig;
+import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.util.Iso7816TLV.Iso7816CompositeTLV;
-@RunWith(RobolectricTestRunner.class)
-@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+@RunWith(RobolectricGradleTestRunner.class)
+@Config(constants = WorkaroundBuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")
public class Iso7816TLVTest {
@Before
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/ParcelableFileCacheTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/util/ParcelableFileCacheTest.java
index 7a48d226f..7b97ebdae 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/ParcelableFileCacheTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/util/ParcelableFileCacheTest.java
@@ -25,16 +25,21 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
+import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
+import org.sufficientlysecure.keychain.BuildConfig;
+import org.sufficientlysecure.keychain.WorkaroundBuildConfig;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
-@RunWith(RobolectricTestRunner.class)
-@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+@RunWith(RobolectricGradleTestRunner.class)
+@Config(constants = WorkaroundBuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")
public class ParcelableFileCacheTest {
@Before
@@ -45,7 +50,7 @@ public class ParcelableFileCacheTest {
@Test
public void testInputOutput() throws Exception {
- ParcelableFileCache<Bundle> cache = new ParcelableFileCache<Bundle>(Robolectric.application, "test.pcl");
+ ParcelableFileCache<Bundle> cache = new ParcelableFileCache<Bundle>(RuntimeEnvironment.application, "test.pcl");
ArrayList<Bundle> list = new ArrayList<Bundle>();
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/TestingUtils.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/util/TestingUtils.java
index 1d7952464..1d7952464 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/TestingUtils.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/util/TestingUtils.java
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/COPYING b/OpenKeychain/src/test/resources/OpenPGP-Haskell/COPYING
index 55234e7a0..55234e7a0 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/COPYING
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/COPYING
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/README b/OpenKeychain/src/test/resources/OpenPGP-Haskell/README
index cff696c83..cff696c83 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/README
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/README
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000001-006.public_key b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000001-006.public_key
index 7cbab1782..7cbab1782 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000001-006.public_key
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000001-006.public_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000002-013.user_id b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000002-013.user_id
index 759449bb4..759449bb4 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000002-013.user_id
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000002-013.user_id
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000003-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000003-002.sig
index 1e0656d27..1e0656d27 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000003-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000003-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000004-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000004-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000004-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000004-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000005-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000005-002.sig
index 108b99842..108b99842 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000005-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000005-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000006-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000006-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000006-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000006-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000007-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000007-002.sig
index 14276d0a5..14276d0a5 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000007-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000007-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000008-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000008-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000008-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000008-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000009-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000009-002.sig
index 4a282dd68..4a282dd68 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000009-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000009-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000010-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000010-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000010-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000010-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000011-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000011-002.sig
index cae1b7391..cae1b7391 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000011-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000011-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000012-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000012-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000012-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000012-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000013-014.public_subkey b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000013-014.public_subkey
index 08676d067..08676d067 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000013-014.public_subkey
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000013-014.public_subkey
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000014-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000014-002.sig
index dd601807f..dd601807f 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000014-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000014-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000015-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000015-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000015-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000015-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000016-006.public_key b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000016-006.public_key
index c9dccbf1e..c9dccbf1e 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000016-006.public_key
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000016-006.public_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000017-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000017-002.sig
index e734505a7..e734505a7 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000017-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000017-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000018-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000018-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000018-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000018-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000019-013.user_id b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000019-013.user_id
index ab3f51d91..ab3f51d91 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000019-013.user_id
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000019-013.user_id
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000020-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000020-002.sig
index 8588489a7..8588489a7 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000020-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000020-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000021-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000021-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000021-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000021-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000022-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000022-002.sig
index fefcb5fea..fefcb5fea 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000022-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000022-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000023-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000023-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000023-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000023-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000024-014.public_subkey b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000024-014.public_subkey
index 2e8deea28..2e8deea28 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000024-014.public_subkey
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000024-014.public_subkey
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000025-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000025-002.sig
index a3eea0a20..a3eea0a20 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000025-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000025-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000026-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000026-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000026-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000026-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000027-006.public_key b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000027-006.public_key
index 5817e0037..5817e0037 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000027-006.public_key
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000027-006.public_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000028-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000028-002.sig
index 5194b7840..5194b7840 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000028-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000028-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000029-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000029-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000029-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000029-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000030-013.user_id b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000030-013.user_id
index fb3f49e0d..fb3f49e0d 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000030-013.user_id
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000030-013.user_id
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000031-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000031-002.sig
index f69f6875b..f69f6875b 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000031-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000031-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000032-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000032-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000032-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000032-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000033-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000033-002.sig
index 2bb55d4fe..2bb55d4fe 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000033-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000033-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000034-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000034-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000034-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000034-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000035-006.public_key b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000035-006.public_key
index 5980638c4..5980638c4 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000035-006.public_key
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000035-006.public_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000036-013.user_id b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000036-013.user_id
index 5d0d46e5d..5d0d46e5d 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000036-013.user_id
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000036-013.user_id
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000037-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000037-002.sig
index 833b563b2..833b563b2 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000037-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000037-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000038-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000038-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000038-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000038-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000039-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000039-002.sig
index 89c34fa5d..89c34fa5d 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000039-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000039-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000040-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000040-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000040-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000040-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000041-017.attribute b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000041-017.attribute
index a21a82fb1..a21a82fb1 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000041-017.attribute
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000041-017.attribute
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000042-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000042-002.sig
index fc6267fd0..fc6267fd0 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000042-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000042-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000043-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000043-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000043-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000043-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000044-014.public_subkey b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000044-014.public_subkey
index 06bf50e4f..06bf50e4f 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000044-014.public_subkey
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000044-014.public_subkey
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000045-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000045-002.sig
index 336eb0f24..336eb0f24 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000045-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000045-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000046-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000046-012.ring_trust
index ffa57e57a..ffa57e57a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000046-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000046-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000047-005.secret_key b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000047-005.secret_key
index 77b5d428a..77b5d428a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000047-005.secret_key
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000047-005.secret_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000048-013.user_id b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000048-013.user_id
index 759449bb4..759449bb4 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000048-013.user_id
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000048-013.user_id
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000049-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000049-002.sig
index 14276d0a5..14276d0a5 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000049-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000049-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000050-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000050-012.ring_trust
index b1eeabb95..b1eeabb95 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000050-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000050-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000051-007.secret_subkey b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000051-007.secret_subkey
index b4e65c92f..b4e65c92f 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000051-007.secret_subkey
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000051-007.secret_subkey
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000052-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000052-002.sig
index dd601807f..dd601807f 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000052-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000052-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000053-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000053-012.ring_trust
index b1eeabb95..b1eeabb95 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000053-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000053-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000054-005.secret_key b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000054-005.secret_key
index f153e5932..f153e5932 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000054-005.secret_key
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000054-005.secret_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000055-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000055-002.sig
index e734505a7..e734505a7 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000055-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000055-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000056-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000056-012.ring_trust
index b1eeabb95..b1eeabb95 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000056-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000056-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000057-013.user_id b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000057-013.user_id
index ab3f51d91..ab3f51d91 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000057-013.user_id
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000057-013.user_id
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000058-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000058-002.sig
index 8588489a7..8588489a7 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000058-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000058-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000059-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000059-012.ring_trust
index b1eeabb95..b1eeabb95 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000059-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000059-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000060-007.secret_subkey b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000060-007.secret_subkey
index 9df45f395..9df45f395 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000060-007.secret_subkey
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000060-007.secret_subkey
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000061-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000061-002.sig
index 639494223..639494223 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000061-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000061-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000062-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000062-012.ring_trust
index b1eeabb95..b1eeabb95 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000062-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000062-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000063-005.secret_key b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000063-005.secret_key
index 2f4268ee1..2f4268ee1 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000063-005.secret_key
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000063-005.secret_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000064-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000064-002.sig
index 5194b7840..5194b7840 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000064-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000064-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000065-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000065-012.ring_trust
index b1eeabb95..b1eeabb95 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000065-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000065-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000066-013.user_id b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000066-013.user_id
index fb3f49e0d..fb3f49e0d 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000066-013.user_id
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000066-013.user_id
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000067-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000067-002.sig
index d354e79df..d354e79df 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000067-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000067-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000068-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000068-012.ring_trust
index b1eeabb95..b1eeabb95 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000068-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000068-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000069-005.secret_key b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000069-005.secret_key
index 17a2c354d..17a2c354d 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000069-005.secret_key
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000069-005.secret_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000070-013.user_id b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000070-013.user_id
index 5d0d46e5d..5d0d46e5d 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000070-013.user_id
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000070-013.user_id
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000071-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000071-002.sig
index 833b563b2..833b563b2 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000071-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000071-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000072-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000072-012.ring_trust
index b1eeabb95..b1eeabb95 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000072-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000072-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000073-017.attribute b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000073-017.attribute
index a21a82fb1..a21a82fb1 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000073-017.attribute
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000073-017.attribute
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000074-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000074-002.sig
index fc6267fd0..fc6267fd0 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000074-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000074-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000075-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000075-012.ring_trust
index b1eeabb95..b1eeabb95 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000075-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000075-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000076-007.secret_subkey b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000076-007.secret_subkey
index b380339a4..b380339a4 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000076-007.secret_subkey
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000076-007.secret_subkey
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000077-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000077-002.sig
index 336eb0f24..336eb0f24 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000077-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000077-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000078-012.ring_trust b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000078-012.ring_trust
index b1eeabb95..b1eeabb95 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000078-012.ring_trust
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/000078-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/002182-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/002182-002.sig
index 2bc6679f4..2bc6679f4 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/002182-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/002182-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000005-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000005-002.sig
index 94055af66..94055af66 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000005-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000005-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000017-002.sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000017-002.sig
index b22f23b91..b22f23b91 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000017-002.sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000017-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-bzip2.gpg b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-bzip2.gpg
index 87539dbe8..87539dbe8 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-bzip2.gpg
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-bzip2.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-zlib.gpg b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-zlib.gpg
index 4da4dfa99..4da4dfa99 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-zlib.gpg
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-zlib.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig.gpg b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig.gpg
index dd617de13..dd617de13 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig.gpg
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/onepass_sig b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/onepass_sig
index 87b2895ea..87b2895ea 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/onepass_sig
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/onepass_sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/pubring.gpg b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/pubring.gpg
index a1519ee74..a1519ee74 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/pubring.gpg
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/pubring.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/secring.gpg b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/secring.gpg
index 13598756a..13598756a 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/secring.gpg
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/secring.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/symmetrically_encrypted b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/symmetrically_encrypted
index 129155aa2..129155aa2 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/symmetrically_encrypted
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/symmetrically_encrypted
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa-sha384.txt.gpg b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa-sha384.txt.gpg
index 39828fcae..39828fcae 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa-sha384.txt.gpg
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa-sha384.txt.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa.gpg b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa.gpg
index 97e7a267b..97e7a267b 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa.gpg
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-rsa.gpg b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-rsa.gpg
index 7ae453da6..7ae453da6 100644
--- a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-rsa.gpg
+++ b/OpenKeychain/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-rsa.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/public-key-canonicalize.blob b/OpenKeychain/src/test/resources/public-key-canonicalize.blob
index 3450824c1..3450824c1 100644
--- a/OpenKeychain-Test/src/test/resources/public-key-canonicalize.blob
+++ b/OpenKeychain/src/test/resources/public-key-canonicalize.blob
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/public-key-for-sample.blob b/OpenKeychain/src/test/resources/public-key-for-sample.blob
index 4aa91510b..4aa91510b 100644
--- a/OpenKeychain-Test/src/test/resources/public-key-for-sample.blob
+++ b/OpenKeychain/src/test/resources/public-key-for-sample.blob
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/sample-altered.txt b/OpenKeychain/src/test/resources/sample-altered.txt
index 458821f81..458821f81 100644
--- a/OpenKeychain-Test/src/test/resources/sample-altered.txt
+++ b/OpenKeychain/src/test/resources/sample-altered.txt
diff --git a/OpenKeychain-Test/src/test/resources/sample.txt b/OpenKeychain/src/test/resources/sample.txt
index c0065f78d..c0065f78d 100644
--- a/OpenKeychain-Test/src/test/resources/sample.txt
+++ b/OpenKeychain/src/test/resources/sample.txt
diff --git a/OpenKeychain-Test/src/test/resources/test-keys/bad_user_id_encoding.asc b/OpenKeychain/src/test/resources/test-keys/bad_user_id_encoding.asc
index 332747f84..332747f84 100644
--- a/OpenKeychain-Test/src/test/resources/test-keys/bad_user_id_encoding.asc
+++ b/OpenKeychain/src/test/resources/test-keys/bad_user_id_encoding.asc
diff --git a/OpenKeychain-Test/src/test/resources/test-keys/broken_cert_version.asc b/OpenKeychain/src/test/resources/test-keys/broken_cert_version.asc
index e2d2abd8e..e2d2abd8e 100644
--- a/OpenKeychain-Test/src/test/resources/test-keys/broken_cert_version.asc
+++ b/OpenKeychain/src/test/resources/test-keys/broken_cert_version.asc
diff --git a/OpenKeychain-Test/src/test/resources/test-keys/cooperpair/9E669861368BCA0BE42DAF7DDDA252EBB8EBE1AF.asc b/OpenKeychain/src/test/resources/test-keys/cooperpair/9E669861368BCA0BE42DAF7DDDA252EBB8EBE1AF.asc
index 4f51252da..4f51252da 100644
--- a/OpenKeychain-Test/src/test/resources/test-keys/cooperpair/9E669861368BCA0BE42DAF7DDDA252EBB8EBE1AF.asc
+++ b/OpenKeychain/src/test/resources/test-keys/cooperpair/9E669861368BCA0BE42DAF7DDDA252EBB8EBE1AF.asc
diff --git a/OpenKeychain-Test/src/test/resources/test-keys/cooperpair/A55120427374F3F7AA5F1166DDA252EBB8EBE1AF.asc b/OpenKeychain/src/test/resources/test-keys/cooperpair/A55120427374F3F7AA5F1166DDA252EBB8EBE1AF.asc
index 549bc51a2..549bc51a2 100644
--- a/OpenKeychain-Test/src/test/resources/test-keys/cooperpair/A55120427374F3F7AA5F1166DDA252EBB8EBE1AF.asc
+++ b/OpenKeychain/src/test/resources/test-keys/cooperpair/A55120427374F3F7AA5F1166DDA252EBB8EBE1AF.asc
diff --git a/OpenKeychain-Test/src/test/resources/test-keys/cooperpair/readme b/OpenKeychain/src/test/resources/test-keys/cooperpair/readme
index fecb372d9..fecb372d9 100644
--- a/OpenKeychain-Test/src/test/resources/test-keys/cooperpair/readme
+++ b/OpenKeychain/src/test/resources/test-keys/cooperpair/readme
diff --git a/OpenKeychain-Test/src/test/resources/test-keys/divert_to_card_sec.asc b/OpenKeychain/src/test/resources/test-keys/divert_to_card_sec.asc
index 3c3bbebe4..3c3bbebe4 100644
--- a/OpenKeychain-Test/src/test/resources/test-keys/divert_to_card_sec.asc
+++ b/OpenKeychain/src/test/resources/test-keys/divert_to_card_sec.asc
diff --git a/OpenKeychain-Test/src/test/resources/test-keys/mailvelope_07_no_key_flags.asc b/OpenKeychain/src/test/resources/test-keys/mailvelope_07_no_key_flags.asc
index 30e6b85e1..30e6b85e1 100644
--- a/OpenKeychain-Test/src/test/resources/test-keys/mailvelope_07_no_key_flags.asc
+++ b/OpenKeychain/src/test/resources/test-keys/mailvelope_07_no_key_flags.asc
diff --git a/OpenKeychain-Test/src/test/resources/test-keys/stripped_flags.asc b/OpenKeychain/src/test/resources/test-keys/stripped_flags.asc
index 0f4728297..0f4728297 100644
--- a/OpenKeychain-Test/src/test/resources/test-keys/stripped_flags.asc
+++ b/OpenKeychain/src/test/resources/test-keys/stripped_flags.asc
diff --git a/OpenKeychain-Test/src/test/resources/test-keys/symantec_public.asc b/OpenKeychain/src/test/resources/test-keys/symantec_public.asc
index f4148935c..f4148935c 100644
--- a/OpenKeychain-Test/src/test/resources/test-keys/symantec_public.asc
+++ b/OpenKeychain/src/test/resources/test-keys/symantec_public.asc
diff --git a/OpenKeychain-Test/src/test/resources/test-keys/symantec_secret.asc b/OpenKeychain/src/test/resources/test-keys/symantec_secret.asc
index 94451c1bf..94451c1bf 100644
--- a/OpenKeychain-Test/src/test/resources/test-keys/symantec_secret.asc
+++ b/OpenKeychain/src/test/resources/test-keys/symantec_secret.asc
diff --git a/README.md b/README.md
index a9f287044..d4bb8dfe3 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,15 @@
-# Branch Description
-
-This is Valodim's topic branch for linked identities in OpenKeychain.
-Everything here is very much work-in-progress and subject to changes.
-Specification will follow.
-
# OpenKeychain (for Android)
OpenKeychain is an OpenPGP implementation for Android.
For a more detailed description and installation instructions go to http://www.openkeychain.org .
-### Travis CI Build Status of development branch
+### Branches
+* The development of OpenKeychain happens in the "master" branch.
+* For every release a new branch, e.g., "3.2-fixes" is created to backport fixes from "master"
+
+### Travis CI Build Status of master branch
-[![Build Status](https://travis-ci.org/open-keychain/open-keychain.png?branch=development)](https://travis-ci.org/open-keychain/open-keychain)
+[![Build Status](https://travis-ci.org/open-keychain/open-keychain.svg?branch=master)](https://travis-ci.org/open-keychain/open-keychain)
## How to help the project?
@@ -53,7 +51,12 @@ Select everything for the newest SDK Platform, API 22, and also API 21
### Run Tests
1. Use OpenJDK instead of Oracle JDK
-2. Execute ``./gradlew test``
+2. Execute ``./gradlew clean testDebug --continue``
+
+### Run Jacoco Test Coverage
+1. Use OpenJDK instead of Oracle JDK
+2. Execute ``./gradlew clean testDebug jacocoTestReport``
+3. Report is here: OpenKeychain/build/reports/jacoco/jacocoTestReport/html/index.html
### Development with Android Studio
@@ -148,12 +151,13 @@ see http://help.transifex.net/features/client/index.html#user-client
## Coding Style
### Code
-* Indentation: 4 spaces, no tabs
-* Maximum line width for code and comments: 100
-* Opening braces don't go on their own line
+* Indentation: 4 spaces, no tabs.
+* Maximum line width for code and comments: 100.
+* Opening braces don't go on their own line.
* Field names: Non-public, non-static fields start with m.
* Acronyms are words: Treat acronyms as words in names, yielding !XmlHttpRequest, getUrl(), etc.
* Fully Qualify Imports: Do *not* use wildcard-imports such as ``import foo.*;``
+* Android Studio warnings should be fixed, or suppressed if they are incorrect.
The full coding style can be found at http://source.android.com/source/code-style.html
@@ -176,7 +180,7 @@ The full coding style can be found at http://source.android.com/source/code-styl
## Licenses
OpenKechain is licensed under GPLv3+.
-The full license text can be found in the [LICENSE file](https://github.com/open-keychain/open-keychain/blob/master/LICENSE).
+The full license text can be found in the [LICENSE file](https://github.com/open-keychain/open-keychain/blob/HEAD/LICENSE).
Some parts and some libraries are Apache License v2, MIT X11 License (see below).
> This program is free software: you can redistribute it and/or modify
@@ -195,23 +199,14 @@ Some parts and some libraries are Apache License v2, MIT X11 License (see below)
### Libraries
-See https://github.com/open-keychain/open-keychain/blob/development/OpenKeychain/src/main/res/raw/help_about.md
+See [In-app about screen](https://github.com/open-keychain/open-keychain/blob/HEAD/OpenKeychain/src/main/res/raw/help_about.md)
### Images
-* icon.svg
- modified version of kgpg_key2_kopete.svgz
-
* Actionbar icons
http://developer.android.com/design/downloads/index.html#action-bar-icon-pack
-
+
* QR Code Actionbar icon
https://github.com/openintents/openintents/blob/master/extensions/qrcode_ext/icons/ic_menu_qr_code/ic_menu_qr_code_holo_light/ic_menu_qr_code.svg
* Key status icons by the ModernPGP working group
https://github.com/ModernPGP
-
-* Purple color scheme
- http://android-holo-colors.com/
-
-* DNS icon
- http://www.fatcow.com/free-icons
diff --git a/build.gradle b/build.gradle
index 4fc97ff6d..5f7f9b082 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,26 +5,24 @@ buildscript {
dependencies {
// NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information
- classpath 'com.android.tools.build:gradle:1.1.3'
+ classpath 'com.android.tools.build:gradle:1.3.0'
classpath files('gradle-witness.jar')
// bintray dependency to satisfy dependency of openpgp-api lib
classpath 'com.novoda:bintray-release:0.2.7'
+
+ classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.0.1'
+ // classpath 'com.stanfy.spoon:spoon-gradle-plugin:1.0.2'
}
}
allprojects {
repositories {
jcenter()
-
- maven {
- // for zxing-android-embedded lib
- url "https://dl.bintray.com/journeyapps/maven"
- }
}
}
task wrapper(type: Wrapper) {
- gradleVersion = '2.3'
+ gradleVersion = '2.5'
}
subprojects {
@@ -33,7 +31,7 @@ subprojects {
}
}
-// Ignore tests for external dependency
+// Ignore tests for external spongycastle
project(':extern:spongycastle') {
subprojects {
// Need to re-apply the plugin here otherwise the test property below can't be set.
@@ -47,4 +45,4 @@ project(':extern:spongycastle') {
ext {
compileSdkVersion = 22
buildToolsVersion = '22.0.1'
-} \ No newline at end of file
+}
diff --git a/extern/KeybaseLib b/extern/KeybaseLib
-Subproject 9615d90b18d1aee4dad994aa45875adfdcfb3c3
+Subproject 0b0a60533c5f76b60e43895f3a0342bb0be6853
diff --git a/extern/openkeychain-api-lib b/extern/openkeychain-api-lib
-Subproject 2a6cc7999f35c6e85e80ab6f98965d0e64974a9
+Subproject 9b5b0b9ca32e80f3dadebe57d27e1422889d825
diff --git a/extern/openpgp-api-lib b/extern/openpgp-api-lib
-Subproject 5a24bb3428e89d394861d71bd50b46d941d6c87
+Subproject a5240bebf4ca0bab13af25ccd8ae30ae413953c
diff --git a/extern/safeslinger-exchange b/extern/safeslinger-exchange
-Subproject 7c84cb54df5b3d5c6780984e48f3e9e4cbc5128
+Subproject ca3300ac9bac73df2124301f5024db309a03c29
diff --git a/extern/spongycastle b/extern/spongycastle
-Subproject 2c744ebade816d2e4f65f3734db373e4c19c2e4
+Subproject b767c5afff37e7fbd26e79387d11cd5bded6eff
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 6e00d9230..4828f29e8 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,7 @@
-#Mon Mar 23 19:39:48 CET 2015
+#Mon Aug 03 18:56:28 CEST 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-bin.zip
-distributionSha256Sum=010dd9f31849abc3d5644e282943b1c1c355f8e2635c5789833979ce590a3774 \ No newline at end of file
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip
+distributionSha256Sum=b71ab21fa5e91dcc6a4bd723b13403e8610a6e1b4b9d4b314ff477820de00bf9 \ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 1b9038344..863fd5541 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -7,5 +7,4 @@ include ':extern:spongycastle:pkix'
include ':extern:spongycastle:prov'
include ':extern:minidns'
include ':extern:KeybaseLib:Lib'
-include ':extern:safeslinger-exchange'
-include ':OpenKeychain-Test'
+include ':extern:safeslinger-exchange:safeslinger-exchange'
diff --git a/tools/android-wait-for-emulator b/tools/android-wait-for-emulator
new file mode 100755
index 000000000..57d045900
--- /dev/null
+++ b/tools/android-wait-for-emulator
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Originally written by Ralf Kistner <ralf@embarkmobile.com>, but placed in the public domain
+
+set +e
+
+bootanim=""
+failcounter=0
+timeout_in_sec=720
+
+until [[ "$bootanim" =~ "stopped" ]]; do
+ bootanim=`adb -e shell getprop init.svc.bootanim 2>&1 &`
+ if [[ "$bootanim" =~ "device not found" || "$bootanim" =~ "device offline"
+ || "$bootanim" =~ "running" ]]; then
+ let "failcounter += 1"
+ echo "Waiting for emulator to start"
+ if [[ $failcounter -gt timeout_in_sec ]]; then
+ echo "Timeout ($timeout_in_sec seconds) reached; failed to start emulator"
+ exit 1
+ fi
+ fi
+ sleep 10
+done
+
+echo "Emulator is ready"
diff --git a/tools/checkstyle b/tools/checkstyle
index 27aabced2..0f6d142c8 100755
--- a/tools/checkstyle
+++ b/tools/checkstyle
@@ -1 +1 @@
-checkstyle -c tools/checkstyle.xml -r OpenPGP-Keychain/src/main/java 2>&1 | egrep -v 'log4j'
+checkstyle -c tools/checkstyle.xml -r OpenKeychain/src/main/java 2>&1 | egrep -v 'log4j'
diff --git a/tools/reset_yubikey.txt b/tools/reset_yubikey.txt
new file mode 100644
index 000000000..a98d4d139
--- /dev/null
+++ b/tools/reset_yubikey.txt
@@ -0,0 +1,13 @@
+/hex
+scd serialno
+scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
+scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
+scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
+scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
+scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
+scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
+scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
+scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
+scd apdu 00 e6 00 00
+scd apdu 00 44 00 00
+/echo Card has been successfully reset.