Merge branch 'import-refactor'
This commit is contained in:
+67
-55
@@ -8,40 +8,40 @@ dependencies {
|
|||||||
// NOTE: libraries are pinned to a specific build, see below
|
// NOTE: libraries are pinned to a specific build, see below
|
||||||
|
|
||||||
// from local Android SDK
|
// from local Android SDK
|
||||||
compile 'com.android.support:support-v4:24.1.1'
|
compile 'com.android.support:support-v4:25.0.0'
|
||||||
compile 'com.android.support:appcompat-v7:24.1.1'
|
compile 'com.android.support:appcompat-v7:25.0.0'
|
||||||
compile 'com.android.support:design:24.1.1'
|
compile 'com.android.support:design:25.0.0'
|
||||||
compile 'com.android.support:recyclerview-v7:24.1.1'
|
compile 'com.android.support:recyclerview-v7:25.0.0'
|
||||||
compile 'com.android.support:cardview-v7:24.1.1'
|
compile 'com.android.support:cardview-v7:25.0.0'
|
||||||
compile 'com.android.support:support-annotations:24.1.1'
|
compile 'com.android.support:support-annotations:25.0.0'
|
||||||
|
|
||||||
// JCenter etc.
|
// JCenter etc.
|
||||||
compile 'com.journeyapps:zxing-android-embedded:3.2.0@aar'
|
compile 'com.journeyapps:zxing-android-embedded:3.4.0@aar'
|
||||||
compile 'com.google.zxing:core:3.2.1'
|
compile 'com.google.zxing:core:3.3.0'
|
||||||
compile 'com.jpardogo.materialtabstrip:library:1.1.0'
|
compile 'com.jpardogo.materialtabstrip:library:1.1.1'
|
||||||
compile 'com.getbase:floatingactionbutton:1.10.1'
|
compile 'com.getbase:floatingactionbutton:1.10.1'
|
||||||
compile 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.0'
|
compile 'org.commonjava.googlecode.markdown4j:markdown4j:2.2-cj-1.0'
|
||||||
compile 'org.ocpsoft.prettytime:prettytime:4.0.1.Final'
|
compile 'org.ocpsoft.prettytime:prettytime:4.0.1.Final'
|
||||||
compile 'com.splitwise:tokenautocomplete:2.0.7@aar'
|
compile 'com.splitwise:tokenautocomplete:2.0.8@aar'
|
||||||
compile 'se.emilsjolander:stickylistheaders:2.7.0'
|
compile 'se.emilsjolander:stickylistheaders:2.7.0'
|
||||||
compile 'org.sufficientlysecure:html-textview:1.8'
|
compile 'org.sufficientlysecure:html-textview:2.0'
|
||||||
compile 'org.sufficientlysecure:donations:2.4'
|
compile 'org.sufficientlysecure:donations:2.4'
|
||||||
compile 'com.nispok:snackbar:2.11.0'
|
compile 'com.nispok:snackbar:2.11.0'
|
||||||
compile 'com.squareup.okhttp3:okhttp:3.2.0'
|
compile 'com.squareup.okhttp3:okhttp:3.2.0'
|
||||||
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.2.0'
|
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.2.0'
|
||||||
compile 'org.apache.james:apache-mime4j-core:0.7.2'
|
compile 'org.apache.james:apache-mime4j-core:0.8.0'
|
||||||
compile 'org.apache.james:apache-mime4j-dom:0.7.2'
|
compile 'org.apache.james:apache-mime4j-dom:0.8.0'
|
||||||
compile 'org.thoughtcrime.ssl.pinning:AndroidPinning:1.0.0'
|
compile 'org.thoughtcrime.ssl.pinning:AndroidPinning:1.0.0'
|
||||||
compile 'com.cocosw:bottomsheet:1.3.0@aar'
|
compile 'com.cocosw:bottomsheet:1.3.0@aar'
|
||||||
|
|
||||||
// Material Drawer
|
// Material Drawer
|
||||||
compile 'com.mikepenz:materialdrawer:5.2.2@aar'
|
compile 'com.mikepenz:materialdrawer:5.6.0@aar'
|
||||||
compile 'com.mikepenz:fastadapter:1.4.4'
|
compile 'com.mikepenz:fastadapter:1.8.2'
|
||||||
compile 'com.mikepenz:materialize:0.8.8'
|
compile 'com.mikepenz:materialize:1.0.0'
|
||||||
compile 'com.mikepenz:iconics-core:2.5.11@aar'
|
compile 'com.mikepenz:iconics-core:2.8.1@aar'
|
||||||
compile 'com.mikepenz:google-material-typeface:2.2.0.1.original@aar'
|
compile 'com.mikepenz:google-material-typeface:2.2.0.3.original@aar'
|
||||||
compile 'com.mikepenz:fontawesome-typeface:4.6.0.1@aar'
|
compile 'com.mikepenz:fontawesome-typeface:4.6.0.3@aar'
|
||||||
compile 'com.mikepenz:community-material-typeface:1.5.54.1@aar'
|
compile 'com.mikepenz:community-material-typeface:1.5.54.2@aar'
|
||||||
|
|
||||||
// Nordpol
|
// Nordpol
|
||||||
compile 'com.fidesmo:nordpol-android:0.1.18'
|
compile 'com.fidesmo:nordpol-android:0.1.18'
|
||||||
@@ -69,9 +69,9 @@ dependencies {
|
|||||||
// UI testing with Espresso
|
// UI testing with Espresso
|
||||||
// Force usage of support libs in the test app, since they are internally used by the runner module.
|
// Force usage of support libs in the test app, since they are internally used by the runner module.
|
||||||
// https://github.com/googlesamples/android-testing/blob/master/ui/espresso/BasicSample/app/build.gradle#L28
|
// https://github.com/googlesamples/android-testing/blob/master/ui/espresso/BasicSample/app/build.gradle#L28
|
||||||
androidTestCompile 'com.android.support:support-annotations:24.1.1'
|
androidTestCompile 'com.android.support:support-annotations:25.0.0'
|
||||||
androidTestCompile 'com.android.support:appcompat-v7:24.1.1'
|
androidTestCompile 'com.android.support:appcompat-v7:25.0.0'
|
||||||
androidTestCompile 'com.android.support:design:24.1.1'
|
androidTestCompile 'com.android.support:design:25.0.0'
|
||||||
androidTestCompile 'com.android.support.test:runner:0.5'
|
androidTestCompile 'com.android.support.test:runner:0.5'
|
||||||
androidTestCompile 'com.android.support.test:rules:0.5'
|
androidTestCompile 'com.android.support.test:rules:0.5'
|
||||||
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
|
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
|
||||||
@@ -88,47 +88,56 @@ dependencies {
|
|||||||
// Comment out the libs referenced as git submodules!
|
// Comment out the libs referenced as git submodules!
|
||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
verify = [
|
verify = [
|
||||||
'com.android.support:support-v4:246c99385a84fe179d7b833c9ddaf2576f217b0abba5e74b5353cc78756f5880',
|
'com.android.support:support-v4:c25c657954152c4315584572f5008a6780b086169519f67acd20c2617b9da325',
|
||||||
'com.android.support:appcompat-v7:bf8db89d678286043778990fc967346e94321cc8d8bad99e9b0db20588509156',
|
'com.android.support:appcompat-v7:bc0b13b1ba63ed226ae509044c880fdfe225cb86d21924ef19f2fb97a71c877e',
|
||||||
'com.android.support:design:391d3d1fd274c3150e949653b9863af61e80953bbb90f3cbd40e9d2f1bd548d9',
|
'com.android.support:design:2fc13bacb1d293e3f35d9de509da6be6f5f0f50cf8c3796296fabbd63d76e9ab',
|
||||||
'com.android.support:recyclerview-v7:e89e0ba2b73bb4002965ff42f93cfe3e7da05162716b15218e31f7dd140fa40e',
|
'com.android.support:recyclerview-v7:fc6d6a9b802ed3a26789812f29167135cac7b4d956f4deda54fc1f317721da64',
|
||||||
'com.android.support:cardview-v7:eb74ab41045d182fcf783c7479c5b35fb1f4d771ed307b2e7b84ab3a8fe10e96',
|
'com.android.support:cardview-v7:b9b214512995fd98490466bd98af08404866369e9526b450c0e2cf7b1e607bb5',
|
||||||
'com.android.support:support-annotations:bac4e534657165b0c4c362c97db389dcb152e43273435d2ccaa939a82e03f42c',
|
'com.android.support:support-annotations:18766c4a12f18a0d96a383bc8c84d7a9c31ba2d1721622e0af814f8413ff06ec',
|
||||||
'com.journeyapps:zxing-android-embedded:afe4cd51d95ba0fd3a4bfe08c5a160bd32602aa174d511600ac824b6de4c79f1',
|
'com.journeyapps:zxing-android-embedded:2422d83c2c09a7b645f516c8458ececba6a7da47b94e40778d876facf495c660',
|
||||||
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259',
|
'com.google.zxing:core:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d',
|
||||||
'com.jpardogo.materialtabstrip:library:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
|
'com.jpardogo.materialtabstrip:library:4ee2f1211c302b45fb8c627cc5b240dc6b38b7aaaab1b8bffc81663e1b108013',
|
||||||
'com.getbase:floatingactionbutton:3edefa511aac4d90794c7b0496aca59cff2eee1e32679247b4f85acbeee05240',
|
'com.getbase:floatingactionbutton:3edefa511aac4d90794c7b0496aca59cff2eee1e32679247b4f85acbeee05240',
|
||||||
'org.commonjava.googlecode.markdown4j:markdown4j:e952e825d29e1317d96f79f346bfb6786c7c5eef50bd26e54a80823704b62e13',
|
'org.commonjava.googlecode.markdown4j:markdown4j:e952e825d29e1317d96f79f346bfb6786c7c5eef50bd26e54a80823704b62e13',
|
||||||
'org.ocpsoft.prettytime:prettytime:ef7098d973ae78b57d1a22dc37d3b8a771bf030301300e24055d676b6cdc5e75',
|
'org.ocpsoft.prettytime:prettytime:ef7098d973ae78b57d1a22dc37d3b8a771bf030301300e24055d676b6cdc5e75',
|
||||||
'com.splitwise:tokenautocomplete:f56239588390f103b270b7c12361d99b06313a5a0410dc7f66e241ac4baf9baa',
|
'com.splitwise:tokenautocomplete:f921f83ee26b5265f719b312c30452ef8e219557826c5ce5bf02e29647967939',
|
||||||
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
|
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
|
||||||
'org.sufficientlysecure:html-textview:206f484fe4178be6c831fe680de558764967e7b56496c4cc7f37f2979a477df6',
|
'org.sufficientlysecure:html-textview:302c449167f9573313e5293ccab689010e028e4d09aee2ccc2682b3211227ce7',
|
||||||
'org.sufficientlysecure:donations:96f8197bab26dfe41900d824f10f8f1914519cd62eedb77bdac5b223eccdf0a6',
|
'org.sufficientlysecure:donations:96f8197bab26dfe41900d824f10f8f1914519cd62eedb77bdac5b223eccdf0a6',
|
||||||
'com.nispok:snackbar:46b5eb9d630d329e13c2ce00ee9fb115ffb66c23c72cff32ee97eedd76824c6f',
|
'com.nispok:snackbar:46b5eb9d630d329e13c2ce00ee9fb115ffb66c23c72cff32ee97eedd76824c6f',
|
||||||
'com.squareup.okhttp3:okhttp:a41cdb7b024c56436a21e38f00b4d12e3b7e01451ffe6c4f545acba805bba03b',
|
'com.squareup.okhttp3:okhttp:a41cdb7b024c56436a21e38f00b4d12e3b7e01451ffe6c4f545acba805bba03b',
|
||||||
'com.squareup.okhttp3:okhttp-urlconnection:7d6598a6665c166e2d4b78956a96056b9be7de192b3c923ccf4695d08e580833',
|
'com.squareup.okhttp3:okhttp-urlconnection:7d6598a6665c166e2d4b78956a96056b9be7de192b3c923ccf4695d08e580833',
|
||||||
'org.apache.james:apache-mime4j-core:4d7434c68f94b81a253c12f28e6bbb4d6239c361d6086a46e22e594bb43ac660',
|
'org.apache.james:apache-mime4j-core:561987f604911e1870b2b4eabf0b0658d666c66cb1e65fba3e9e4bffe63acab9',
|
||||||
'org.apache.james:apache-mime4j-dom:7e6b06ee164a1c21b7e477249ea0b74a18fddce44764e5764085f58dd8c34633',
|
'org.apache.james:apache-mime4j-dom:e18717fe6d36f32e5c5f7cbeea1a9bf04645fdabc84e7e8374d9da10fd52e78d',
|
||||||
'org.thoughtcrime.ssl.pinning:AndroidPinning:afa1d74e699257fa75cb109ff29bac50726ef269c6e306bdeffe8223cee06ef4',
|
'org.thoughtcrime.ssl.pinning:AndroidPinning:afa1d74e699257fa75cb109ff29bac50726ef269c6e306bdeffe8223cee06ef4',
|
||||||
'com.cocosw:bottomsheet:4af6112a7f4cad4e2b70e5fdf1edc39f51275523a0f53011a012837dc103e597',
|
'com.cocosw:bottomsheet:4af6112a7f4cad4e2b70e5fdf1edc39f51275523a0f53011a012837dc103e597',
|
||||||
'com.mikepenz:materialdrawer:4169462fdde042e2bb53a7c2b4e2334d569d16b2020781ee05741b50e1a2967d',
|
'com.mikepenz:materialdrawer:8bba1428dcef5ad7c2decf49c612ad980b38e2f1031cbd66c152a8a104793929',
|
||||||
'com.mikepenz:fastadapter:1bfc00216d71dfdfe0d8e7a9d92bb97bfaa1794543930e34b1f79d5d7adbddf6',
|
'com.mikepenz:fastadapter:21d4ecb5c128bcda37b14e7998d799ed52cfc768b72cdf3d5578bb6775769ebd',
|
||||||
'com.mikepenz:materialize:575195b2fa5b2414fb14a59470ee21d8a8cd8355b651e0cf52e477e3ff1cd96c',
|
'com.mikepenz:materialize:942ccf5e2aa1a46803aa884e8dc7bbaf2a9e8e9996a0cf92e3fe2f44a8592ba4',
|
||||||
'com.mikepenz:iconics-core:d57c6b0ecb33d9ed9708da62e07ad8993d80a16f374f7a2018be7837a60b7ed7',
|
'com.mikepenz:iconics-core:478d7e245098f7c28b5b20a0e6b1e5cb108ef3eaf595af7190bc60f91063aa3d',
|
||||||
'com.mikepenz:google-material-typeface:47eabb0aadcc0f56530c3ca462b671a5cf5251101cef8f581aedf90ca511914a',
|
'com.mikepenz:google-material-typeface:f27c629ba5d2a90ecfbd7f221ff98cd363e1ee6be06b099b82bae490766e14a5',
|
||||||
'com.mikepenz:fontawesome-typeface:033cf3460d8074bd37a1fefc2ff4eac8f2e3db835ec78bf386d46710e4d0827c',
|
'com.mikepenz:fontawesome-typeface:ee47b7fe97b90412f01f2fcdd78f65a4edb0ab00006f5ef59ed00516baca9309',
|
||||||
'com.mikepenz:community-material-typeface:382e8446fc08fe03cb1e0f91ee329ffd514c113ad22f8389b88424ac71ed5fbb',
|
'com.mikepenz:community-material-typeface:d6035d261c5eba880cd7fe5dcb8cc00b09bfe6d41063b881b759e9897dc7b7c9',
|
||||||
'com.fidesmo:nordpol-android:56f43fe2b1676817bcb4085926de14a08282ef6729c855c198d81aec62b20d65',
|
'com.fidesmo:nordpol-android:56f43fe2b1676817bcb4085926de14a08282ef6729c855c198d81aec62b20d65',
|
||||||
// 'OpenKeychain:openpgp-api-lib:2c145be0d124d37558f65ed962c47358b7e424dd4c00dc8818aad64598664c3d',
|
// 'OpenKeychain:openpgp-api-lib:43d7e67e8ae19ae46e228393a7c4f5861da64db47da2fbb398817bde774669f7',
|
||||||
// 'OpenKeychain:openkeychain-api-lib:afff9f8410d8781fcd4023ca247d876ca124a6ee4f718afbc3740fc6078a255d',
|
// 'OpenKeychain:openkeychain-api-lib:d2e66ce4828c47cb6081cfb8e7352681cc5d337404aec87e156cbc5a6e732587',
|
||||||
// 'OpenKeychain.extern.bouncycastle:core:18876ec7629c427002ee00bb361b016b86159dcd9aac68951aec92f9ca41bb7a',
|
// 'OpenKeychain.extern.bouncycastle:core:20bd3036f93758c5ed937fc172b5e7e8d82d071e7b435bd9b6fa3869d5788095',
|
||||||
// 'OpenKeychain.extern.bouncycastle:pg:b6469f69d17ecb43c86c082fb1b5c01ae2f8e7a8ca5fa29e9f199c74dc021be1',
|
// 'OpenKeychain.extern.bouncycastle:pg:97b7318ee208b8fe552425aac9ffdcc8723032a78784ecc82c999ee09161e2e8',
|
||||||
// 'OpenKeychain.extern.bouncycastle:prov:db7c1a72a86d918a25fc1ca8553762d1280274e8e2f02299f344b244bb527708',
|
// 'OpenKeychain.extern.bouncycastle:prov:76a8d07e88a557f91fad5c01f1f827085bde12ee924fa4d186a65d80e39d42a2',
|
||||||
// 'OpenKeychain.extern:minidns:8b324812d4b9ea9d639be43183f4292e5f257319fbb6c23faf2ee420f041d279',
|
// 'OpenKeychain.extern:minidns:8183e688b754b9e64ea498b735890a3ece9af1e240d949b804bf7b12a0d5b830',
|
||||||
// 'OpenKeychain:KeybaseLib:a992fb93c718fa34ab45850e34a294e413c9410e2b00243a43e6f303d85b6147',
|
// 'OpenKeychain:KeybaseLib:a870229a22779dfe433d6e48970822b0dfcd351c3cc885cfb94816d064c10fb0',
|
||||||
// 'OpenKeychain:safeslinger-exchange:99d6ce7745edf5341495932bb26d0d1f8894b1edbfbddffc8514b2cf4149ad9b',
|
// 'OpenKeychain:safeslinger-exchange:2cbff71ec5d59c824dd244042539cfc5cfbf450ab813c95b9dc83fbb50401ba9',
|
||||||
'com.android.support:animated-vector-drawable:4fcd1fc36034a804200ef3e552b0f2f688a0a7a8a007de43201e40bfedda73b3',
|
// 'com.android.databinding:library:def2976cb30dd5abf9f3a35d70c70cfb5485af4fb4ae022f5b9a6e2f8cff6386',
|
||||||
'com.android.support:support-vector-drawable:45b1f180b437a750429f6c1457181c167ba211c17fcb992f83cdbefef5eb1519',
|
// 'com.android.databinding:baseLibrary:47cb0d2d4d1aae4af3f860c31540493332a26278c016bbae90d22fdde3b0b83d',
|
||||||
|
// 'com.android.databinding:adapters:0dd06349dad760f3cb56769f8e9f46451634be6a8a1bfdb2e88a5ca10afcebd6',
|
||||||
|
'com.android.support:support-compat:94648aef50a1938b541465d6d126769f20e77e247c7bca10b9b8cb61caa43398',
|
||||||
|
'com.android.support:support-media-compat:8665ae69c74b2879bdce2bdea6d3b7296126487d0ea6fa2f9dd9ffdd5a5cee86',
|
||||||
|
'com.android.support:support-core-utils:1808deca97864e78066d93d998f71bf71b0a774b6ea39c4e91fc1376d090728d',
|
||||||
|
'com.android.support:support-core-ui:cdd0e8ec4d82e30f4c8ff2897997d59330992b670023fd2793a89a226ac960d6',
|
||||||
|
'com.android.support:support-fragment:e47441b6a0e01b20cf744e3a8383ab60d922badcfde37c0b35fb078c84f2eae0',
|
||||||
|
'com.android.support:support-vector-drawable:0758104bc324c1638705cd3cddf773639679bab5af28e3a63093655113d75d20',
|
||||||
|
'com.android.support:animated-vector-drawable:9539f5dd8a919692cef064f72658034bf921a19ec14a4ff4abdfa3b478f3a3f9',
|
||||||
|
'com.android.support:transition:331d4152f878442b7bf92c4320335ec7f707d1f6248da2fb3667be436dd508d1',
|
||||||
'com.squareup.okio:okio:114bdc1f47338a68bcbc95abf2f5cdc72beeec91812f2fcd7b521c1937876266',
|
'com.squareup.okio:okio:114bdc1f47338a68bcbc95abf2f5cdc72beeec91812f2fcd7b521c1937876266',
|
||||||
'com.fidesmo:nordpol-core:3de58e850a00bba5b4d3a604d1399bcd89f695ea191ec0b03a57222e18062d15',
|
'com.fidesmo:nordpol-core:3de58e850a00bba5b4d3a604d1399bcd89f695ea191ec0b03a57222e18062d15',
|
||||||
]
|
]
|
||||||
@@ -142,7 +151,7 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 24
|
targetSdkVersion 25
|
||||||
versionCode 42000
|
versionCode 42000
|
||||||
versionName "4.2 beta1"
|
versionName "4.2 beta1"
|
||||||
applicationId "org.sufficientlysecure.keychain"
|
applicationId "org.sufficientlysecure.keychain"
|
||||||
@@ -274,7 +283,6 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dexOptions {
|
dexOptions {
|
||||||
incremental = true
|
|
||||||
preDexLibraries = true
|
preDexLibraries = true
|
||||||
// dexInProcess requires much RAM, which is not available on all dev systems
|
// dexInProcess requires much RAM, which is not available on all dev systems
|
||||||
dexInProcess = false
|
dexInProcess = false
|
||||||
@@ -291,6 +299,10 @@ android {
|
|||||||
exclude 'META-INF/NOTICE'
|
exclude 'META-INF/NOTICE'
|
||||||
exclude '.readme'
|
exclude '.readme'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dataBinding {
|
||||||
|
enabled = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task jacocoTestReport(type:JacocoReport, dependsOn: "testFdroidDebugWithTestCoverageUnitTest") {
|
task jacocoTestReport(type:JacocoReport, dependsOn: "testFdroidDebugWithTestCoverageUnitTest") {
|
||||||
|
|||||||
+41
-39
@@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.keyimport;
|
package org.sufficientlysecure.keychain.keyimport;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||||
@@ -26,8 +28,6 @@ import java.net.Proxy;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search two or more types of server for online keys.
|
* Search two or more types of server for online keys.
|
||||||
*/
|
*/
|
||||||
@@ -38,8 +38,8 @@ public class CloudSearch {
|
|||||||
public static ArrayList<ImportKeysListEntry> search(
|
public static ArrayList<ImportKeysListEntry> search(
|
||||||
@NonNull final String query, Preferences.CloudSearchPrefs cloudPrefs, @NonNull final ParcelableProxy proxy)
|
@NonNull final String query, Preferences.CloudSearchPrefs cloudPrefs, @NonNull final ParcelableProxy proxy)
|
||||||
throws Keyserver.CloudSearchFailureException {
|
throws Keyserver.CloudSearchFailureException {
|
||||||
final ArrayList<Keyserver> servers = new ArrayList<>();
|
|
||||||
|
|
||||||
|
final ArrayList<Keyserver> servers = new ArrayList<>();
|
||||||
// it's a Vector for sync, multiple threads might report problems
|
// it's a Vector for sync, multiple threads might report problems
|
||||||
final Vector<Keyserver.CloudSearchFailureException> problems = new Vector<>();
|
final Vector<Keyserver.CloudSearchFailureException> problems = new Vector<>();
|
||||||
|
|
||||||
@@ -52,46 +52,48 @@ public class CloudSearch {
|
|||||||
if (cloudPrefs.searchFacebook) {
|
if (cloudPrefs.searchFacebook) {
|
||||||
servers.add(new FacebookKeyserver());
|
servers.add(new FacebookKeyserver());
|
||||||
}
|
}
|
||||||
final ImportKeysList results = new ImportKeysList(servers.size());
|
|
||||||
|
|
||||||
ArrayList<Thread> searchThreads = new ArrayList<>();
|
int numberOfServers = servers.size();
|
||||||
for (final Keyserver keyserver : servers) {
|
final ImportKeysList results = new ImportKeysList(numberOfServers);
|
||||||
Runnable r = new Runnable() {
|
|
||||||
@Override
|
if (numberOfServers > 0) {
|
||||||
public void run() {
|
ArrayList<Thread> searchThreads = new ArrayList<>();
|
||||||
try {
|
for (final Keyserver keyserver : servers) {
|
||||||
results.addAll(keyserver.search(query, proxy));
|
Runnable r = new Runnable() {
|
||||||
} catch (Keyserver.CloudSearchFailureException e) {
|
@Override
|
||||||
problems.add(e);
|
public void run() {
|
||||||
|
try {
|
||||||
|
results.addAll(keyserver.search(query, proxy));
|
||||||
|
} catch (Keyserver.CloudSearchFailureException e) {
|
||||||
|
problems.add(e);
|
||||||
|
}
|
||||||
|
results.finishedAdding(); // notifies if all searchers done
|
||||||
}
|
}
|
||||||
results.finishedAdding(); // notifies if all searchers done
|
};
|
||||||
}
|
Thread searchThread = new Thread(r);
|
||||||
};
|
searchThreads.add(searchThread);
|
||||||
Thread searchThread = new Thread(r);
|
searchThread.start();
|
||||||
searchThreads.add(searchThread);
|
|
||||||
searchThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for either all the searches to come back, or 10 seconds. If using proxy, wait 30 seconds.
|
|
||||||
synchronized (results) {
|
|
||||||
try {
|
|
||||||
if (proxy.getProxy() == Proxy.NO_PROXY) {
|
|
||||||
results.wait(30 * SECONDS);
|
|
||||||
} else {
|
|
||||||
results.wait(10 * SECONDS);
|
|
||||||
}
|
|
||||||
for (Thread thread : searchThreads) {
|
|
||||||
// kill threads that haven't returned yet
|
|
||||||
thread.interrupt();
|
|
||||||
}
|
|
||||||
} catch (InterruptedException ignored) {
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (results.outstandingSuppliers() > 0) {
|
// wait for either all the searches to come back, or 10 seconds. If using proxy, wait 30 seconds.
|
||||||
String message = "Launched " + servers.size() + " cloud searchers, but " +
|
synchronized (results) {
|
||||||
results.outstandingSuppliers() + "failed to complete.";
|
try {
|
||||||
problems.add(new Keyserver.QueryFailedException(message));
|
results.wait((proxy.getProxy() == Proxy.NO_PROXY ? 30 : 10) * SECONDS);
|
||||||
|
for (Thread thread : searchThreads) {
|
||||||
|
// kill threads that haven't returned yet
|
||||||
|
thread.interrupt();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.outstandingSuppliers() > 0) {
|
||||||
|
String message = "Launched " + servers.size() + " cloud searchers, but " +
|
||||||
|
results.outstandingSuppliers() + "failed to complete.";
|
||||||
|
problems.add(new Keyserver.QueryFailedException(message));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
problems.add(new Keyserver.QueryNoEnabledSourceException());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!problems.isEmpty()) {
|
if (!problems.isEmpty()) {
|
||||||
|
|||||||
+21
-26
@@ -23,11 +23,6 @@ import android.net.Uri;
|
|||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
import okhttp3.OkHttpClient;
|
|
||||||
import okhttp3.Request;
|
|
||||||
import okhttp3.Response;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
||||||
@@ -40,13 +35,16 @@ import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
|||||||
import org.sufficientlysecure.keychain.util.TlsHelper;
|
import org.sufficientlysecure.keychain.util.TlsHelper;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
public class FacebookKeyserver extends Keyserver {
|
public class FacebookKeyserver extends Keyserver {
|
||||||
|
|
||||||
private static final String FB_KEY_URL_FORMAT
|
private static final String FB_KEY_URL_FORMAT
|
||||||
@@ -54,10 +52,6 @@ public class FacebookKeyserver extends Keyserver {
|
|||||||
private static final String FB_HOST = "facebook.com";
|
private static final String FB_HOST = "facebook.com";
|
||||||
private static final String FB_HOST_WWW = "www." + FB_HOST;
|
private static final String FB_HOST_WWW = "www." + FB_HOST;
|
||||||
|
|
||||||
public static final String FB_URL = "https://" + FB_HOST_WWW;
|
|
||||||
|
|
||||||
public static final String ORIGIN = FB_URL;
|
|
||||||
|
|
||||||
public FacebookKeyserver() {
|
public FacebookKeyserver() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +79,7 @@ public class FacebookKeyserver extends Keyserver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String get(String fbUsername, ParcelableProxy proxy) throws QueryFailedException {
|
public String get(String fbUsername, ParcelableProxy proxy) throws QueryFailedException {
|
||||||
Log.d(Constants.TAG, "FacebookKeyserver get: " + fbUsername + " using Proxy: " + proxy);
|
Log.d(Constants.TAG, "FacebookKeyserver get: " + fbUsername + " using Proxy: " + proxy.getProxy());
|
||||||
|
|
||||||
String data = query(fbUsername, proxy);
|
String data = query(fbUsername, proxy);
|
||||||
|
|
||||||
@@ -102,14 +96,21 @@ public class FacebookKeyserver extends Keyserver {
|
|||||||
|
|
||||||
private String query(String fbUsername, ParcelableProxy proxy) throws QueryFailedException {
|
private String query(String fbUsername, ParcelableProxy proxy) throws QueryFailedException {
|
||||||
try {
|
try {
|
||||||
String request = String.format(FB_KEY_URL_FORMAT, fbUsername);
|
URL url = new URL(String.format(FB_KEY_URL_FORMAT, fbUsername));
|
||||||
Log.d(Constants.TAG, "fetching from Facebook with: " + request + " proxy: " + proxy);
|
Log.d(Constants.TAG, "fetching from Facebook with: " + url + " proxy: " + proxy.getProxy());
|
||||||
|
|
||||||
URL url = new URL(request);
|
/*
|
||||||
|
* For some URLs such as https://www.facebook.com/adithya.abraham/publickey/download
|
||||||
|
* Facebook redirects to a mobile version (302) and then errors out (500).
|
||||||
|
* Using a desktop User-Agent solves that!
|
||||||
|
*/
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.header("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:49.0) Gecko/20100101 Firefox/49.0")
|
||||||
|
.build();
|
||||||
|
|
||||||
OkHttpClient client = OkHttpClientFactory.getClientPinnedIfAvailable(url, proxy.getProxy());
|
OkHttpClient client = OkHttpClientFactory.getClientPinnedIfAvailable(url, proxy.getProxy());
|
||||||
|
Response response = client.newCall(request).execute();
|
||||||
Response response = client.newCall(new Request.Builder().url(url).build()).execute();
|
|
||||||
|
|
||||||
// contains body both in case of success or failure
|
// contains body both in case of success or failure
|
||||||
String responseBody = response.body().string();
|
String responseBody = response.body().string();
|
||||||
@@ -118,7 +119,8 @@ public class FacebookKeyserver extends Keyserver {
|
|||||||
return responseBody;
|
return responseBody;
|
||||||
} else {
|
} else {
|
||||||
// probably a 404 indicating that the key does not exist
|
// probably a 404 indicating that the key does not exist
|
||||||
throw new QueryFailedException("key for " + fbUsername + " not found on Facebook");
|
throw new QueryFailedException("key for " + fbUsername + " not found on Facebook." +
|
||||||
|
"response:" + response);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -128,7 +130,7 @@ public class FacebookKeyserver extends Keyserver {
|
|||||||
+ (proxy.getProxy() == Proxy.NO_PROXY ? "" : " Using proxy " + proxy.getProxy()));
|
+ (proxy.getProxy() == Proxy.NO_PROXY ? "" : " Using proxy " + proxy.getProxy()));
|
||||||
} catch (TlsHelper.TlsHelperException e) {
|
} catch (TlsHelper.TlsHelperException e) {
|
||||||
Log.e(Constants.TAG, "Exception in cert pinning", e);
|
Log.e(Constants.TAG, "Exception in cert pinning", e);
|
||||||
throw new QueryFailedException("Exception in cert pinning. ");
|
throw new QueryFailedException("Exception in cert pinning.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +150,6 @@ public class FacebookKeyserver extends Keyserver {
|
|||||||
throws UnsupportedOperationException {
|
throws UnsupportedOperationException {
|
||||||
ImportKeysListEntry entry = new ImportKeysListEntry();
|
ImportKeysListEntry entry = new ImportKeysListEntry();
|
||||||
entry.setSecretKey(false); // keys imported from Facebook must be public
|
entry.setSecretKey(false); // keys imported from Facebook must be public
|
||||||
entry.addOrigin(ORIGIN);
|
|
||||||
|
|
||||||
// so we can query for the Facebook username directly, and to identify the source to
|
// so we can query for the Facebook username directly, and to identify the source to
|
||||||
// download the key from
|
// download the key from
|
||||||
@@ -156,17 +157,11 @@ public class FacebookKeyserver extends Keyserver {
|
|||||||
|
|
||||||
UncachedPublicKey key = ring.getPublicKey();
|
UncachedPublicKey key = ring.getPublicKey();
|
||||||
|
|
||||||
entry.setPrimaryUserId(key.getPrimaryUserIdWithFallback());
|
|
||||||
entry.setUserIds(key.getUnorderedUserIds());
|
entry.setUserIds(key.getUnorderedUserIds());
|
||||||
entry.updateMergedUserIds();
|
|
||||||
|
|
||||||
entry.setPrimaryUserId(key.getPrimaryUserIdWithFallback());
|
entry.setPrimaryUserId(key.getPrimaryUserIdWithFallback());
|
||||||
|
|
||||||
entry.setKeyId(key.getKeyId());
|
entry.setKeyId(key.getKeyId());
|
||||||
entry.setKeyIdHex(KeyFormattingUtils.convertKeyIdToHex(key.getKeyId()));
|
entry.setFingerprint(key.getFingerprint());
|
||||||
|
|
||||||
entry.setFingerprintHex(KeyFormattingUtils.convertFingerprintToHex(key.getFingerprint()));
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (key.isEC()) { // unsupported key format (ECDH or ECDSA)
|
if (key.isEC()) { // unsupported key format (ECDH or ECDSA)
|
||||||
|
|||||||
+14
-33
@@ -70,44 +70,25 @@ public class ImportKeysList extends ArrayList<ImportKeysListEntry> {
|
|||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// keep track if this key result is from a HKP keyserver
|
if (incoming.getKeyserver() != null) {
|
||||||
boolean incomingFromHkpServer = true;
|
existing.setKeyserver(incoming.getKeyserver());
|
||||||
// we’re going to want to try to fetch the key from everywhere we found it, so remember
|
// Mail addresses returned by HKP servers are preferred over keybase.io IDs
|
||||||
// all the origins
|
existing.setPrimaryUserId(incoming.getPrimaryUserId());
|
||||||
for (String origin : incoming.getOrigins()) {
|
|
||||||
existing.addOrigin(origin);
|
|
||||||
|
|
||||||
|
modified = true;
|
||||||
|
} else if (incoming.getKeybaseName() != null) {
|
||||||
// to work properly, Keybase-sourced/Facebook-sourced entries need to pass along the
|
// to work properly, Keybase-sourced/Facebook-sourced entries need to pass along the
|
||||||
// identifying name/id
|
// identifying name/id
|
||||||
if (incoming.getKeybaseName() != null) {
|
existing.setKeybaseName(incoming.getKeybaseName());
|
||||||
existing.setKeybaseName(incoming.getKeybaseName());
|
modified = true;
|
||||||
// one of the origins is not a HKP keyserver
|
} else if (incoming.getFbUsername() != null) {
|
||||||
incomingFromHkpServer = false;
|
existing.setFbUsername(incoming.getFbUsername());
|
||||||
}
|
modified = true;
|
||||||
if (incoming.getFbUsername() != null) {
|
|
||||||
existing.setFbUsername(incoming.getFbUsername());
|
|
||||||
// one of the origins is not a HKP keyserver
|
|
||||||
incomingFromHkpServer = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<String> incomingIDs = incoming.getUserIds();
|
if (existing.addUserIds(incoming.getUserIds()))
|
||||||
ArrayList<String> existingIDs = existing.getUserIds();
|
modified = true;
|
||||||
for (String incomingID : incomingIDs) {
|
|
||||||
if (!existingIDs.contains(incomingID)) {
|
|
||||||
// prepend HKP server results to the start of the list,
|
|
||||||
// so that the UI (for cloud key search, which is picking the first list item)
|
|
||||||
// shows the right main email address, as mail addresses returned by HKP servers
|
|
||||||
// are preferred over keybase.io IDs
|
|
||||||
if (incomingFromHkpServer) {
|
|
||||||
existingIDs.add(0, incomingID);
|
|
||||||
} else {
|
|
||||||
existingIDs.add(incomingID);
|
|
||||||
}
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
existing.updateMergedUserIds();
|
|
||||||
return modified;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+202
-137
@@ -21,108 +21,59 @@ import android.content.Context;
|
|||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
import org.openintents.openpgp.util.OpenPgpUtils;
|
import org.openintents.openpgp.util.OpenPgpUtils.UserId;
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
|
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class ImportKeysListEntry implements Serializable, Parcelable {
|
public class ImportKeysListEntry implements Serializable, Parcelable {
|
||||||
private static final long serialVersionUID = -7797972103284992662L;
|
private static final long serialVersionUID = -7797972103284992662L;
|
||||||
|
|
||||||
|
private ParcelableKeyRing mParcelableKeyRing;
|
||||||
|
|
||||||
private ArrayList<String> mUserIds;
|
private ArrayList<String> mUserIds;
|
||||||
private HashMap<String, HashSet<String>> mMergedUserIds;
|
private HashMap<String, HashSet<String>> mMergedUserIds;
|
||||||
private long mKeyId;
|
private ArrayList<Map.Entry<String, HashSet<String>>> mSortedUserIds;
|
||||||
|
|
||||||
private String mKeyIdHex;
|
private String mKeyIdHex;
|
||||||
|
|
||||||
|
private boolean mSecretKey;
|
||||||
private boolean mRevoked;
|
private boolean mRevoked;
|
||||||
private boolean mExpired;
|
private boolean mExpired;
|
||||||
private Date mDate; // TODO: not displayed
|
private boolean mUpdated;
|
||||||
|
|
||||||
|
private Date mDate;
|
||||||
private String mFingerprintHex;
|
private String mFingerprintHex;
|
||||||
private Integer mBitStrength;
|
private Integer mBitStrength;
|
||||||
private String mCurveOid;
|
private String mCurveOid;
|
||||||
private String mAlgorithm;
|
private String mAlgorithm;
|
||||||
private boolean mSecretKey;
|
|
||||||
private String mPrimaryUserId;
|
private UserId mPrimaryUserId;
|
||||||
|
private ParcelableHkpKeyserver mKeyserver;
|
||||||
private String mKeybaseName;
|
private String mKeybaseName;
|
||||||
private String mFbUsername;
|
private String mFbUsername;
|
||||||
|
|
||||||
private String mQuery;
|
private String mQuery;
|
||||||
private ArrayList<String> mOrigins;
|
|
||||||
private Integer mHashCode = null;
|
private Integer mHashCode = null;
|
||||||
|
|
||||||
private boolean mSelected;
|
public ParcelableKeyRing getParcelableKeyRing() {
|
||||||
|
return mParcelableKeyRing;
|
||||||
public int describeContents() {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setParcelableKeyRing(ParcelableKeyRing parcelableKeyRing) {
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
this.mParcelableKeyRing = parcelableKeyRing;
|
||||||
dest.writeString(mPrimaryUserId);
|
|
||||||
dest.writeStringList(mUserIds);
|
|
||||||
dest.writeSerializable(mMergedUserIds);
|
|
||||||
dest.writeLong(mKeyId);
|
|
||||||
dest.writeByte((byte) (mRevoked ? 1 : 0));
|
|
||||||
dest.writeByte((byte) (mExpired ? 1 : 0));
|
|
||||||
dest.writeInt(mDate == null ? 0 : 1);
|
|
||||||
if (mDate != null) {
|
|
||||||
dest.writeLong(mDate.getTime());
|
|
||||||
}
|
|
||||||
dest.writeString(mFingerprintHex);
|
|
||||||
dest.writeString(mKeyIdHex);
|
|
||||||
dest.writeInt(mBitStrength == null ? 0 : 1);
|
|
||||||
if (mBitStrength != null) {
|
|
||||||
dest.writeInt(mBitStrength);
|
|
||||||
}
|
|
||||||
dest.writeString(mAlgorithm);
|
|
||||||
dest.writeByte((byte) (mSecretKey ? 1 : 0));
|
|
||||||
dest.writeByte((byte) (mSelected ? 1 : 0));
|
|
||||||
dest.writeString(mKeybaseName);
|
|
||||||
dest.writeString(mFbUsername);
|
|
||||||
dest.writeStringList(mOrigins);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Creator<ImportKeysListEntry> CREATOR = new Creator<ImportKeysListEntry>() {
|
|
||||||
public ImportKeysListEntry createFromParcel(final Parcel source) {
|
|
||||||
ImportKeysListEntry vr = new ImportKeysListEntry();
|
|
||||||
vr.mPrimaryUserId = source.readString();
|
|
||||||
vr.mUserIds = new ArrayList<>();
|
|
||||||
source.readStringList(vr.mUserIds);
|
|
||||||
vr.mMergedUserIds = (HashMap<String, HashSet<String>>) source.readSerializable();
|
|
||||||
vr.mKeyId = source.readLong();
|
|
||||||
vr.mRevoked = source.readByte() == 1;
|
|
||||||
vr.mExpired = source.readByte() == 1;
|
|
||||||
vr.mDate = source.readInt() != 0 ? new Date(source.readLong()) : null;
|
|
||||||
vr.mFingerprintHex = source.readString();
|
|
||||||
vr.mKeyIdHex = source.readString();
|
|
||||||
vr.mBitStrength = source.readInt() != 0 ? source.readInt() : null;
|
|
||||||
vr.mAlgorithm = source.readString();
|
|
||||||
vr.mSecretKey = source.readByte() == 1;
|
|
||||||
vr.mSelected = source.readByte() == 1;
|
|
||||||
vr.mKeybaseName = source.readString();
|
|
||||||
vr.mFbUsername = source.readString();
|
|
||||||
vr.mOrigins = new ArrayList<>();
|
|
||||||
source.readStringList(vr.mOrigins);
|
|
||||||
|
|
||||||
return vr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImportKeysListEntry[] newArray(final int size) {
|
|
||||||
return new ImportKeysListEntry[size];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public int hashCode() {
|
|
||||||
if (mHashCode != null) {
|
|
||||||
return mHashCode;
|
|
||||||
}
|
|
||||||
return super.hashCode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSameKeyAs(ImportKeysListEntry other) {
|
public boolean hasSameKeyAs(ImportKeysListEntry other) {
|
||||||
@@ -136,12 +87,12 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
return mKeyIdHex;
|
return mKeyIdHex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSelected() {
|
public void setKeyIdHex(String keyIdHex) {
|
||||||
return mSelected;
|
mKeyIdHex = keyIdHex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelected(boolean selected) {
|
public void setKeyId(long keyId) {
|
||||||
mSelected = selected;
|
mKeyIdHex = KeyFormattingUtils.convertKeyIdToHex(keyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isExpired() {
|
public boolean isExpired() {
|
||||||
@@ -152,18 +103,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
mExpired = expired;
|
mExpired = expired;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getKeyId() {
|
|
||||||
return mKeyId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKeyId(long keyId) {
|
|
||||||
mKeyId = keyId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKeyIdHex(String keyIdHex) {
|
|
||||||
mKeyIdHex = keyIdHex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRevoked() {
|
public boolean isRevoked() {
|
||||||
return mRevoked;
|
return mRevoked;
|
||||||
}
|
}
|
||||||
@@ -172,10 +111,22 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
mRevoked = revoked;
|
mRevoked = revoked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isRevokedOrExpired() {
|
||||||
|
return mRevoked || mExpired;
|
||||||
|
}
|
||||||
|
|
||||||
public Date getDate() {
|
public Date getDate() {
|
||||||
return mDate;
|
return mDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isUpdated() {
|
||||||
|
return mUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpdated(boolean updated) {
|
||||||
|
mUpdated = updated;
|
||||||
|
}
|
||||||
|
|
||||||
public void setDate(Date date) {
|
public void setDate(Date date) {
|
||||||
mDate = date;
|
mDate = date;
|
||||||
}
|
}
|
||||||
@@ -188,6 +139,10 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
mFingerprintHex = fingerprintHex;
|
mFingerprintHex = fingerprintHex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setFingerprint(byte[] fingerprint) {
|
||||||
|
mFingerprintHex = KeyFormattingUtils.convertFingerprintToHex(fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
public Integer getBitStrength() {
|
public Integer getBitStrength() {
|
||||||
return mBitStrength;
|
return mBitStrength;
|
||||||
}
|
}
|
||||||
@@ -216,35 +171,38 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
mSecretKey = secretKey;
|
mSecretKey = secretKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<String> getUserIds() {
|
public UserId getPrimaryUserId() {
|
||||||
return mUserIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUserIds(ArrayList<String> userIds) {
|
|
||||||
mUserIds = userIds;
|
|
||||||
updateMergedUserIds();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPrimaryUserId() {
|
|
||||||
return mPrimaryUserId;
|
return mPrimaryUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPrimaryUserId(String uid) {
|
public void setPrimaryUserId(String userId) {
|
||||||
mPrimaryUserId = uid;
|
mPrimaryUserId = KeyRing.splitUserId(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrimaryUserId(UserId primaryUserId) {
|
||||||
|
mPrimaryUserId = primaryUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParcelableHkpKeyserver getKeyserver() {
|
||||||
|
return mKeyserver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyserver(ParcelableHkpKeyserver keyserver) {
|
||||||
|
mKeyserver = keyserver;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKeybaseName() {
|
public String getKeybaseName() {
|
||||||
return mKeybaseName;
|
return mKeybaseName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFbUsername() {
|
|
||||||
return mFbUsername;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKeybaseName(String keybaseName) {
|
public void setKeybaseName(String keybaseName) {
|
||||||
mKeybaseName = keybaseName;
|
mKeybaseName = keybaseName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getFbUsername() {
|
||||||
|
return mFbUsername;
|
||||||
|
}
|
||||||
|
|
||||||
public void setFbUsername(String fbUsername) {
|
public void setFbUsername(String fbUsername) {
|
||||||
mFbUsername = fbUsername;
|
mFbUsername = fbUsername;
|
||||||
}
|
}
|
||||||
@@ -257,16 +215,49 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
mQuery = query;
|
mQuery = query;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<String> getOrigins() {
|
public int hashCode() {
|
||||||
return mOrigins;
|
return mHashCode != null ? mHashCode : super.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addOrigin(String origin) {
|
public List<String> getUserIds() {
|
||||||
mOrigins.add(origin);
|
// To ensure choerency, use methods of this class to edit the list
|
||||||
|
return Collections.unmodifiableList(mUserIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashMap<String, HashSet<String>> getMergedUserIds() {
|
public ArrayList<Map.Entry<String, HashSet<String>>> getSortedUserIds() {
|
||||||
return mMergedUserIds;
|
if (mSortedUserIds == null)
|
||||||
|
sortMergedUserIds();
|
||||||
|
|
||||||
|
return mSortedUserIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserIds(ArrayList<String> userIds) {
|
||||||
|
mUserIds = userIds;
|
||||||
|
updateMergedUserIds();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addUserIds(List<String> userIds) {
|
||||||
|
boolean modified = false;
|
||||||
|
for (String uid : userIds) {
|
||||||
|
if (!mUserIds.contains(uid)) {
|
||||||
|
mUserIds.add(uid);
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modified)
|
||||||
|
updateMergedUserIds();
|
||||||
|
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<String> getKeybaseUserIds() {
|
||||||
|
ArrayList<String> keybaseUserIds = new ArrayList<>();
|
||||||
|
for (String s : mUserIds) {
|
||||||
|
if (s.contains(":"))
|
||||||
|
keybaseUserIds.add(s);
|
||||||
|
}
|
||||||
|
return keybaseUserIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -275,51 +266,44 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
public ImportKeysListEntry() {
|
public ImportKeysListEntry() {
|
||||||
// keys from keyserver are always public keys; from keybase too
|
// keys from keyserver are always public keys; from keybase too
|
||||||
mSecretKey = false;
|
mSecretKey = false;
|
||||||
// do not select by default
|
|
||||||
mSelected = false;
|
|
||||||
mUserIds = new ArrayList<>();
|
mUserIds = new ArrayList<>();
|
||||||
mOrigins = new ArrayList<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor based on key object, used for import from NFC, QR Codes, files
|
* Constructor based on key object, used for import from NFC, QR Codes, files
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
public ImportKeysListEntry(Context ctx, UncachedKeyRing ring) {
|
||||||
public ImportKeysListEntry(Context context, UncachedKeyRing ring) {
|
|
||||||
// selected is default
|
|
||||||
this.mSelected = true;
|
|
||||||
|
|
||||||
mSecretKey = ring.isSecret();
|
mSecretKey = ring.isSecret();
|
||||||
|
|
||||||
UncachedPublicKey key = ring.getPublicKey();
|
UncachedPublicKey key = ring.getPublicKey();
|
||||||
|
setPrimaryUserId(key.getPrimaryUserIdWithFallback());
|
||||||
mHashCode = key.hashCode();
|
setKeyId(key.getKeyId());
|
||||||
|
setFingerprint(key.getFingerprint());
|
||||||
mPrimaryUserId = key.getPrimaryUserIdWithFallback();
|
|
||||||
mUserIds = key.getUnorderedUserIds();
|
|
||||||
updateMergedUserIds();
|
|
||||||
|
|
||||||
// if there was no user id flagged as primary, use the first one
|
|
||||||
if (mPrimaryUserId == null) {
|
|
||||||
mPrimaryUserId = context.getString(R.string.user_id_none);
|
|
||||||
}
|
|
||||||
|
|
||||||
mKeyId = key.getKeyId();
|
|
||||||
mKeyIdHex = KeyFormattingUtils.convertKeyIdToHex(mKeyId);
|
|
||||||
|
|
||||||
// NOTE: Dont use maybe methods for now, they can be wrong.
|
// NOTE: Dont use maybe methods for now, they can be wrong.
|
||||||
mRevoked = false; //key.isMaybeRevoked();
|
mRevoked = false; //key.isMaybeRevoked();
|
||||||
mExpired = false; //key.isMaybeExpired();
|
mExpired = false; //key.isMaybeExpired();
|
||||||
mFingerprintHex = KeyFormattingUtils.convertFingerprintToHex(key.getFingerprint());
|
|
||||||
mBitStrength = key.getBitStrength();
|
mBitStrength = key.getBitStrength();
|
||||||
mCurveOid = key.getCurveOid();
|
mCurveOid = key.getCurveOid();
|
||||||
final int algorithm = key.getAlgorithm();
|
int algorithm = key.getAlgorithm();
|
||||||
mAlgorithm = KeyFormattingUtils.getAlgorithmInfo(context, algorithm, mBitStrength, mCurveOid);
|
mAlgorithm = KeyFormattingUtils.getAlgorithmInfo(ctx, algorithm, mBitStrength, mCurveOid);
|
||||||
|
mHashCode = key.hashCode();
|
||||||
|
|
||||||
|
setUserIds(key.getUnorderedUserIds());
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] encoded = ring.getEncoded();
|
||||||
|
mParcelableKeyRing = new ParcelableKeyRing(encoded);
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateMergedUserIds() {
|
private void updateMergedUserIds() {
|
||||||
mMergedUserIds = new HashMap<>();
|
mMergedUserIds = new HashMap<>();
|
||||||
for (String userId : mUserIds) {
|
for (String userId : mUserIds) {
|
||||||
OpenPgpUtils.UserId userIdSplit = KeyRing.splitUserId(userId);
|
UserId userIdSplit = KeyRing.splitUserId(userId);
|
||||||
|
|
||||||
// TODO: comment field?
|
// TODO: comment field?
|
||||||
|
|
||||||
@@ -341,6 +325,87 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
|||||||
mMergedUserIds.put(userId, new HashSet<String>());
|
mMergedUserIds.put(userId, new HashSet<String>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mSortedUserIds = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sortMergedUserIds() {
|
||||||
|
mSortedUserIds = new ArrayList<>(mMergedUserIds.entrySet());
|
||||||
|
|
||||||
|
Collections.sort(mSortedUserIds, new Comparator<Map.Entry<String, HashSet<String>>>() {
|
||||||
|
@Override
|
||||||
|
public int compare(Map.Entry<String, HashSet<String>> entry1,
|
||||||
|
Map.Entry<String, HashSet<String>> entry2) {
|
||||||
|
|
||||||
|
// sort keybase UserIds after non-Keybase
|
||||||
|
boolean e1IsKeybase = entry1.getKey().contains(":");
|
||||||
|
boolean e2IsKeybase = entry2.getKey().contains(":");
|
||||||
|
if (e1IsKeybase != e2IsKeybase) {
|
||||||
|
return (e1IsKeybase) ? 1 : -1;
|
||||||
|
}
|
||||||
|
return entry1.getKey().compareTo(entry2.getKey());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeParcelable(mParcelableKeyRing, flags);
|
||||||
|
dest.writeSerializable(mPrimaryUserId);
|
||||||
|
dest.writeStringList(mUserIds);
|
||||||
|
dest.writeSerializable(mMergedUserIds);
|
||||||
|
dest.writeByte((byte) (mRevoked ? 1 : 0));
|
||||||
|
dest.writeByte((byte) (mExpired ? 1 : 0));
|
||||||
|
dest.writeByte((byte) (mUpdated ? 1 : 0));
|
||||||
|
dest.writeInt(mDate == null ? 0 : 1);
|
||||||
|
if (mDate != null) {
|
||||||
|
dest.writeLong(mDate.getTime());
|
||||||
|
}
|
||||||
|
dest.writeString(mFingerprintHex);
|
||||||
|
dest.writeString(mKeyIdHex);
|
||||||
|
dest.writeInt(mBitStrength == null ? 0 : 1);
|
||||||
|
if (mBitStrength != null) {
|
||||||
|
dest.writeInt(mBitStrength);
|
||||||
|
}
|
||||||
|
dest.writeString(mAlgorithm);
|
||||||
|
dest.writeByte((byte) (mSecretKey ? 1 : 0));
|
||||||
|
dest.writeParcelable(mKeyserver, flags);
|
||||||
|
dest.writeString(mKeybaseName);
|
||||||
|
dest.writeString(mFbUsername);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<ImportKeysListEntry> CREATOR = new Creator<ImportKeysListEntry>() {
|
||||||
|
public ImportKeysListEntry createFromParcel(final Parcel source) {
|
||||||
|
ImportKeysListEntry vr = new ImportKeysListEntry();
|
||||||
|
|
||||||
|
vr.mParcelableKeyRing = source.readParcelable(ParcelableKeyRing.class.getClassLoader());
|
||||||
|
vr.mPrimaryUserId = (UserId) source.readSerializable();
|
||||||
|
vr.mUserIds = new ArrayList<>();
|
||||||
|
source.readStringList(vr.mUserIds);
|
||||||
|
vr.mMergedUserIds = (HashMap<String, HashSet<String>>) source.readSerializable();
|
||||||
|
vr.mRevoked = source.readByte() == 1;
|
||||||
|
vr.mExpired = source.readByte() == 1;
|
||||||
|
vr.mUpdated = source.readByte() == 1;
|
||||||
|
vr.mDate = source.readInt() != 0 ? new Date(source.readLong()) : null;
|
||||||
|
vr.mFingerprintHex = source.readString();
|
||||||
|
vr.mKeyIdHex = source.readString();
|
||||||
|
vr.mBitStrength = source.readInt() != 0 ? source.readInt() : null;
|
||||||
|
vr.mAlgorithm = source.readString();
|
||||||
|
vr.mSecretKey = source.readByte() == 1;
|
||||||
|
vr.mKeyserver = source.readParcelable(ParcelableHkpKeyserver.class.getClassLoader());
|
||||||
|
vr.mKeybaseName = source.readString();
|
||||||
|
vr.mFbUsername = source.readString();
|
||||||
|
|
||||||
|
return vr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImportKeysListEntry[] newArray(final int size) {
|
||||||
|
return new ImportKeysListEntry[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -18,8 +18,8 @@
|
|||||||
package org.sufficientlysecure.keychain.keyimport;
|
package org.sufficientlysecure.keychain.keyimport;
|
||||||
|
|
||||||
import com.textuality.keybase.lib.KeybaseException;
|
import com.textuality.keybase.lib.KeybaseException;
|
||||||
import com.textuality.keybase.lib.Match;
|
|
||||||
import com.textuality.keybase.lib.KeybaseQuery;
|
import com.textuality.keybase.lib.KeybaseQuery;
|
||||||
|
import com.textuality.keybase.lib.Match;
|
||||||
import com.textuality.keybase.lib.User;
|
import com.textuality.keybase.lib.User;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
@@ -32,7 +32,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class KeybaseKeyserver extends Keyserver {
|
public class KeybaseKeyserver extends Keyserver {
|
||||||
public static final String ORIGIN = "keybase:keybase.io";
|
|
||||||
|
|
||||||
public KeybaseKeyserver() {
|
public KeybaseKeyserver() {
|
||||||
}
|
}
|
||||||
@@ -68,7 +67,6 @@ public class KeybaseKeyserver extends Keyserver {
|
|||||||
private ImportKeysListEntry makeEntry(Match match, String query) throws KeybaseException {
|
private ImportKeysListEntry makeEntry(Match match, String query) throws KeybaseException {
|
||||||
final ImportKeysListEntry entry = new ImportKeysListEntry();
|
final ImportKeysListEntry entry = new ImportKeysListEntry();
|
||||||
entry.setQuery(query);
|
entry.setQuery(query);
|
||||||
entry.addOrigin(ORIGIN);
|
|
||||||
|
|
||||||
entry.setRevoked(false); // keybase doesn’t say anything about revoked keys
|
entry.setRevoked(false); // keybase doesn’t say anything about revoked keys
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,10 @@ public abstract class Keyserver {
|
|||||||
private static final long serialVersionUID = 2703768928624654518L;
|
private static final long serialVersionUID = 2703768928624654518L;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class QueryNoEnabledSourceException extends QueryNeedsRepairException {
|
||||||
|
private static final long serialVersionUID = 2703768928624654519L;
|
||||||
|
}
|
||||||
|
|
||||||
public static class AddKeyException extends Exception {
|
public static class AddKeyException extends Exception {
|
||||||
private static final long serialVersionUID = -507574859137295530L;
|
private static final long serialVersionUID = -507574859137295530L;
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-9
@@ -22,6 +22,7 @@ import android.os.Parcel;
|
|||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
@@ -172,9 +173,11 @@ public class ParcelableHkpKeyserver extends Keyserver implements Parcelable {
|
|||||||
URI originalURI = new URI(keyserverUrl);
|
URI originalURI = new URI(keyserverUrl);
|
||||||
|
|
||||||
String scheme = originalURI.getScheme();
|
String scheme = originalURI.getScheme();
|
||||||
if (scheme == null
|
if (scheme == null) {
|
||||||
|| (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme)
|
throw new URISyntaxException("", "scheme null!");
|
||||||
&& !"hkp".equalsIgnoreCase(scheme) && !"hkps".equalsIgnoreCase(scheme))) {
|
}
|
||||||
|
if (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme)
|
||||||
|
&& !"hkp".equalsIgnoreCase(scheme) && !"hkps".equalsIgnoreCase(scheme)) {
|
||||||
throw new URISyntaxException(scheme, "unsupported scheme!");
|
throw new URISyntaxException(scheme, "unsupported scheme!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +272,6 @@ public class ParcelableHkpKeyserver extends Keyserver implements Parcelable {
|
|||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
final ImportKeysListEntry entry = new ImportKeysListEntry();
|
final ImportKeysListEntry entry = new ImportKeysListEntry();
|
||||||
entry.setQuery(query);
|
entry.setQuery(query);
|
||||||
entry.addOrigin(getHostID());
|
|
||||||
|
|
||||||
// group 1 contains the full fingerprint (v4) or the long key id if available
|
// group 1 contains the full fingerprint (v4) or the long key id if available
|
||||||
// see https://bitbucket.org/skskeyserver/sks-keyserver/pull-request/12/fixes-for-machine-readable-indexes/diff
|
// see https://bitbucket.org/skskeyserver/sks-keyserver/pull-request/12/fixes-for-machine-readable-indexes/diff
|
||||||
@@ -293,10 +295,10 @@ public class ParcelableHkpKeyserver extends Keyserver implements Parcelable {
|
|||||||
int algorithmId = Integer.decode(matcher.group(2));
|
int algorithmId = Integer.decode(matcher.group(2));
|
||||||
entry.setAlgorithm(KeyFormattingUtils.getAlgorithmInfo(algorithmId, bitSize, null));
|
entry.setAlgorithm(KeyFormattingUtils.getAlgorithmInfo(algorithmId, bitSize, null));
|
||||||
|
|
||||||
final long creationDate = Long.parseLong(matcher.group(4));
|
long creationDate = Long.parseLong(matcher.group(4));
|
||||||
final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
||||||
tmpGreg.setTimeInMillis(creationDate * 1000);
|
calendar.setTimeInMillis(creationDate * 1000);
|
||||||
entry.setDate(tmpGreg.getTime());
|
entry.setDate(calendar.getTime());
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
Log.e(Constants.TAG, "Conversation for bit size, algorithm, or creation date failed.", e);
|
Log.e(Constants.TAG, "Conversation for bit size, algorithm, or creation date failed.", e);
|
||||||
// skip this key
|
// skip this key
|
||||||
@@ -305,7 +307,18 @@ public class ParcelableHkpKeyserver extends Keyserver implements Parcelable {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
entry.setRevoked(matcher.group(6).contains("r"));
|
entry.setRevoked(matcher.group(6).contains("r"));
|
||||||
entry.setExpired(matcher.group(6).contains("e"));
|
boolean expired = matcher.group(6).contains("e");
|
||||||
|
|
||||||
|
// It may be expired even without flag, thus check expiration date
|
||||||
|
String expiration;
|
||||||
|
if (!expired && !(expiration = matcher.group(5)).isEmpty()) {
|
||||||
|
long expirationDate = Long.parseLong(expiration);
|
||||||
|
TimeZone timeZoneUTC = TimeZone.getTimeZone("UTC");
|
||||||
|
GregorianCalendar calendar = new GregorianCalendar(timeZoneUTC);
|
||||||
|
calendar.setTimeInMillis(expirationDate * 1000);
|
||||||
|
expired = new GregorianCalendar(timeZoneUTC).compareTo(calendar) >= 0;
|
||||||
|
}
|
||||||
|
entry.setExpired(expired);
|
||||||
} catch (NullPointerException e) {
|
} catch (NullPointerException e) {
|
||||||
Log.e(Constants.TAG, "Check for revocation or expiry failed.", e);
|
Log.e(Constants.TAG, "Check for revocation or expiry failed.", e);
|
||||||
// skip this key
|
// skip this key
|
||||||
@@ -338,6 +351,7 @@ public class ParcelableHkpKeyserver extends Keyserver implements Parcelable {
|
|||||||
}
|
}
|
||||||
entry.setUserIds(userIds);
|
entry.setUserIds(userIds);
|
||||||
entry.setPrimaryUserId(userIds.get(0));
|
entry.setPrimaryUserId(userIds.get(0));
|
||||||
|
entry.setKeyserver(this);
|
||||||
|
|
||||||
results.add(entry);
|
results.add(entry);
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-27
@@ -21,7 +21,8 @@ package org.sufficientlysecure.keychain.keyimport;
|
|||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
/** This class is a parcelable representation of either a keyring as raw data,
|
/**
|
||||||
|
* This class is a parcelable representation of either a keyring as raw data,
|
||||||
* or a (unique) reference to one as a fingerprint, keyid, or keybase name.
|
* or a (unique) reference to one as a fingerprint, keyid, or keybase name.
|
||||||
*/
|
*/
|
||||||
public class ParcelableKeyRing implements Parcelable {
|
public class ParcelableKeyRing implements Parcelable {
|
||||||
@@ -35,36 +36,23 @@ public class ParcelableKeyRing implements Parcelable {
|
|||||||
public final String mFbUsername;
|
public final String mFbUsername;
|
||||||
|
|
||||||
public ParcelableKeyRing(byte[] bytes) {
|
public ParcelableKeyRing(byte[] bytes) {
|
||||||
this(null, bytes, false);
|
this(bytes, null, null, null, null);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param disAmbiguator useless parameter intended to distinguish this overloaded constructor
|
|
||||||
* for when null is passed as first two arguments
|
|
||||||
*/
|
|
||||||
public ParcelableKeyRing(String expectedFingerprint, byte[] bytes, boolean disAmbiguator) {
|
|
||||||
mBytes = bytes;
|
|
||||||
mExpectedFingerprint = expectedFingerprint;
|
|
||||||
mKeyIdHex = null;
|
|
||||||
mKeybaseName = null;
|
|
||||||
mFbUsername = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ParcelableKeyRing(String expectedFingerprint, String keyIdHex) {
|
|
||||||
mBytes = null;
|
|
||||||
mExpectedFingerprint = expectedFingerprint;
|
|
||||||
mKeyIdHex = keyIdHex;
|
|
||||||
mKeybaseName = null;
|
|
||||||
mFbUsername = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParcelableKeyRing(String expectedFingerprint, String keyIdHex, String keybaseName,
|
public ParcelableKeyRing(String expectedFingerprint, String keyIdHex, String keybaseName,
|
||||||
String fbUsername) {
|
String fbUsername) {
|
||||||
mBytes = null;
|
|
||||||
mExpectedFingerprint = expectedFingerprint;
|
this(null, expectedFingerprint, keyIdHex, keybaseName, fbUsername);
|
||||||
mKeyIdHex = keyIdHex;
|
}
|
||||||
mKeybaseName = keybaseName;
|
|
||||||
mFbUsername = fbUsername;
|
public ParcelableKeyRing(byte[] bytes, String expectedFingerprint, String keyIdHex,
|
||||||
|
String keybaseName, String fbUsername) {
|
||||||
|
|
||||||
|
this.mBytes = bytes;
|
||||||
|
this.mExpectedFingerprint = expectedFingerprint;
|
||||||
|
this.mKeyIdHex = keyIdHex;
|
||||||
|
this.mKeybaseName = keybaseName;
|
||||||
|
this.mFbUsername = fbUsername;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ParcelableKeyRing(Parcel source) {
|
private ParcelableKeyRing(Parcel source) {
|
||||||
@@ -78,6 +66,7 @@ public class ParcelableKeyRing implements Parcelable {
|
|||||||
|
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeByteArray(mBytes);
|
dest.writeByteArray(mBytes);
|
||||||
|
|
||||||
dest.writeString(mExpectedFingerprint);
|
dest.writeString(mExpectedFingerprint);
|
||||||
dest.writeString(mKeyIdHex);
|
dest.writeString(mKeyIdHex);
|
||||||
dest.writeString(mKeybaseName);
|
dest.writeString(mKeybaseName);
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui.adapter;
|
package org.sufficientlysecure.keychain.keyimport.processing;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||||
|
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package org.sufficientlysecure.keychain.keyimport.processing;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
public class BytesLoaderState implements LoaderState {
|
||||||
|
|
||||||
|
public byte[] mKeyBytes;
|
||||||
|
public Uri mDataUri;
|
||||||
|
|
||||||
|
public BytesLoaderState(byte[] keyBytes, Uri dataUri) {
|
||||||
|
mKeyBytes = keyBytes;
|
||||||
|
mDataUri = dataUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBasicModeSupported() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package org.sufficientlysecure.keychain.keyimport.processing;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
|
|
||||||
|
public class CloudLoaderState implements LoaderState {
|
||||||
|
|
||||||
|
public Preferences.CloudSearchPrefs mCloudPrefs;
|
||||||
|
public String mServerQuery;
|
||||||
|
|
||||||
|
public CloudLoaderState(String serverQuery, Preferences.CloudSearchPrefs cloudPrefs) {
|
||||||
|
mServerQuery = serverQuery;
|
||||||
|
mCloudPrefs = cloudPrefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBasicModeSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+25
-18
@@ -15,7 +15,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui.adapter;
|
package org.sufficientlysecure.keychain.keyimport.processing;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
@@ -25,6 +25,7 @@ import org.sufficientlysecure.keychain.Constants;
|
|||||||
import org.sufficientlysecure.keychain.keyimport.CloudSearch;
|
import org.sufficientlysecure.keychain.keyimport.CloudSearch;
|
||||||
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
||||||
import org.sufficientlysecure.keychain.keyimport.Keyserver;
|
import org.sufficientlysecure.keychain.keyimport.Keyserver;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||||
import org.sufficientlysecure.keychain.operations.results.GetKeyResult;
|
import org.sufficientlysecure.keychain.operations.results.GetKeyResult;
|
||||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||||
@@ -38,11 +39,9 @@ import java.util.ArrayList;
|
|||||||
|
|
||||||
public class ImportKeysListCloudLoader
|
public class ImportKeysListCloudLoader
|
||||||
extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
|
extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
|
||||||
Context mContext;
|
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
Preferences.CloudSearchPrefs mCloudPrefs;
|
private CloudLoaderState mState;
|
||||||
String mServerQuery;
|
|
||||||
private ParcelableProxy mParcelableProxy;
|
private ParcelableProxy mParcelableProxy;
|
||||||
|
|
||||||
private ArrayList<ImportKeysListEntry> mEntryList = new ArrayList<>();
|
private ArrayList<ImportKeysListEntry> mEntryList = new ArrayList<>();
|
||||||
@@ -51,18 +50,18 @@ public class ImportKeysListCloudLoader
|
|||||||
/**
|
/**
|
||||||
* Searches a keyserver as specified in cloudPrefs, using an explicit proxy if passed
|
* 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,
|
* @param loaderState state containing the string to search on servers for (if it is a
|
||||||
* will enforce fingerprint check
|
* fingerprint, will enforce fingerprint check) and the keyserver to
|
||||||
* @param cloudPrefs contains keyserver to search on, whether to search on the keyserver,
|
* search on (whether to search on the keyserver, and whether to search
|
||||||
* and whether to search keybase.io
|
* keybase.io)
|
||||||
* @param parcelableProxy explicit proxy to use. If null, will retrieve from preferences
|
* @param parcelableProxy explicit proxy to use. If null, will retrieve from preferences
|
||||||
*/
|
*/
|
||||||
public ImportKeysListCloudLoader(Context context, String serverQuery, Preferences.CloudSearchPrefs cloudPrefs,
|
public ImportKeysListCloudLoader(Context context, CloudLoaderState loaderState,
|
||||||
@Nullable ParcelableProxy parcelableProxy) {
|
@Nullable ParcelableProxy parcelableProxy) {
|
||||||
|
|
||||||
super(context);
|
super(context);
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mServerQuery = serverQuery;
|
mState = loaderState;
|
||||||
mCloudPrefs = cloudPrefs;
|
|
||||||
mParcelableProxy = parcelableProxy;
|
mParcelableProxy = parcelableProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,18 +69,24 @@ public class ImportKeysListCloudLoader
|
|||||||
public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() {
|
public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() {
|
||||||
mEntryListWrapper = new AsyncTaskResultWrapper<>(mEntryList, null);
|
mEntryListWrapper = new AsyncTaskResultWrapper<>(mEntryList, null);
|
||||||
|
|
||||||
if (mServerQuery == null) {
|
if (mState.mServerQuery == null) {
|
||||||
Log.e(Constants.TAG, "mServerQuery is null!");
|
Log.e(Constants.TAG, "mServerQuery is null!");
|
||||||
return mEntryListWrapper;
|
return mEntryListWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mServerQuery.startsWith("0x") && mServerQuery.length() == 42) {
|
if (mState.mServerQuery.startsWith("0x") && mState.mServerQuery.length() == 42) {
|
||||||
Log.d(Constants.TAG, "This search is based on a unique fingerprint. Enforce a fingerprint check!");
|
Log.d(Constants.TAG, "This search is based on a unique fingerprint. Enforce a fingerprint check!");
|
||||||
queryServer(true);
|
queryServer(true);
|
||||||
} else {
|
} else {
|
||||||
queryServer(false);
|
queryServer(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now we have all the data needed to build the parcelable key ring for this key
|
||||||
|
for (ImportKeysListEntry e : mEntryList) {
|
||||||
|
e.setParcelableKeyRing(new ParcelableKeyRing(e.getFingerprintHex(), e.getKeyIdHex(),
|
||||||
|
e.getKeybaseName(), e.getFbUsername()));
|
||||||
|
}
|
||||||
|
|
||||||
return mEntryListWrapper;
|
return mEntryListWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,15 +138,15 @@ public class ImportKeysListCloudLoader
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
ArrayList<ImportKeysListEntry> searchResult = CloudSearch.search(
|
ArrayList<ImportKeysListEntry> searchResult = CloudSearch.search(
|
||||||
mServerQuery,
|
mState.mServerQuery,
|
||||||
mCloudPrefs,
|
mState.mCloudPrefs,
|
||||||
proxy
|
proxy
|
||||||
);
|
);
|
||||||
|
|
||||||
mEntryList.clear();
|
mEntryList.clear();
|
||||||
// add result to data
|
// add result to data
|
||||||
if (enforceFingerprint) {
|
if (enforceFingerprint) {
|
||||||
String fingerprint = mServerQuery.substring(2);
|
String fingerprint = mState.mServerQuery.substring(2);
|
||||||
Log.d(Constants.TAG, "fingerprint: " + fingerprint);
|
Log.d(Constants.TAG, "fingerprint: " + fingerprint);
|
||||||
// query must return only one result!
|
// query must return only one result!
|
||||||
if (searchResult.size() == 1) {
|
if (searchResult.size() == 1) {
|
||||||
@@ -151,7 +156,6 @@ public class ImportKeysListCloudLoader
|
|||||||
* to enforce a check when the key is imported by KeychainService
|
* to enforce a check when the key is imported by KeychainService
|
||||||
*/
|
*/
|
||||||
uniqueEntry.setFingerprintHex(fingerprint);
|
uniqueEntry.setFingerprintHex(fingerprint);
|
||||||
uniqueEntry.setSelected(true);
|
|
||||||
mEntryList.add(uniqueEntry);
|
mEntryList.add(uniqueEntry);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -175,6 +179,9 @@ public class ImportKeysListCloudLoader
|
|||||||
} else if (e instanceof Keyserver.QueryTooShortOrTooManyResponsesException) {
|
} else if (e instanceof Keyserver.QueryTooShortOrTooManyResponsesException) {
|
||||||
error = GetKeyResult.RESULT_ERROR_TOO_SHORT_OR_TOO_MANY_RESPONSES;
|
error = GetKeyResult.RESULT_ERROR_TOO_SHORT_OR_TOO_MANY_RESPONSES;
|
||||||
logType = OperationResult.LogType.MSG_GET_QUERY_TOO_SHORT_OR_TOO_MANY_RESPONSES;
|
logType = OperationResult.LogType.MSG_GET_QUERY_TOO_SHORT_OR_TOO_MANY_RESPONSES;
|
||||||
|
} else if (e instanceof Keyserver.QueryNoEnabledSourceException) {
|
||||||
|
error = GetKeyResult.RESULT_ERROR_NO_ENABLED_SOURCE;
|
||||||
|
logType = OperationResult.LogType.MSG_GET_NO_ENABLED_SOURCE;
|
||||||
}
|
}
|
||||||
OperationResult.OperationLog log = new OperationResult.OperationLog();
|
OperationResult.OperationLog log = new OperationResult.OperationLog();
|
||||||
log.add(logType, 0);
|
log.add(logType, 0);
|
||||||
+39
-51
@@ -15,9 +15,26 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui.adapter;
|
package org.sufficientlysecure.keychain.keyimport.processing;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.content.AsyncTaskLoader;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.GetKeyResult;
|
||||||
|
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.UncachedKeyRing;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow;
|
||||||
|
import org.sufficientlysecure.keychain.util.FileHelper;
|
||||||
|
import org.sufficientlysecure.keychain.util.InputData;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
@@ -25,40 +42,19 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.content.AsyncTaskLoader;
|
|
||||||
import android.support.v4.util.LongSparseArray;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
|
||||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
|
||||||
import org.sufficientlysecure.keychain.operations.results.GetKeyResult;
|
|
||||||
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.UncachedKeyRing;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow;
|
|
||||||
import org.sufficientlysecure.keychain.ui.ImportKeysListFragment.BytesLoaderState;
|
|
||||||
import org.sufficientlysecure.keychain.util.FileHelper;
|
|
||||||
import org.sufficientlysecure.keychain.util.InputData;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
|
|
||||||
|
|
||||||
public class ImportKeysListLoader
|
public class ImportKeysListLoader
|
||||||
extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
|
extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
|
||||||
|
|
||||||
final Context mContext;
|
private Context mContext;
|
||||||
final BytesLoaderState mLoaderState;
|
private BytesLoaderState mState;
|
||||||
|
|
||||||
ArrayList<ImportKeysListEntry> mData = new ArrayList<>();
|
private ArrayList<ImportKeysListEntry> mData = new ArrayList<>();
|
||||||
LongSparseArray<ParcelableKeyRing> mParcelableRings = new LongSparseArray<>();
|
private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper;
|
||||||
AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper;
|
|
||||||
|
|
||||||
public ImportKeysListLoader(Context context, BytesLoaderState inputData) {
|
public ImportKeysListLoader(Context context, BytesLoaderState loaderState) {
|
||||||
super(context);
|
super(context);
|
||||||
this.mContext = context;
|
mContext = context;
|
||||||
this.mLoaderState = inputData;
|
mState = loaderState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -73,13 +69,13 @@ public class ImportKeysListLoader
|
|||||||
mEntryListWrapper = new AsyncTaskResultWrapper<>(mData, getKeyResult);
|
mEntryListWrapper = new AsyncTaskResultWrapper<>(mData, getKeyResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mLoaderState == null) {
|
if (mState == null) {
|
||||||
Log.e(Constants.TAG, "Input data is null!");
|
Log.e(Constants.TAG, "Input data is null!");
|
||||||
return mEntryListWrapper;
|
return mEntryListWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
InputData inputData = getInputData(getContext(), mLoaderState);
|
InputData inputData = getInputData(mState);
|
||||||
generateListOfKeyrings(inputData);
|
generateListOfKeyrings(inputData);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
OperationLog log = new OperationLog();
|
OperationLog log = new OperationLog();
|
||||||
@@ -109,16 +105,9 @@ public class ImportKeysListLoader
|
|||||||
super.cancelLoad();
|
super.cancelLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void deliverResult(AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> data) {
|
* Reads all PGPKeyRing objects from the bytes of an InputData object.
|
||||||
super.deliverResult(data);
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
public LongSparseArray<ParcelableKeyRing> getParcelableRings() {
|
|
||||||
return mParcelableRings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Reads all PGPKeyRing objects from the bytes of an InputData object. */
|
|
||||||
private void generateListOfKeyrings(InputData inputData) {
|
private void generateListOfKeyrings(InputData inputData) {
|
||||||
PositionAwareInputStream progressIn = new PositionAwareInputStream(
|
PositionAwareInputStream progressIn = new PositionAwareInputStream(
|
||||||
inputData.getInputStream());
|
inputData.getInputStream());
|
||||||
@@ -131,10 +120,7 @@ public class ImportKeysListLoader
|
|||||||
// parse all keyrings
|
// parse all keyrings
|
||||||
IteratorWithIOThrow<UncachedKeyRing> it = UncachedKeyRing.fromStream(bufferedInput);
|
IteratorWithIOThrow<UncachedKeyRing> it = UncachedKeyRing.fromStream(bufferedInput);
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
UncachedKeyRing ring = it.next();
|
mData.add(new ImportKeysListEntry(mContext, it.next()));
|
||||||
ImportKeysListEntry item = new ImportKeysListEntry(getContext(), ring);
|
|
||||||
mData.add(item);
|
|
||||||
mParcelableRings.put(item.hashCode(), new ParcelableKeyRing(ring.getEncoded()));
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(Constants.TAG, "IOException on parsing key file! Return NoValidKeysException!", e);
|
Log.e(Constants.TAG, "IOException on parsing key file! Return NoValidKeysException!", e);
|
||||||
@@ -147,13 +133,15 @@ public class ImportKeysListLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private static InputData getInputData(Context context, BytesLoaderState loaderState) throws FileNotFoundException {
|
private InputData getInputData(BytesLoaderState ls)
|
||||||
|
throws FileNotFoundException {
|
||||||
|
|
||||||
InputData inputData;
|
InputData inputData;
|
||||||
if (loaderState.mKeyBytes != null) {
|
if (ls.mKeyBytes != null) {
|
||||||
inputData = new InputData(new ByteArrayInputStream(loaderState.mKeyBytes), loaderState.mKeyBytes.length);
|
inputData = new InputData(new ByteArrayInputStream(ls.mKeyBytes), ls.mKeyBytes.length);
|
||||||
} else if (loaderState.mDataUri != null) {
|
} else if (ls.mDataUri != null) {
|
||||||
InputStream inputStream = context.getContentResolver().openInputStream(loaderState.mDataUri);
|
InputStream inputStream = mContext.getContentResolver().openInputStream(ls.mDataUri);
|
||||||
long length = FileHelper.getFileSize(context, loaderState.mDataUri, -1);
|
long length = FileHelper.getFileSize(mContext, ls.mDataUri, -1);
|
||||||
|
|
||||||
inputData = new InputData(inputStream, length);
|
inputData = new InputData(inputStream, length);
|
||||||
} else {
|
} else {
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package org.sufficientlysecure.keychain.keyimport.processing;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ImportKeysListener extends ImportKeysResultListener {
|
||||||
|
|
||||||
|
void loadKeys(LoaderState loaderState);
|
||||||
|
|
||||||
|
void importKeys(List<ImportKeysListEntry> entries);
|
||||||
|
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
package org.sufficientlysecure.keychain.keyimport.processing;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||||
|
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||||
|
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||||
|
|
||||||
|
public class ImportKeysOperationCallback implements
|
||||||
|
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
||||||
|
|
||||||
|
private ImportKeysResultListener mResultListener;
|
||||||
|
private ImportKeyringParcel mKeyringParcel;
|
||||||
|
|
||||||
|
public ImportKeysOperationCallback(
|
||||||
|
ImportKeysResultListener resultListener,
|
||||||
|
ImportKeyringParcel inputParcel
|
||||||
|
) {
|
||||||
|
this.mResultListener = resultListener;
|
||||||
|
this.mKeyringParcel = inputParcel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImportKeyringParcel createOperationInput() {
|
||||||
|
return mKeyringParcel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCryptoOperationSuccess(ImportKeyResult result) {
|
||||||
|
mResultListener.handleResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCryptoOperationCancelled() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCryptoOperationError(ImportKeyResult result) {
|
||||||
|
mResultListener.handleResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
package org.sufficientlysecure.keychain.keyimport.processing;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||||
|
|
||||||
|
public interface ImportKeysResultListener {
|
||||||
|
|
||||||
|
void handleResult(ImportKeyResult result);
|
||||||
|
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package org.sufficientlysecure.keychain.keyimport.processing;
|
||||||
|
|
||||||
|
public interface LoaderState {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic mode includes ability to import all keys retrieved from the selected source
|
||||||
|
* This doesn't make sense for all sources (for example keyservers..)
|
||||||
|
*
|
||||||
|
* @return if currently selected source supports basic mode
|
||||||
|
*/
|
||||||
|
boolean isBasicModeSupported();
|
||||||
|
|
||||||
|
}
|
||||||
+68
-56
@@ -19,20 +19,6 @@
|
|||||||
package org.sufficientlysecure.keychain.operations;
|
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.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
@@ -49,6 +35,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
|||||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||||
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
|
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.Progressable;
|
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||||
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
@@ -57,14 +44,28 @@ import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
|
|||||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||||
|
import org.sufficientlysecure.keychain.util.IteratorWithSize;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
||||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
|
|
||||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||||
import org.sufficientlysecure.keychain.util.Preferences;
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||||
|
|
||||||
|
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.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An operation class which implements high level import
|
* An operation class which implements high level import
|
||||||
* operations.
|
* operations.
|
||||||
@@ -79,13 +80,13 @@ import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
|||||||
* not include self certificates for user ids in the secret keyring. The import
|
* 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
|
* method here will generally import keyrings in the order given by the
|
||||||
* iterator, so this should be ensured beforehand.
|
* iterator, so this should be ensured beforehand.
|
||||||
*
|
|
||||||
* @see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries()
|
|
||||||
*/
|
*/
|
||||||
public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
||||||
|
|
||||||
private static final int MAX_THREADS = 10;
|
private static final int MAX_THREADS = 10;
|
||||||
|
|
||||||
|
public static final String CACHE_FILE_NAME = "key_import.pcl";
|
||||||
|
|
||||||
public ImportOperation(Context context, ProviderHelper providerHelper, Progressable
|
public ImportOperation(Context context, ProviderHelper providerHelper, Progressable
|
||||||
progressable) {
|
progressable) {
|
||||||
super(context, providerHelper, progressable);
|
super(context, providerHelper, progressable);
|
||||||
@@ -98,20 +99,20 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
|
|
||||||
// Overloaded functions for using progressable supplied in constructor during import
|
// Overloaded functions for using progressable supplied in constructor during import
|
||||||
public ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
|
public ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
|
||||||
ParcelableHkpKeyserver hkpKeyserver, ParcelableProxy proxy) {
|
ParcelableHkpKeyserver keyserver, ParcelableProxy proxy, boolean skipSave) {
|
||||||
return serialKeyRingImport(entries, num, hkpKeyserver, mProgressable, proxy);
|
return serialKeyRingImport(entries, num, keyserver, mProgressable, proxy, skipSave);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private ImportKeyResult serialKeyRingImport(ParcelableFileCache<ParcelableKeyRing> cache,
|
private ImportKeyResult serialKeyRingImport(ParcelableFileCache<ParcelableKeyRing> cache,
|
||||||
ParcelableHkpKeyserver hkpKeyserver, ParcelableProxy proxy) {
|
ParcelableHkpKeyserver keyserver, ParcelableProxy proxy, boolean skipSave) {
|
||||||
|
|
||||||
// get entries from cached file
|
// get entries from cached file
|
||||||
try {
|
try {
|
||||||
IteratorWithSize<ParcelableKeyRing> it = cache.readCache();
|
IteratorWithSize<ParcelableKeyRing> it = cache.readCache();
|
||||||
int numEntries = it.getSize();
|
int numEntries = it.getSize();
|
||||||
|
|
||||||
return serialKeyRingImport(it, numEntries, hkpKeyserver, mProgressable, proxy);
|
return serialKeyRingImport(it, numEntries, keyserver, mProgressable, proxy, skipSave);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
||||||
// Special treatment here, we need a lot
|
// Special treatment here, we need a lot
|
||||||
@@ -137,7 +138,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
@NonNull
|
@NonNull
|
||||||
private ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
|
private ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
|
||||||
ParcelableHkpKeyserver hkpKeyserver, Progressable progressable,
|
ParcelableHkpKeyserver hkpKeyserver, Progressable progressable,
|
||||||
@NonNull ParcelableProxy proxy) {
|
@NonNull ParcelableProxy proxy, boolean skipSave) {
|
||||||
if (progressable != null) {
|
if (progressable != null) {
|
||||||
progressable.setProgress(R.string.progress_importing, 0, 100);
|
progressable.setProgress(R.string.progress_importing, 0, 100);
|
||||||
}
|
}
|
||||||
@@ -153,6 +154,8 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
int newKeys = 0, updatedKeys = 0, badKeys = 0, secret = 0;
|
int newKeys = 0, updatedKeys = 0, badKeys = 0, secret = 0;
|
||||||
ArrayList<Long> importedMasterKeyIds = new ArrayList<>();
|
ArrayList<Long> importedMasterKeyIds = new ArrayList<>();
|
||||||
|
|
||||||
|
ArrayList<CanonicalizedKeyRing> canKeyRings = new ArrayList<>();
|
||||||
|
|
||||||
boolean cancelled = false;
|
boolean cancelled = false;
|
||||||
int position = 0;
|
int position = 0;
|
||||||
double progSteps = 100.0 / num;
|
double progSteps = 100.0 / num;
|
||||||
@@ -314,14 +317,14 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
// and https://github.com/open-keychain/open-keychain/issues/1480
|
// and https://github.com/open-keychain/open-keychain/issues/1480
|
||||||
synchronized (mProviderHelper) {
|
synchronized (mProviderHelper) {
|
||||||
mProviderHelper.clearLog();
|
mProviderHelper.clearLog();
|
||||||
|
ProgressScaler progressScaler = new ProgressScaler(progressable, (int) (position * progSteps),
|
||||||
|
(int) ((position + 1) * progSteps), 100);
|
||||||
if (key.isSecret()) {
|
if (key.isSecret()) {
|
||||||
result = mProviderHelper.saveSecretKeyRing(key,
|
result = mProviderHelper.saveSecretKeyRing(key, progressScaler,
|
||||||
new ProgressScaler(progressable, (int) (position * progSteps),
|
canKeyRings, skipSave);
|
||||||
(int) ((position + 1) * progSteps), 100));
|
|
||||||
} else {
|
} else {
|
||||||
result = mProviderHelper.savePublicKeyRing(key,
|
result = mProviderHelper.savePublicKeyRing(key, progressScaler,
|
||||||
new ProgressScaler(progressable, (int) (position * progSteps),
|
entry.mExpectedFingerprint, canKeyRings, skipSave);
|
||||||
(int) ((position + 1) * progSteps), 100), entry.mExpectedFingerprint);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!result.success()) {
|
if (!result.success()) {
|
||||||
@@ -337,7 +340,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
}
|
}
|
||||||
importedMasterKeyIds.add(key.getMasterKeyId());
|
importedMasterKeyIds.add(key.getMasterKeyId());
|
||||||
}
|
}
|
||||||
if (entry.mBytes == null) {
|
if (!skipSave && (entry.mBytes == null)) {
|
||||||
// synonymous to isDownloadFromKeyserver.
|
// synonymous to isDownloadFromKeyserver.
|
||||||
// If no byte data was supplied, import from keyserver took place
|
// If no byte data was supplied, import from keyserver took place
|
||||||
// this prevents file imports being noted as keyserver imports
|
// this prevents file imports being noted as keyserver imports
|
||||||
@@ -360,7 +363,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
// synchronized on mProviderHelper to prevent
|
// synchronized on mProviderHelper to prevent
|
||||||
// https://github.com/open-keychain/open-keychain/issues/1221 since a consolidate deletes
|
// 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
|
// and re-inserts keys, which could conflict with a parallel db key update
|
||||||
if (secret > 0) {
|
if (!skipSave && (secret > 0)) {
|
||||||
setPreventCancel();
|
setPreventCancel();
|
||||||
ConsolidateResult result;
|
ConsolidateResult result;
|
||||||
synchronized (mProviderHelper) {
|
synchronized (mProviderHelper) {
|
||||||
@@ -418,8 +421,11 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ImportKeyResult(resultType, log, newKeys, updatedKeys, badKeys, secret,
|
ImportKeyResult result = new ImportKeyResult(resultType, log, newKeys, updatedKeys, badKeys, secret,
|
||||||
importedMasterKeyIdsArray);
|
importedMasterKeyIdsArray);
|
||||||
|
|
||||||
|
result.setCanonicalizedKeyRings(canKeyRings);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -427,19 +433,18 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) {
|
public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) {
|
||||||
ArrayList<ParcelableKeyRing> keyList = importInput.mKeyList;
|
ArrayList<ParcelableKeyRing> keyList = importInput.mKeyList;
|
||||||
ParcelableHkpKeyserver keyServer = importInput.mKeyserver;
|
ParcelableHkpKeyserver keyServer = importInput.mKeyserver;
|
||||||
|
boolean skipSave = importInput.mSkipSave;
|
||||||
|
|
||||||
ImportKeyResult result;
|
ImportKeyResult result;
|
||||||
|
|
||||||
if (keyList == null) {// import from file, do serially
|
if (keyList == null) {// import from file, do serially
|
||||||
ParcelableFileCache<ParcelableKeyRing> cache = new ParcelableFileCache<>(mContext,
|
ParcelableFileCache<ParcelableKeyRing> cache =
|
||||||
"key_import.pcl");
|
new ParcelableFileCache<>(mContext, CACHE_FILE_NAME);
|
||||||
|
result = serialKeyRingImport(cache, null, null, skipSave);
|
||||||
result = serialKeyRingImport(cache, null, null);
|
|
||||||
} else {
|
} else {
|
||||||
ParcelableProxy proxy;
|
ParcelableProxy proxy;
|
||||||
if (cryptoInput.getParcelableProxy() == null) {
|
if (cryptoInput.getParcelableProxy() == null) {
|
||||||
// explicit proxy not set
|
// explicit proxy not set
|
||||||
if(!OrbotHelper.isOrbotInRequiredState(mContext)) {
|
if (!OrbotHelper.isOrbotInRequiredState(mContext)) {
|
||||||
// show dialog to enable/install dialog
|
// show dialog to enable/install dialog
|
||||||
return new ImportKeyResult(null,
|
return new ImportKeyResult(null,
|
||||||
RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput);
|
RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput);
|
||||||
@@ -449,7 +454,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
proxy = cryptoInput.getParcelableProxy();
|
proxy = cryptoInput.getParcelableProxy();
|
||||||
}
|
}
|
||||||
|
|
||||||
result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer, proxy);
|
result = multiThreadedKeyImport(keyList, keyServer, proxy, skipSave);
|
||||||
}
|
}
|
||||||
|
|
||||||
ContactSyncAdapterService.requestContactsSync();
|
ContactSyncAdapterService.requestContactsSync();
|
||||||
@@ -457,44 +462,43 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private ImportKeyResult multiThreadedKeyImport(@NonNull Iterator<ParcelableKeyRing> keyListIterator,
|
private ImportKeyResult multiThreadedKeyImport(ArrayList<ParcelableKeyRing> keyList,
|
||||||
int totKeys, final ParcelableHkpKeyserver hkpKeyserver,
|
final ParcelableHkpKeyserver keyServer, final ParcelableProxy proxy,
|
||||||
final ParcelableProxy proxy) {
|
final boolean skipSave) {
|
||||||
Log.d(Constants.TAG, "Multi-threaded key import starting");
|
|
||||||
KeyImportAccumulator accumulator = new KeyImportAccumulator(totKeys, mProgressable);
|
|
||||||
|
|
||||||
final ProgressScaler ignoreProgressable = new ProgressScaler();
|
Log.d(Constants.TAG, "Multi-threaded key import starting");
|
||||||
|
|
||||||
|
final Iterator<ParcelableKeyRing> keyListIterator = keyList.iterator();
|
||||||
|
final int totKeys = keyList.size();
|
||||||
|
|
||||||
ExecutorService importExecutor = new ThreadPoolExecutor(0, MAX_THREADS, 30L, TimeUnit.SECONDS,
|
ExecutorService importExecutor = new ThreadPoolExecutor(0, MAX_THREADS, 30L, TimeUnit.SECONDS,
|
||||||
new LinkedBlockingQueue<Runnable>());
|
new LinkedBlockingQueue<Runnable>());
|
||||||
|
|
||||||
ExecutorCompletionService<ImportKeyResult> importCompletionService =
|
ExecutorCompletionService<ImportKeyResult> importCompletionService =
|
||||||
new ExecutorCompletionService<>(importExecutor);
|
new ExecutorCompletionService<>(importExecutor);
|
||||||
|
|
||||||
while (keyListIterator.hasNext()) { // submit all key rings to be imported
|
while (keyListIterator.hasNext()) { // submit all key rings to be imported
|
||||||
|
|
||||||
final ParcelableKeyRing pkRing = keyListIterator.next();
|
|
||||||
|
|
||||||
Callable<ImportKeyResult> importOperationCallable = new Callable<ImportKeyResult>
|
Callable<ImportKeyResult> importOperationCallable = new Callable<ImportKeyResult>
|
||||||
() {
|
() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImportKeyResult call() {
|
public ImportKeyResult call() {
|
||||||
|
|
||||||
if (checkCancelled()) {
|
if (checkCancelled()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<ParcelableKeyRing> list = new ArrayList<>();
|
ArrayList<ParcelableKeyRing> list = new ArrayList<>();
|
||||||
list.add(pkRing);
|
list.add(keyListIterator.next());
|
||||||
|
ProgressScaler ignoreProgressable = new ProgressScaler();
|
||||||
|
|
||||||
return serialKeyRingImport(list.iterator(), 1, hkpKeyserver, ignoreProgressable, proxy);
|
return serialKeyRingImport(list.iterator(), 1, keyServer, ignoreProgressable,
|
||||||
|
proxy, skipSave);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
importCompletionService.submit(importOperationCallable);
|
importCompletionService.submit(importOperationCallable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeyImportAccumulator accumulator = new KeyImportAccumulator(totKeys, mProgressable);
|
||||||
while (!accumulator.isImportFinished()) { // accumulate the results of each import
|
while (!accumulator.isImportFinished()) { // accumulate the results of each import
|
||||||
try {
|
try {
|
||||||
accumulator.accumulateKeyImport(importCompletionService.take().get());
|
accumulator.accumulateKeyImport(importCompletionService.take().get());
|
||||||
@@ -511,7 +515,6 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return accumulator.getConsolidatedResult();
|
return accumulator.getConsolidatedResult();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -519,10 +522,10 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
*/
|
*/
|
||||||
public static class KeyImportAccumulator {
|
public static class KeyImportAccumulator {
|
||||||
private OperationResult.OperationLog mImportLog = new OperationResult.OperationLog();
|
private OperationResult.OperationLog mImportLog = new OperationResult.OperationLog();
|
||||||
Progressable mProgressable;
|
private Progressable mProgressable;
|
||||||
private int mTotalKeys;
|
private int mTotalKeys;
|
||||||
private int mImportedKeys = 0;
|
private int mImportedKeys = 0;
|
||||||
ArrayList<Long> mImportedMasterKeyIds = new ArrayList<>();
|
private ArrayList<Long> mImportedMasterKeyIds = new ArrayList<>();
|
||||||
private int mBadKeys = 0;
|
private int mBadKeys = 0;
|
||||||
private int mNewKeys = 0;
|
private int mNewKeys = 0;
|
||||||
private int mUpdatedKeys = 0;
|
private int mUpdatedKeys = 0;
|
||||||
@@ -530,6 +533,8 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
private int mResultType = 0;
|
private int mResultType = 0;
|
||||||
private boolean mHasCancelledResult;
|
private boolean mHasCancelledResult;
|
||||||
|
|
||||||
|
public ArrayList<CanonicalizedKeyRing> mCanonicalizedKeyRings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accumulates keyring imports and updates the progressable whenever a new key is imported.
|
* Accumulates keyring imports and updates the progressable whenever a new key is imported.
|
||||||
* Also sets the progress to 0 on instantiation.
|
* Also sets the progress to 0 on instantiation.
|
||||||
@@ -544,6 +549,8 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
if (mProgressable != null) {
|
if (mProgressable != null) {
|
||||||
mProgressable.setProgress(0, totalKeys);
|
mProgressable.setProgress(0, totalKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mCanonicalizedKeyRings = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void accumulateKeyImport(ImportKeyResult result) {
|
public void accumulateKeyImport(ImportKeyResult result) {
|
||||||
@@ -575,6 +582,8 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
mImportedMasterKeyIds.add(masterKeyId);
|
mImportedMasterKeyIds.add(masterKeyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mCanonicalizedKeyRings.addAll(result.mCanonicalizedKeyRings);
|
||||||
|
|
||||||
// if any key import has been cancelled, set result type to cancelled
|
// if any key import has been cancelled, set result type to cancelled
|
||||||
// resultType is added to in getConsolidatedKayImport to account for remaining factors
|
// resultType is added to in getConsolidatedKayImport to account for remaining factors
|
||||||
mResultType |= result.getResult() & ImportKeyResult.RESULT_CANCELLED;
|
mResultType |= result.getResult() & ImportKeyResult.RESULT_CANCELLED;
|
||||||
@@ -614,8 +623,11 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
|||||||
masterKeyIds[i] = mImportedMasterKeyIds.get(i);
|
masterKeyIds[i] = mImportedMasterKeyIds.get(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ImportKeyResult(mResultType, mImportLog, mNewKeys, mUpdatedKeys, mBadKeys,
|
ImportKeyResult result = new ImportKeyResult(mResultType, mImportLog, mNewKeys,
|
||||||
mSecret, masterKeyIds);
|
mUpdatedKeys, mBadKeys, mSecret, masterKeyIds);
|
||||||
|
|
||||||
|
result.setCanonicalizedKeyRings(mCanonicalizedKeyRings);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isImportFinished() {
|
public boolean isImportFinished() {
|
||||||
|
|||||||
+8
-7
@@ -44,13 +44,14 @@ public class GetKeyResult extends InputPendingResult {
|
|||||||
super(log, requiredInput, cryptoInputParcel);
|
super(log, requiredInput, cryptoInputParcel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final int RESULT_ERROR_NO_VALID_KEYS = RESULT_ERROR + (1<<4);
|
public static final int RESULT_ERROR_NO_VALID_KEYS = RESULT_ERROR + (1 << 4);
|
||||||
public static final int RESULT_ERROR_NO_PGP_PARTS = RESULT_ERROR + (2<<4);
|
public static final int RESULT_ERROR_NO_PGP_PARTS = RESULT_ERROR + (2 << 4);
|
||||||
public static final int RESULT_ERROR_QUERY_TOO_SHORT = RESULT_ERROR + (3<<4);
|
public static final int RESULT_ERROR_QUERY_TOO_SHORT = RESULT_ERROR + (3 << 4);
|
||||||
public static final int RESULT_ERROR_TOO_MANY_RESPONSES = RESULT_ERROR + (4<<4);
|
public static final int RESULT_ERROR_TOO_MANY_RESPONSES = RESULT_ERROR + (4 << 4);
|
||||||
public static final int RESULT_ERROR_TOO_SHORT_OR_TOO_MANY_RESPONSES = RESULT_ERROR + (5<<4);
|
public static final int RESULT_ERROR_TOO_SHORT_OR_TOO_MANY_RESPONSES = RESULT_ERROR + (5 << 4);
|
||||||
public static final int RESULT_ERROR_QUERY_FAILED = RESULT_ERROR + (6<<4);
|
public static final int RESULT_ERROR_QUERY_FAILED = RESULT_ERROR + (6 << 4);
|
||||||
public static final int RESULT_ERROR_FILE_NOT_FOUND = RESULT_ERROR + (7<<4);
|
public static final int RESULT_ERROR_FILE_NOT_FOUND = RESULT_ERROR + (7 << 4);
|
||||||
|
public static final int RESULT_ERROR_NO_ENABLED_SOURCE = RESULT_ERROR + (8 << 4);
|
||||||
|
|
||||||
public GetKeyResult(Parcel source) {
|
public GetKeyResult(Parcel source) {
|
||||||
super(source);
|
super(source);
|
||||||
|
|||||||
+10
-2
@@ -23,6 +23,7 @@ import android.content.Intent;
|
|||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
|
||||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||||
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
|
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
|
||||||
@@ -32,11 +33,16 @@ import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
|
|||||||
import org.sufficientlysecure.keychain.ui.util.Notify.Showable;
|
import org.sufficientlysecure.keychain.ui.util.Notify.Showable;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class ImportKeyResult extends InputPendingResult {
|
public class ImportKeyResult extends InputPendingResult {
|
||||||
|
|
||||||
public final int mNewKeys, mUpdatedKeys, mBadKeys, mSecret;
|
public final int mNewKeys, mUpdatedKeys, mBadKeys, mSecret;
|
||||||
public final long[] mImportedMasterKeyIds;
|
public final long[] mImportedMasterKeyIds;
|
||||||
|
|
||||||
|
// NOT PARCELED
|
||||||
|
public ArrayList<CanonicalizedKeyRing> mCanonicalizedKeyRings;
|
||||||
|
|
||||||
// At least one new key
|
// At least one new key
|
||||||
public static final int RESULT_OK_NEWKEYS = 8;
|
public static final int RESULT_OK_NEWKEYS = 8;
|
||||||
// At least one updated key
|
// At least one updated key
|
||||||
@@ -107,6 +113,10 @@ public class ImportKeyResult extends InputPendingResult {
|
|||||||
mImportedMasterKeyIds = new long[]{};
|
mImportedMasterKeyIds = new long[]{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCanonicalizedKeyRings(ArrayList<CanonicalizedKeyRing> canonicalizedKeyRings) {
|
||||||
|
this.mCanonicalizedKeyRings = canonicalizedKeyRings;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
super.writeToParcel(dest, flags);
|
super.writeToParcel(dest, flags);
|
||||||
@@ -128,7 +138,6 @@ public class ImportKeyResult extends InputPendingResult {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public Showable createNotify(final Activity activity) {
|
public Showable createNotify(final Activity activity) {
|
||||||
|
|
||||||
int resultType = getResult();
|
int resultType = getResult();
|
||||||
|
|
||||||
String str;
|
String str;
|
||||||
@@ -204,7 +213,6 @@ public class ImportKeyResult extends InputPendingResult {
|
|||||||
activity.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
}
|
}
|
||||||
}, R.string.snackbar_details);
|
}, R.string.snackbar_details);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -818,6 +818,7 @@ public abstract class OperationResult implements Parcelable {
|
|||||||
MSG_GET_QUERY_TOO_SHORT_OR_TOO_MANY_RESPONSES (LogLevel.ERROR, R.string.msg_get_query_too_short_or_too_many_responses),
|
MSG_GET_QUERY_TOO_SHORT_OR_TOO_MANY_RESPONSES (LogLevel.ERROR, R.string.msg_get_query_too_short_or_too_many_responses),
|
||||||
MSG_GET_QUERY_FAILED (LogLevel.ERROR, R.string.msg_download_query_failed),
|
MSG_GET_QUERY_FAILED (LogLevel.ERROR, R.string.msg_download_query_failed),
|
||||||
MSG_GET_FILE_NOT_FOUND (LogLevel.ERROR, R.string.msg_get_file_not_found),
|
MSG_GET_FILE_NOT_FOUND (LogLevel.ERROR, R.string.msg_get_file_not_found),
|
||||||
|
MSG_GET_NO_ENABLED_SOURCE (LogLevel.ERROR, R.string.msg_get_no_enabled_source),
|
||||||
|
|
||||||
MSG_DEL_ERROR_EMPTY (LogLevel.ERROR, R.string.msg_del_error_empty),
|
MSG_DEL_ERROR_EMPTY (LogLevel.ERROR, R.string.msg_del_error_empty),
|
||||||
MSG_DEL_ERROR_MULTI_SECRET (LogLevel.ERROR, R.string.msg_del_error_multi_secret),
|
MSG_DEL_ERROR_MULTI_SECRET (LogLevel.ERROR, R.string.msg_del_error_multi_secret),
|
||||||
|
|||||||
+22
-13
@@ -29,18 +29,17 @@ import java.io.OutputStream;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
/** A generic wrapped PGPKeyRing object.
|
/**
|
||||||
*
|
* A generic wrapped PGPKeyRing object.
|
||||||
|
* <p>
|
||||||
* This class provides implementations for all basic getters which both
|
* This class provides implementations for all basic getters which both
|
||||||
* PublicKeyRing and SecretKeyRing have in common. To make the wrapped keyring
|
* PublicKeyRing and SecretKeyRing have in common. To make the wrapped keyring
|
||||||
* class typesafe in implementing subclasses, the field is stored in the
|
* class typesafe in implementing subclasses, the field is stored in the
|
||||||
* implementing class, providing properly typed access through the getRing
|
* implementing class, providing properly typed access through the getRing
|
||||||
* getter method.
|
* getter method.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public abstract class CanonicalizedKeyRing extends KeyRing {
|
public abstract class CanonicalizedKeyRing extends KeyRing {
|
||||||
|
|
||||||
@@ -80,16 +79,24 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
|
|||||||
|
|
||||||
public boolean isRevoked() {
|
public boolean isRevoked() {
|
||||||
// Is the master key revoked?
|
// Is the master key revoked?
|
||||||
return getRing().getPublicKey().isRevoked();
|
return getRing().getPublicKey().hasRevocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getCreationDate() {
|
||||||
|
return getPublicKey().getCreationTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getExpirationDate() {
|
||||||
|
return getPublicKey().getExpiryTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isExpired() {
|
public boolean isExpired() {
|
||||||
// Is the master key expired?
|
// Is the master key expired?
|
||||||
Date creationDate = getPublicKey().getCreationTime();
|
Date creationDate = getCreationDate();
|
||||||
Date expiryDate = getPublicKey().getExpiryTime();
|
Date expirationDate = getExpirationDate();
|
||||||
|
|
||||||
Date now = new Date();
|
Date now = new Date();
|
||||||
return creationDate.after(now) || (expiryDate != null && expiryDate.before(now));
|
return creationDate.after(now) || (expirationDate != null && expirationDate.before(now));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canCertify() throws PgpKeyNotFoundException {
|
public boolean canCertify() throws PgpKeyNotFoundException {
|
||||||
@@ -98,7 +105,7 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
|
|||||||
|
|
||||||
public Set<Long> getEncryptIds() {
|
public Set<Long> getEncryptIds() {
|
||||||
HashSet<Long> result = new HashSet<>();
|
HashSet<Long> result = new HashSet<>();
|
||||||
for(CanonicalizedPublicKey key : publicKeyIterator()) {
|
for (CanonicalizedPublicKey key : publicKeyIterator()) {
|
||||||
if (key.canEncrypt() && key.isValid()) {
|
if (key.canEncrypt() && key.isValid()) {
|
||||||
result.add(key.getKeyId());
|
result.add(key.getKeyId());
|
||||||
}
|
}
|
||||||
@@ -107,7 +114,7 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public long getEncryptId() throws PgpKeyNotFoundException {
|
public long getEncryptId() throws PgpKeyNotFoundException {
|
||||||
for(CanonicalizedPublicKey key : publicKeyIterator()) {
|
for (CanonicalizedPublicKey key : publicKeyIterator()) {
|
||||||
if (key.canEncrypt() && key.isValid()) {
|
if (key.canEncrypt() && key.isValid()) {
|
||||||
return key.getKeyId();
|
return key.getKeyId();
|
||||||
}
|
}
|
||||||
@@ -119,7 +126,7 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
|
|||||||
try {
|
try {
|
||||||
getEncryptId();
|
getEncryptId();
|
||||||
return true;
|
return true;
|
||||||
} catch(PgpKeyNotFoundException e) {
|
} catch (PgpKeyNotFoundException e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,8 +135,10 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
|
|||||||
getRing().encode(stream);
|
getRing().encode(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns an UncachedKeyRing which wraps the same data as this ring. This method should
|
/**
|
||||||
* only be used */
|
* Returns an UncachedKeyRing which wraps the same data as this ring. This method should
|
||||||
|
* only be used
|
||||||
|
*/
|
||||||
public UncachedKeyRing getUncachedKeyRing() {
|
public UncachedKeyRing getUncachedKeyRing() {
|
||||||
return new UncachedKeyRing(getRing());
|
return new UncachedKeyRing(getRing());
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -173,7 +173,7 @@ public class CachedPublicKeyRing extends KeyRing {
|
|||||||
Object data = mProviderHelper.getGenericData(mUri,
|
Object data = mProviderHelper.getGenericData(mUri,
|
||||||
KeychainContract.KeyRings.VERIFIED,
|
KeychainContract.KeyRings.VERIFIED,
|
||||||
ProviderHelper.FIELD_TYPE_INTEGER);
|
ProviderHelper.FIELD_TYPE_INTEGER);
|
||||||
return (Integer) data;
|
return ((Long) data).intValue();
|
||||||
} catch(ProviderHelper.NotFoundException e) {
|
} catch(ProviderHelper.NotFoundException e) {
|
||||||
throw new PgpKeyNotFoundException(e);
|
throw new PgpKeyNotFoundException(e);
|
||||||
}
|
}
|
||||||
|
|||||||
+64
-23
@@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
|||||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||||
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
|
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
|
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
|
||||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
|
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
|
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
|
||||||
@@ -61,9 +62,9 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys;
|
|||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||||
|
import org.sufficientlysecure.keychain.util.IteratorWithSize;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
||||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
|
|
||||||
import org.sufficientlysecure.keychain.util.Preferences;
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
import org.sufficientlysecure.keychain.util.ProgressFixedScaler;
|
import org.sufficientlysecure.keychain.util.ProgressFixedScaler;
|
||||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||||
@@ -276,7 +277,7 @@ public class ProviderHelper {
|
|||||||
|
|
||||||
public ArrayList<String> getConfirmedUserIds(long masterKeyId) throws NotFoundException {
|
public ArrayList<String> getConfirmedUserIds(long masterKeyId) throws NotFoundException {
|
||||||
Cursor cursor = mContentResolver.query(UserPackets.buildUserIdsUri(masterKeyId),
|
Cursor cursor = mContentResolver.query(UserPackets.buildUserIdsUri(masterKeyId),
|
||||||
new String[]{ UserPackets.USER_ID }, UserPackets.VERIFIED + " = " + Certs.VERIFIED_SECRET, null, null
|
new String[]{UserPackets.USER_ID}, UserPackets.VERIFIED + " = " + Certs.VERIFIED_SECRET, null, null
|
||||||
);
|
);
|
||||||
if (cursor == null) {
|
if (cursor == null) {
|
||||||
throw new NotFoundException("Key id for requested user ids not found");
|
throw new NotFoundException("Key id for requested user ids not found");
|
||||||
@@ -476,7 +477,7 @@ public class ProviderHelper {
|
|||||||
String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId);
|
String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId);
|
||||||
UserPacketItem item = new UserPacketItem();
|
UserPacketItem item = new UserPacketItem();
|
||||||
uids.add(item);
|
uids.add(item);
|
||||||
OpenPgpUtils.UserId splitUserId = KeyRing.splitUserId(userId);
|
OpenPgpUtils.UserId splitUserId = KeyRing.splitUserId(userId);
|
||||||
item.userId = userId;
|
item.userId = userId;
|
||||||
item.name = splitUserId.name;
|
item.name = splitUserId.name;
|
||||||
item.email = splitUserId.email;
|
item.email = splitUserId.email;
|
||||||
@@ -794,7 +795,7 @@ public class ProviderHelper {
|
|||||||
}
|
}
|
||||||
// if one is *trusted* but the other isn't, that one comes first
|
// if one is *trusted* but the other isn't, that one comes first
|
||||||
// this overrides the primary attribute, even!
|
// this overrides the primary attribute, even!
|
||||||
if ( (trustedCerts.size() == 0) != (o.trustedCerts.size() == 0) ) {
|
if ((trustedCerts.size() == 0) != (o.trustedCerts.size() == 0)) {
|
||||||
return trustedCerts.size() > o.trustedCerts.size() ? -1 : 1;
|
return trustedCerts.size() > o.trustedCerts.size() ? -1 : 1;
|
||||||
}
|
}
|
||||||
// if one key is primary but the other isn't, the primary one always comes first
|
// if one key is primary but the other isn't, the primary one always comes first
|
||||||
@@ -903,17 +904,16 @@ public class ProviderHelper {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) {
|
|
||||||
return savePublicKeyRing(keyRing, new ProgressScaler(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save a public keyring into the database.
|
* Save a public keyring into the database.
|
||||||
* <p/>
|
* <p/>
|
||||||
* This is a high level method, which takes care of merging all new information into the old and
|
* This is a high level method, which takes care of merging all new information into the old and
|
||||||
* keep public and secret keyrings in sync.
|
* keep public and secret keyrings in sync.
|
||||||
*/
|
*/
|
||||||
public SaveKeyringResult savePublicKeyRing(UncachedKeyRing publicRing, Progressable progress, String expectedFingerprint) {
|
public SaveKeyringResult savePublicKeyRing(UncachedKeyRing publicRing, Progressable progress,
|
||||||
|
String expectedFingerprint,
|
||||||
|
ArrayList<CanonicalizedKeyRing> canKeyRings,
|
||||||
|
boolean skipSave) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
long masterKeyId = publicRing.getMasterKeyId();
|
long masterKeyId = publicRing.getMasterKeyId();
|
||||||
@@ -926,10 +926,12 @@ public class ProviderHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CanonicalizedPublicKeyRing canPublicRing;
|
CanonicalizedPublicKeyRing canPublicRing;
|
||||||
|
boolean alreadyExists = false;
|
||||||
|
|
||||||
// If there is an old keyring, merge it
|
// If there is an old keyring, merge it
|
||||||
try {
|
try {
|
||||||
UncachedKeyRing oldPublicRing = getCanonicalizedPublicKeyRing(masterKeyId).getUncachedKeyRing();
|
UncachedKeyRing oldPublicRing = getCanonicalizedPublicKeyRing(masterKeyId).getUncachedKeyRing();
|
||||||
|
alreadyExists = true;
|
||||||
|
|
||||||
// Merge data from new public ring into the old one
|
// Merge data from new public ring into the old one
|
||||||
log(LogType.MSG_IP_MERGE_PUBLIC);
|
log(LogType.MSG_IP_MERGE_PUBLIC);
|
||||||
@@ -945,6 +947,7 @@ public class ProviderHelper {
|
|||||||
if (canPublicRing == null) {
|
if (canPublicRing == null) {
|
||||||
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
||||||
}
|
}
|
||||||
|
if (canKeyRings != null) canKeyRings.add(canPublicRing);
|
||||||
|
|
||||||
// Early breakout if nothing changed
|
// Early breakout if nothing changed
|
||||||
if (Arrays.hashCode(publicRing.getEncoded())
|
if (Arrays.hashCode(publicRing.getEncoded())
|
||||||
@@ -960,7 +963,7 @@ public class ProviderHelper {
|
|||||||
if (canPublicRing == null) {
|
if (canPublicRing == null) {
|
||||||
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
||||||
}
|
}
|
||||||
|
if (canKeyRings != null) canKeyRings.add(canPublicRing);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is a secret key, merge new data (if any) and save the key for later
|
// If there is a secret key, merge new data (if any) and save the key for later
|
||||||
@@ -997,29 +1000,51 @@ public class ProviderHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = saveCanonicalizedPublicKeyRing(canPublicRing, progress, canSecretRing != null);
|
int result;
|
||||||
|
if (!skipSave) {
|
||||||
|
result = saveCanonicalizedPublicKeyRing(canPublicRing, progress, canSecretRing != null);
|
||||||
|
} else {
|
||||||
|
result = SaveKeyringResult.SAVED_PUBLIC
|
||||||
|
| (alreadyExists ? SaveKeyringResult.UPDATED : 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Save the saved keyring (if any)
|
// Save the saved keyring (if any)
|
||||||
if (canSecretRing != null) {
|
if (canSecretRing != null) {
|
||||||
progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100);
|
progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100);
|
||||||
int secretResult = saveCanonicalizedSecretKeyRing(canSecretRing);
|
|
||||||
|
int secretResult;
|
||||||
|
if (!skipSave) {
|
||||||
|
secretResult = saveCanonicalizedSecretKeyRing(canSecretRing);
|
||||||
|
} else {
|
||||||
|
secretResult = SaveKeyringResult.SAVED_SECRET;
|
||||||
|
}
|
||||||
|
|
||||||
if ((secretResult & SaveKeyringResult.RESULT_ERROR) != SaveKeyringResult.RESULT_ERROR) {
|
if ((secretResult & SaveKeyringResult.RESULT_ERROR) != SaveKeyringResult.RESULT_ERROR) {
|
||||||
result |= SaveKeyringResult.SAVED_SECRET;
|
result |= SaveKeyringResult.SAVED_SECRET;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SaveKeyringResult(result, mLog, canSecretRing);
|
return new SaveKeyringResult(result, mLog, canSecretRing);
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log(LogType.MSG_IP_ERROR_IO_EXC);
|
log(LogType.MSG_IP_ERROR_IO_EXC);
|
||||||
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
||||||
} finally {
|
} finally {
|
||||||
mIndent -= 1;
|
mIndent -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SaveKeyringResult saveSecretKeyRing(UncachedKeyRing secretRing, Progressable progress) {
|
public SaveKeyringResult savePublicKeyRing(UncachedKeyRing publicRing, Progressable progress,
|
||||||
|
String expectedFingerprint) {
|
||||||
|
return savePublicKeyRing(publicRing, progress, expectedFingerprint, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) {
|
||||||
|
return savePublicKeyRing(keyRing, new ProgressScaler(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaveKeyringResult saveSecretKeyRing(UncachedKeyRing secretRing, Progressable progress,
|
||||||
|
ArrayList<CanonicalizedKeyRing> canKeyRings,
|
||||||
|
boolean skipSave) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
long masterKeyId = secretRing.getMasterKeyId();
|
long masterKeyId = secretRing.getMasterKeyId();
|
||||||
@@ -1032,10 +1057,12 @@ public class ProviderHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CanonicalizedSecretKeyRing canSecretRing;
|
CanonicalizedSecretKeyRing canSecretRing;
|
||||||
|
boolean alreadyExists = false;
|
||||||
|
|
||||||
// If there is an old secret key, merge it.
|
// If there is an old secret key, merge it.
|
||||||
try {
|
try {
|
||||||
UncachedKeyRing oldSecretRing = getCanonicalizedSecretKeyRing(masterKeyId).getUncachedKeyRing();
|
UncachedKeyRing oldSecretRing = getCanonicalizedSecretKeyRing(masterKeyId).getUncachedKeyRing();
|
||||||
|
alreadyExists = true;
|
||||||
|
|
||||||
// Merge data from new secret ring into old one
|
// Merge data from new secret ring into old one
|
||||||
log(LogType.MSG_IS_MERGE_SECRET);
|
log(LogType.MSG_IS_MERGE_SECRET);
|
||||||
@@ -1052,6 +1079,7 @@ public class ProviderHelper {
|
|||||||
if (canSecretRing == null) {
|
if (canSecretRing == null) {
|
||||||
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
||||||
}
|
}
|
||||||
|
if (canKeyRings != null) canKeyRings.add(canSecretRing);
|
||||||
|
|
||||||
// Early breakout if nothing changed
|
// Early breakout if nothing changed
|
||||||
if (Arrays.hashCode(secretRing.getEncoded())
|
if (Arrays.hashCode(secretRing.getEncoded())
|
||||||
@@ -1083,7 +1111,7 @@ public class ProviderHelper {
|
|||||||
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (canKeyRings != null) canKeyRings.add(canSecretRing);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge new data into public keyring as well, if there is any
|
// Merge new data into public keyring as well, if there is any
|
||||||
@@ -1109,25 +1137,38 @@ public class ProviderHelper {
|
|||||||
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
int result;
|
int publicResult;
|
||||||
|
if (!skipSave) {
|
||||||
|
publicResult = saveCanonicalizedPublicKeyRing(canPublicRing, progress, true);
|
||||||
|
} else {
|
||||||
|
publicResult = SaveKeyringResult.SAVED_PUBLIC;
|
||||||
|
}
|
||||||
|
|
||||||
result = saveCanonicalizedPublicKeyRing(canPublicRing, progress, true);
|
if ((publicResult & SaveKeyringResult.RESULT_ERROR) == SaveKeyringResult.RESULT_ERROR) {
|
||||||
if ((result & SaveKeyringResult.RESULT_ERROR) == SaveKeyringResult.RESULT_ERROR) {
|
|
||||||
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100);
|
progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100);
|
||||||
result = saveCanonicalizedSecretKeyRing(canSecretRing);
|
|
||||||
|
int result;
|
||||||
|
if (!skipSave) {
|
||||||
|
result = saveCanonicalizedSecretKeyRing(canSecretRing);
|
||||||
|
} else {
|
||||||
|
result = SaveKeyringResult.SAVED_SECRET
|
||||||
|
| (alreadyExists ? SaveKeyringResult.UPDATED : 0);
|
||||||
|
}
|
||||||
|
|
||||||
return new SaveKeyringResult(result, mLog, canSecretRing);
|
return new SaveKeyringResult(result, mLog, canSecretRing);
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log(LogType.MSG_IS_ERROR_IO_EXC);
|
log(LogType.MSG_IS_ERROR_IO_EXC);
|
||||||
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
||||||
} finally {
|
} finally {
|
||||||
mIndent -= 1;
|
mIndent -= 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaveKeyringResult saveSecretKeyRing(UncachedKeyRing secretRing, Progressable progress) {
|
||||||
|
return saveSecretKeyRing(secretRing, progress, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -1350,7 +1391,7 @@ public class ProviderHelper {
|
|||||||
|
|
||||||
ImportKeyResult result = new ImportOperation(mContext, this,
|
ImportKeyResult result = new ImportOperation(mContext, this,
|
||||||
new ProgressFixedScaler(progress, 10, 25, 100, R.string.progress_con_reimport))
|
new ProgressFixedScaler(progress, 10, 25, 100, R.string.progress_con_reimport))
|
||||||
.serialKeyRingImport(itSecrets, numSecrets, null, null);
|
.serialKeyRingImport(itSecrets, numSecrets, null, null, false);
|
||||||
log.add(result, indent);
|
log.add(result, indent);
|
||||||
} else {
|
} else {
|
||||||
log.add(LogType.MSG_CON_REIMPORT_SECRET_SKIP, indent);
|
log.add(LogType.MSG_CON_REIMPORT_SECRET_SKIP, indent);
|
||||||
@@ -1378,7 +1419,7 @@ public class ProviderHelper {
|
|||||||
|
|
||||||
ImportKeyResult result = new ImportOperation(mContext, this,
|
ImportKeyResult result = new ImportOperation(mContext, this,
|
||||||
new ProgressFixedScaler(progress, 25, 99, 100, R.string.progress_con_reimport))
|
new ProgressFixedScaler(progress, 25, 99, 100, R.string.progress_con_reimport))
|
||||||
.serialKeyRingImport(itPublics, numPublics, null, null);
|
.serialKeyRingImport(itPublics, numPublics, null, null, false);
|
||||||
log.add(result, indent);
|
log.add(result, indent);
|
||||||
// re-insert our backed up list of updated key times
|
// re-insert our backed up list of updated key times
|
||||||
// TODO: can this cause issues in case a public key re-import failed?
|
// TODO: can this cause issues in case a public key re-import failed?
|
||||||
|
|||||||
+1
-3
@@ -21,9 +21,7 @@ import android.content.Intent;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||||
import org.sufficientlysecure.keychain.remote.CryptoInputParcelCacheService;
|
|
||||||
import org.sufficientlysecure.keychain.ui.ImportKeysActivity;
|
import org.sufficientlysecure.keychain.ui.ImportKeysActivity;
|
||||||
import org.sufficientlysecure.keychain.ui.SecurityTokenOperationActivity;
|
|
||||||
|
|
||||||
public class RemoteImportKeysActivity extends ImportKeysActivity {
|
public class RemoteImportKeysActivity extends ImportKeysActivity {
|
||||||
|
|
||||||
@@ -39,7 +37,7 @@ public class RemoteImportKeysActivity extends ImportKeysActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void handleResult(ImportKeyResult result) {
|
public void handleResult(ImportKeyResult result) {
|
||||||
setResult(RESULT_OK, mPendingIntentData);
|
setResult(RESULT_OK, mPendingIntentData);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-1
@@ -27,15 +27,23 @@ import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class ImportKeyringParcel implements Parcelable {
|
public class ImportKeyringParcel implements Parcelable {
|
||||||
// if null, keys are expected to be read from a cache file in ImportExportOperations
|
// If null, keys are expected to be read from a cache file in ImportExportOperations
|
||||||
public ArrayList<ParcelableKeyRing> mKeyList;
|
public ArrayList<ParcelableKeyRing> mKeyList;
|
||||||
public ParcelableHkpKeyserver mKeyserver; // must be set if keys are to be imported from a keyserver
|
public ParcelableHkpKeyserver mKeyserver; // must be set if keys are to be imported from a keyserver
|
||||||
|
|
||||||
|
// If false, don't save the key, only return it as part of result
|
||||||
|
public boolean mSkipSave = false;
|
||||||
|
|
||||||
public ImportKeyringParcel(ArrayList<ParcelableKeyRing> keyList, ParcelableHkpKeyserver keyserver) {
|
public ImportKeyringParcel(ArrayList<ParcelableKeyRing> keyList, ParcelableHkpKeyserver keyserver) {
|
||||||
mKeyList = keyList;
|
mKeyList = keyList;
|
||||||
mKeyserver = keyserver;
|
mKeyserver = keyserver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ImportKeyringParcel(ArrayList<ParcelableKeyRing> keyList, ParcelableHkpKeyserver keyserver, boolean skipSave) {
|
||||||
|
this(keyList, keyserver);
|
||||||
|
mSkipSave = skipSave;
|
||||||
|
}
|
||||||
|
|
||||||
protected ImportKeyringParcel(Parcel in) {
|
protected ImportKeyringParcel(Parcel in) {
|
||||||
if (in.readByte() == 0x01) {
|
if (in.readByte() == 0x01) {
|
||||||
mKeyList = new ArrayList<>();
|
mKeyList = new ArrayList<>();
|
||||||
@@ -44,6 +52,7 @@ public class ImportKeyringParcel implements Parcelable {
|
|||||||
mKeyList = null;
|
mKeyList = null;
|
||||||
}
|
}
|
||||||
mKeyserver = in.readParcelable(ParcelableHkpKeyserver.class.getClassLoader());
|
mKeyserver = in.readParcelable(ParcelableHkpKeyserver.class.getClassLoader());
|
||||||
|
mSkipSave = in.readInt() != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -60,6 +69,7 @@ public class ImportKeyringParcel implements Parcelable {
|
|||||||
dest.writeList(mKeyList);
|
dest.writeList(mKeyList);
|
||||||
}
|
}
|
||||||
dest.writeParcelable(mKeyserver, flags);
|
dest.writeParcelable(mKeyserver, flags);
|
||||||
|
dest.writeInt(mSkipSave ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Parcelable.Creator<ImportKeyringParcel> CREATOR = new Parcelable.Creator<ImportKeyringParcel>() {
|
public static final Parcelable.Creator<ImportKeyringParcel> CREATOR = new Parcelable.Creator<ImportKeyringParcel>() {
|
||||||
|
|||||||
+1
-1
@@ -465,7 +465,7 @@ public class KeyserverSyncAdapterService extends Service {
|
|||||||
String hexKeyId = KeyFormattingUtils
|
String hexKeyId = KeyFormattingUtils
|
||||||
.convertKeyIdToHex(keyId);
|
.convertKeyIdToHex(keyId);
|
||||||
// we aren't updating from keybase as of now
|
// we aren't updating from keybase as of now
|
||||||
keyList.add(new ParcelableKeyRing(fingerprint, hexKeyId));
|
keyList.add(new ParcelableKeyRing(fingerprint, hexKeyId, null, null));
|
||||||
}
|
}
|
||||||
keyCursor.close();
|
keyCursor.close();
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -18,6 +18,7 @@
|
|||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -80,8 +81,8 @@ public class CreateKeyNameFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(activity);
|
super.onAttach(context);
|
||||||
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -164,8 +164,8 @@ public class CreateKeyPassphraseFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(activity);
|
super.onAttach(context);
|
||||||
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -18,6 +18,7 @@
|
|||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
@@ -130,8 +131,8 @@ public class CreateKeyStartFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(activity);
|
super.onAttach(context);
|
||||||
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -18,6 +18,7 @@
|
|||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -75,8 +76,8 @@ public class CreateSecurityTokenBlankFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(activity);
|
super.onAttach(context);
|
||||||
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-8
@@ -18,11 +18,8 @@
|
|||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
@@ -36,6 +33,7 @@ import android.widget.TextView;
|
|||||||
import org.bouncycastle.util.encoders.Hex;
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.CloudLoaderState;
|
||||||
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||||
@@ -48,6 +46,10 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
|||||||
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
|
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
|
||||||
import org.sufficientlysecure.keychain.util.Preferences;
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
|
||||||
public class CreateSecurityTokenImportResetFragment
|
public class CreateSecurityTokenImportResetFragment
|
||||||
extends QueueingCryptoOperationFragment<ImportKeyringParcel, ImportKeyResult>
|
extends QueueingCryptoOperationFragment<ImportKeyringParcel, ImportKeyResult>
|
||||||
@@ -194,8 +196,8 @@ public class CreateSecurityTokenImportResetFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(activity);
|
super.onAttach(context);
|
||||||
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,14 +213,14 @@ public class CreateSecurityTokenImportResetFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void refreshSearch() {
|
public void refreshSearch() {
|
||||||
mListFragment.loadNew(new ImportKeysListFragment.CloudLoaderState("0x" + mTokenFingerprint,
|
mListFragment.loadState(new CloudLoaderState("0x" + mTokenFingerprint,
|
||||||
Preferences.getPreferences(getActivity()).getCloudSearchPrefs()));
|
Preferences.getPreferences(getActivity()).getCloudSearchPrefs()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void importKey() {
|
public void importKey() {
|
||||||
|
|
||||||
ArrayList<ParcelableKeyRing> keyList = new ArrayList<>();
|
ArrayList<ParcelableKeyRing> keyList = new ArrayList<>();
|
||||||
keyList.add(new ParcelableKeyRing(mTokenFingerprint, null));
|
keyList.add(new ParcelableKeyRing(mTokenFingerprint, null, null, null));
|
||||||
mKeyList = keyList;
|
mKeyList = keyList;
|
||||||
|
|
||||||
mKeyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
|
mKeyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
|
||||||
|
|||||||
+2
-2
@@ -167,8 +167,8 @@ public class CreateSecurityTokenPinFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(activity);
|
super.onAttach(context);
|
||||||
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
@@ -59,6 +57,8 @@ import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
|||||||
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
|
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
|
||||||
import org.sufficientlysecure.keychain.util.Preferences;
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public abstract class DecryptFragment extends Fragment 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 int LOADER_ID_UNIFIED = 0;
|
||||||
@@ -145,7 +145,7 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
|
|||||||
|
|
||||||
{
|
{
|
||||||
ParcelableKeyRing keyEntry = new ParcelableKeyRing(null,
|
ParcelableKeyRing keyEntry = new ParcelableKeyRing(null,
|
||||||
KeyFormattingUtils.convertKeyIdToHex(unknownKeyId));
|
KeyFormattingUtils.convertKeyIdToHex(unknownKeyId), null, null);
|
||||||
ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
|
ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
|
||||||
selectedEntries.add(keyEntry);
|
selectedEntries.add(keyEntry);
|
||||||
|
|
||||||
@@ -320,7 +320,7 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
|
|||||||
mSignatureEmail.setText(userIdSplit.email);
|
mSignatureEmail.setText(userIdSplit.email);
|
||||||
} else {
|
} else {
|
||||||
mSignatureEmail.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix(
|
mSignatureEmail.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix(
|
||||||
getActivity(), mSignatureResult.getKeyId()));
|
mSignatureResult.getKeyId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Don't use revoked and expired fields from database, they don't show
|
// NOTE: Don't use revoked and expired fields from database, they don't show
|
||||||
@@ -430,7 +430,7 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
|
|||||||
mSignatureEmail.setText(userIdSplit.email);
|
mSignatureEmail.setText(userIdSplit.email);
|
||||||
} else {
|
} else {
|
||||||
mSignatureEmail.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix(
|
mSignatureEmail.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix(
|
||||||
getActivity(), mSignatureResult.getKeyId()));
|
mSignatureResult.getKeyId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (mSignatureResult.getResult()) {
|
switch (mSignatureResult.getResult()) {
|
||||||
|
|||||||
+29
-28
@@ -68,6 +68,7 @@ import android.widget.Toast;
|
|||||||
import android.widget.ViewAnimator;
|
import android.widget.ViewAnimator;
|
||||||
|
|
||||||
import com.cocosw.bottomsheet.BottomSheet;
|
import com.cocosw.bottomsheet.BottomSheet;
|
||||||
|
|
||||||
import org.openintents.openpgp.OpenPgpMetadata;
|
import org.openintents.openpgp.OpenPgpMetadata;
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
import org.sufficientlysecure.keychain.BuildConfig;
|
import org.sufficientlysecure.keychain.BuildConfig;
|
||||||
@@ -98,24 +99,24 @@ import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
|
|||||||
import org.sufficientlysecure.keychain.util.Preferences;
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
|
|
||||||
|
|
||||||
/** Displays a list of decrypted inputs.
|
/**
|
||||||
*
|
* Displays a list of decrypted inputs.
|
||||||
|
* <p/>
|
||||||
* This class has a complex control flow to manage its input URIs. Each URI
|
* This class has a complex control flow to manage its input URIs. Each URI
|
||||||
* which is in mInputUris is also in exactly one of mPendingInputUris,
|
* which is in mInputUris is also in exactly one of mPendingInputUris,
|
||||||
* mCancelledInputUris, mCurrentInputUri, or a key in mInputDataResults.
|
* mCancelledInputUris, mCurrentInputUri, or a key in mInputDataResults.
|
||||||
*
|
* <p/>
|
||||||
* Processing of URIs happens using a looping approach:
|
* Processing of URIs happens using a looping approach:
|
||||||
* - There is always exactly one method running which works on mCurrentInputUri
|
* - There is always exactly one method running which works on mCurrentInputUri
|
||||||
* - Processing starts in cryptoOperation(), which pops a new mCurrentInputUri
|
* - Processing starts in cryptoOperation(), which pops a new mCurrentInputUri
|
||||||
* from the list of mPendingInputUris.
|
* from the list of mPendingInputUris.
|
||||||
* - Once a mCurrentInputUri is finished processing, it should be set to null and
|
* - Once a mCurrentInputUri is finished processing, it should be set to null and
|
||||||
* control handed back to cryptoOperation()
|
* control handed back to cryptoOperation()
|
||||||
* - Control flow can move through asynchronous calls, and resume in callbacks
|
* - Control flow can move through asynchronous calls, and resume in callbacks
|
||||||
* like onActivityResult() or onPermissionRequestResult().
|
* like onActivityResult() or onPermissionRequestResult().
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class DecryptListFragment
|
public class DecryptListFragment
|
||||||
extends QueueingCryptoOperationFragment<InputDataParcel,InputDataResult>
|
extends QueueingCryptoOperationFragment<InputDataParcel, InputDataResult>
|
||||||
implements OnMenuItemClickListener {
|
implements OnMenuItemClickListener {
|
||||||
|
|
||||||
public static final String ARG_INPUT_URIS = "input_uris";
|
public static final String ARG_INPUT_URIS = "input_uris";
|
||||||
@@ -189,7 +190,7 @@ public class DecryptListFragment
|
|||||||
|
|
||||||
outState.putParcelableArrayList(ARG_INPUT_URIS, mInputUris);
|
outState.putParcelableArrayList(ARG_INPUT_URIS, mInputUris);
|
||||||
|
|
||||||
HashMap<Uri,InputDataResult> results = new HashMap<>(mInputUris.size());
|
HashMap<Uri, InputDataResult> results = new HashMap<>(mInputUris.size());
|
||||||
for (Uri uri : mInputUris) {
|
for (Uri uri : mInputUris) {
|
||||||
if (mPendingInputUris.contains(uri)) {
|
if (mPendingInputUris.contains(uri)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -219,7 +220,7 @@ public class DecryptListFragment
|
|||||||
|
|
||||||
ArrayList<Uri> inputUris = getArguments().getParcelableArrayList(ARG_INPUT_URIS);
|
ArrayList<Uri> inputUris = getArguments().getParcelableArrayList(ARG_INPUT_URIS);
|
||||||
ArrayList<Uri> cancelledUris = args.getParcelableArrayList(ARG_CANCELLED_URIS);
|
ArrayList<Uri> cancelledUris = args.getParcelableArrayList(ARG_CANCELLED_URIS);
|
||||||
ParcelableHashMap<Uri,InputDataResult> results = args.getParcelable(ARG_RESULTS);
|
ParcelableHashMap<Uri, InputDataResult> results = args.getParcelable(ARG_RESULTS);
|
||||||
|
|
||||||
mCanDelete = args.getBoolean(ARG_CAN_DELETE, false);
|
mCanDelete = args.getBoolean(ARG_CAN_DELETE, false);
|
||||||
|
|
||||||
@@ -231,11 +232,11 @@ public class DecryptListFragment
|
|||||||
private void displayInputUris(
|
private void displayInputUris(
|
||||||
ArrayList<Uri> inputUris,
|
ArrayList<Uri> inputUris,
|
||||||
ArrayList<Uri> cancelledUris,
|
ArrayList<Uri> cancelledUris,
|
||||||
HashMap<Uri,InputDataResult> results) {
|
HashMap<Uri, InputDataResult> results) {
|
||||||
|
|
||||||
mInputUris = inputUris;
|
mInputUris = inputUris;
|
||||||
mCurrentInputUri = null;
|
mCurrentInputUri = null;
|
||||||
mInputDataResults = results != null ? results : new HashMap<Uri,InputDataResult>(inputUris.size());
|
mInputDataResults = results != null ? results : new HashMap<Uri, InputDataResult>(inputUris.size());
|
||||||
mCancelledInputUris = cancelledUris != null ? cancelledUris : new ArrayList<Uri>();
|
mCancelledInputUris = cancelledUris != null ? cancelledUris : new ArrayList<Uri>();
|
||||||
|
|
||||||
mPendingInputUris = new ArrayList<>();
|
mPendingInputUris = new ArrayList<>();
|
||||||
@@ -295,7 +296,7 @@ public class DecryptListFragment
|
|||||||
String filename = metadata.getFilename();
|
String filename = metadata.getFilename();
|
||||||
if (TextUtils.isEmpty(filename)) {
|
if (TextUtils.isEmpty(filename)) {
|
||||||
String ext = MimeTypeMap.getSingleton().getExtensionFromMimeType(metadata.getMimeType());
|
String ext = MimeTypeMap.getSingleton().getExtensionFromMimeType(metadata.getMimeType());
|
||||||
filename = "decrypted" + (ext != null ? "."+ext : "");
|
filename = "decrypted" + (ext != null ? "." + ext : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires >=kitkat
|
// requires >=kitkat
|
||||||
@@ -395,7 +396,7 @@ public class DecryptListFragment
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HashMap<Uri,Drawable> mIconCache = new HashMap<>();
|
HashMap<Uri, Drawable> mIconCache = new HashMap<>();
|
||||||
|
|
||||||
private void processResult(final Uri uri) {
|
private void processResult(final Uri uri) {
|
||||||
|
|
||||||
@@ -562,7 +563,7 @@ public class DecryptListFragment
|
|||||||
|
|
||||||
Intent chooserIntent = Intent.createChooser(intent, getString(R.string.intent_show));
|
Intent chooserIntent = Intent.createChooser(intent, getString(R.string.intent_show));
|
||||||
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
|
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
|
||||||
new Parcelable[] { internalIntent });
|
new Parcelable[]{internalIntent});
|
||||||
|
|
||||||
startActivity(chooserIntent);
|
startActivity(chooserIntent);
|
||||||
|
|
||||||
@@ -606,7 +607,7 @@ public class DecryptListFragment
|
|||||||
.putExtra(DisplayTextActivity.EXTRA_METADATA, metadata),
|
.putExtra(DisplayTextActivity.EXTRA_METADATA, metadata),
|
||||||
BuildConfig.APPLICATION_ID, R.string.view_internal, R.mipmap.ic_launcher);
|
BuildConfig.APPLICATION_ID, R.string.view_internal, R.mipmap.ic_launcher);
|
||||||
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
|
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
|
||||||
new Parcelable[] { internalIntent });
|
new Parcelable[]{internalIntent});
|
||||||
}
|
}
|
||||||
|
|
||||||
startActivity(chooserIntent);
|
startActivity(chooserIntent);
|
||||||
@@ -633,7 +634,7 @@ public class DecryptListFragment
|
|||||||
|
|
||||||
Log.d(Constants.TAG, "mCurrentInputUri=" + mCurrentInputUri);
|
Log.d(Constants.TAG, "mCurrentInputUri=" + mCurrentInputUri);
|
||||||
|
|
||||||
if ( ! checkAndRequestReadPermission(activity, mCurrentInputUri)) {
|
if (!checkAndRequestReadPermission(activity, mCurrentInputUri)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -645,15 +646,15 @@ public class DecryptListFragment
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Request READ_EXTERNAL_STORAGE permission on Android >= 6.0 to read content from "file" Uris.
|
* Request READ_EXTERNAL_STORAGE permission on Android >= 6.0 to read content from "file" Uris.
|
||||||
*
|
* <p/>
|
||||||
* This method returns true on Android < 6, or if permission is already granted. It
|
* This method returns true on Android < 6, or if permission is already granted. It
|
||||||
* requests the permission and returns false otherwise, taking over responsibility
|
* requests the permission and returns false otherwise, taking over responsibility
|
||||||
* for mCurrentInputUri.
|
* for mCurrentInputUri.
|
||||||
*
|
* <p/>
|
||||||
* see https://commonsware.com/blog/2015/10/07/runtime-permissions-files-action-send.html
|
* see https://commonsware.com/blog/2015/10/07/runtime-permissions-files-action-send.html
|
||||||
*/
|
*/
|
||||||
private boolean checkAndRequestReadPermission(Activity activity, final Uri uri) {
|
private boolean checkAndRequestReadPermission(Activity activity, final Uri uri) {
|
||||||
if ( ! ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
|
if (!ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,7 +669,7 @@ public class DecryptListFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
requestPermissions(
|
requestPermissions(
|
||||||
new String[] { Manifest.permission.READ_EXTERNAL_STORAGE },
|
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
|
||||||
REQUEST_PERMISSION_READ_EXTERNAL_STORAGE);
|
REQUEST_PERMISSION_READ_EXTERNAL_STORAGE);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -677,8 +678,8 @@ public class DecryptListFragment
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode,
|
public void onRequestPermissionsResult(int requestCode,
|
||||||
@NonNull String[] permissions,
|
@NonNull String[] permissions,
|
||||||
@NonNull int[] grantResults) {
|
@NonNull int[] grantResults) {
|
||||||
|
|
||||||
if (requestCode != REQUEST_PERMISSION_READ_EXTERNAL_STORAGE) {
|
if (requestCode != REQUEST_PERMISSION_READ_EXTERNAL_STORAGE) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
@@ -694,7 +695,7 @@ public class DecryptListFragment
|
|||||||
Iterator<Uri> it = mCancelledInputUris.iterator();
|
Iterator<Uri> it = mCancelledInputUris.iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
Uri uri = it.next();
|
Uri uri = it.next();
|
||||||
if ( ! ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
|
if (!ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
it.remove();
|
it.remove();
|
||||||
@@ -712,7 +713,7 @@ public class DecryptListFragment
|
|||||||
Iterator<Uri> it = mPendingInputUris.iterator();
|
Iterator<Uri> it = mPendingInputUris.iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
Uri uri = it.next();
|
Uri uri = it.next();
|
||||||
if ( ! ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
|
if (!ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
it.remove();
|
it.remove();
|
||||||
@@ -767,7 +768,7 @@ public class DecryptListFragment
|
|||||||
|
|
||||||
{
|
{
|
||||||
ParcelableKeyRing keyEntry = new ParcelableKeyRing(null,
|
ParcelableKeyRing keyEntry = new ParcelableKeyRing(null,
|
||||||
KeyFormattingUtils.convertKeyIdToHex(unknownKeyId));
|
KeyFormattingUtils.convertKeyIdToHex(unknownKeyId), null, null);
|
||||||
ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
|
ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
|
||||||
selectedEntries.add(keyEntry);
|
selectedEntries.add(keyEntry);
|
||||||
|
|
||||||
@@ -975,7 +976,7 @@ public class DecryptListFragment
|
|||||||
String filename;
|
String filename;
|
||||||
if (metadata == null) {
|
if (metadata == null) {
|
||||||
filename = getString(R.string.filename_unknown);
|
filename = getString(R.string.filename_unknown);
|
||||||
} else if ( ! TextUtils.isEmpty(metadata.getFilename())) {
|
} else if (!TextUtils.isEmpty(metadata.getFilename())) {
|
||||||
filename = metadata.getFilename();
|
filename = metadata.getFilename();
|
||||||
} else if (ClipDescription.compareMimeTypes(metadata.getMimeType(), Constants.MIME_TYPE_KEYS)) {
|
} else if (ClipDescription.compareMimeTypes(metadata.getMimeType(), Constants.MIME_TYPE_KEYS)) {
|
||||||
filename = getString(R.string.filename_keys);
|
filename = getString(R.string.filename_keys);
|
||||||
@@ -1227,7 +1228,7 @@ public class DecryptListFragment
|
|||||||
vSigStatusText = (TextView) itemView.findViewById(R.id.result_signature_text);
|
vSigStatusText = (TextView) itemView.findViewById(R.id.result_signature_text);
|
||||||
vSignatureLayout = itemView.findViewById(R.id.result_signature_layout);
|
vSignatureLayout = itemView.findViewById(R.id.result_signature_layout);
|
||||||
vSignatureName = (TextView) itemView.findViewById(R.id.result_signature_name);
|
vSignatureName = (TextView) itemView.findViewById(R.id.result_signature_name);
|
||||||
vSignatureMail= (TextView) itemView.findViewById(R.id.result_signature_email);
|
vSignatureMail = (TextView) itemView.findViewById(R.id.result_signature_email);
|
||||||
vSignatureAction = (ViewAnimator) itemView.findViewById(R.id.result_signature_action);
|
vSignatureAction = (ViewAnimator) itemView.findViewById(R.id.result_signature_action);
|
||||||
|
|
||||||
vFileList = (LinearLayout) itemView.findViewById(R.id.file_list);
|
vFileList = (LinearLayout) itemView.findViewById(R.id.file_list);
|
||||||
|
|||||||
+5
-4
@@ -125,10 +125,11 @@ public class EncryptFilesFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(activity);
|
super.onAttach(context);
|
||||||
if ( ! (activity instanceof EncryptActivity) ) {
|
|
||||||
throw new AssertionError(activity + " must inherit from EncryptionActivity");
|
if ( ! (context instanceof EncryptActivity) ) {
|
||||||
|
throw new AssertionError(context + " must inherit from EncryptionActivity");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-4
@@ -82,10 +82,11 @@ public class EncryptTextFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(activity);
|
super.onAttach(context);
|
||||||
if ( ! (activity instanceof EncryptActivity) ) {
|
|
||||||
throw new AssertionError(activity + " must inherit from EncryptionActivity");
|
if ( ! (context instanceof EncryptActivity) ) {
|
||||||
|
throw new AssertionError(context + " must inherit from EncryptionActivity");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -28,7 +28,7 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.markdown4j.Markdown4jProcessor;
|
import org.markdown4j.Markdown4jProcessor;
|
||||||
import org.sufficientlysecure.htmltextview.HtmlLocalImageGetter;
|
import org.sufficientlysecure.htmltextview.HtmlResImageGetter;
|
||||||
import org.sufficientlysecure.htmltextview.HtmlTextView;
|
import org.sufficientlysecure.htmltextview.HtmlTextView;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
@@ -52,7 +52,7 @@ public class HelpAboutFragment extends Fragment {
|
|||||||
try {
|
try {
|
||||||
String html = new Markdown4jProcessor().process(
|
String html = new Markdown4jProcessor().process(
|
||||||
getActivity().getResources().openRawResource(R.raw.help_about));
|
getActivity().getResources().openRawResource(R.raw.help_about));
|
||||||
aboutTextView.setHtml(html, new HtmlLocalImageGetter(aboutTextView));
|
aboutTextView.setHtml(html, new HtmlResImageGetter(aboutTextView));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(Constants.TAG, "IOException", e);
|
Log.e(Constants.TAG, "IOException", e);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -26,7 +26,7 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
|
|
||||||
import org.markdown4j.Markdown4jProcessor;
|
import org.markdown4j.Markdown4jProcessor;
|
||||||
import org.sufficientlysecure.htmltextview.HtmlLocalImageGetter;
|
import org.sufficientlysecure.htmltextview.HtmlResImageGetter;
|
||||||
import org.sufficientlysecure.htmltextview.HtmlTextView;
|
import org.sufficientlysecure.htmltextview.HtmlTextView;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
@@ -69,7 +69,7 @@ public class HelpMarkdownFragment extends Fragment {
|
|||||||
try {
|
try {
|
||||||
String html = new Markdown4jProcessor().process(
|
String html = new Markdown4jProcessor().process(
|
||||||
getActivity().getResources().openRawResource(mHtmlFile));
|
getActivity().getResources().openRawResource(mHtmlFile));
|
||||||
text.setHtml(html, new HtmlLocalImageGetter(text));
|
text.setHtml(html, new HtmlResImageGetter(text));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(Constants.TAG, "IOException", e);
|
Log.e(Constants.TAG, "IOException", e);
|
||||||
}
|
}
|
||||||
|
|||||||
+111
-181
@@ -1,18 +1,18 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2012-2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
* Copyright (C) 2011 Senecaso
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* you may not use this file except in compliance with the License.
|
* it under the terms of the GNU General Public License as published by
|
||||||
* You may obtain a copy of the License at
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* You should have received a copy of the GNU General Public License
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* 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;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
@@ -24,8 +24,6 @@ import android.os.Bundle;
|
|||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
@@ -34,6 +32,10 @@ import org.sufficientlysecure.keychain.intents.OpenKeychainIntents;
|
|||||||
import org.sufficientlysecure.keychain.keyimport.FacebookKeyserver;
|
import org.sufficientlysecure.keychain.keyimport.FacebookKeyserver;
|
||||||
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
||||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.ImportKeysListener;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.ImportKeysOperationCallback;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.LoaderState;
|
||||||
|
import org.sufficientlysecure.keychain.operations.ImportOperation;
|
||||||
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||||
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
|
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
|
||||||
@@ -42,15 +44,14 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
|||||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
||||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
|
|
||||||
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
|
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
|
||||||
import org.sufficientlysecure.keychain.util.Preferences;
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class ImportKeysActivity extends BaseActivity
|
public class ImportKeysActivity extends BaseActivity implements ImportKeysListener {
|
||||||
implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
|
||||||
|
|
||||||
public static final String ACTION_IMPORT_KEY = OpenKeychainIntents.IMPORT_KEY;
|
public static final String ACTION_IMPORT_KEY = OpenKeychainIntents.IMPORT_KEY;
|
||||||
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER = OpenKeychainIntents.IMPORT_KEY_FROM_KEYSERVER;
|
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER = OpenKeychainIntents.IMPORT_KEY_FROM_KEYSERVER;
|
||||||
@@ -79,13 +80,8 @@ public class ImportKeysActivity extends BaseActivity
|
|||||||
public static final String TAG_FRAG_LIST = "frag_list";
|
public static final String TAG_FRAG_LIST = "frag_list";
|
||||||
public static final String TAG_FRAG_TOP = "frag_top";
|
public static final String TAG_FRAG_TOP = "frag_top";
|
||||||
|
|
||||||
// for CryptoOperationHelper.Callback
|
|
||||||
private ParcelableHkpKeyserver mKeyserver;
|
|
||||||
private ArrayList<ParcelableKeyRing> mKeyList;
|
|
||||||
|
|
||||||
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
|
|
||||||
|
|
||||||
private boolean mFreshIntent;
|
private boolean mFreshIntent;
|
||||||
|
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOpHelper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -95,12 +91,6 @@ public class ImportKeysActivity extends BaseActivity
|
|||||||
mFreshIntent = true;
|
mFreshIntent = true;
|
||||||
|
|
||||||
setFullScreenDialogClose(Activity.RESULT_CANCELED, true);
|
setFullScreenDialogClose(Activity.RESULT_CANCELED, true);
|
||||||
findViewById(R.id.import_import).setOnClickListener(new OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
importSelectedKeys();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -182,7 +172,7 @@ public class ImportKeysActivity extends BaseActivity
|
|||||||
|
|
||||||
if (query != null && query.length() > 0) {
|
if (query != null && query.length() > 0) {
|
||||||
// display keyserver fragment with query
|
// display keyserver fragment with query
|
||||||
startTopCloudFragment(query, false, null);
|
startTopCloudFragment(query, null);
|
||||||
|
|
||||||
// action: search immediately
|
// action: search immediately
|
||||||
startListFragment(null, null, query, null);
|
startListFragment(null, null, query, null);
|
||||||
@@ -200,9 +190,6 @@ public class ImportKeysActivity extends BaseActivity
|
|||||||
if (isFingerprintValid(fingerprint)) {
|
if (isFingerprintValid(fingerprint)) {
|
||||||
String query = "0x" + fingerprint;
|
String query = "0x" + fingerprint;
|
||||||
|
|
||||||
// display keyserver fragment with query
|
|
||||||
startTopCloudFragment(query, true, null);
|
|
||||||
|
|
||||||
// action: search immediately
|
// action: search immediately
|
||||||
startListFragment(null, null, query, null);
|
startListFragment(null, null, query, null);
|
||||||
}
|
}
|
||||||
@@ -220,27 +207,35 @@ public class ImportKeysActivity extends BaseActivity
|
|||||||
|
|
||||||
Preferences.CloudSearchPrefs cloudSearchPrefs =
|
Preferences.CloudSearchPrefs cloudSearchPrefs =
|
||||||
new Preferences.CloudSearchPrefs(false, true, true, null);
|
new Preferences.CloudSearchPrefs(false, true, true, null);
|
||||||
// we allow our users to edit the query if they wish
|
|
||||||
startTopCloudFragment(fbUsername, false, cloudSearchPrefs);
|
|
||||||
// search immediately
|
// search immediately
|
||||||
startListFragment(null, null, fbUsername, cloudSearchPrefs);
|
startListFragment(null, null, fbUsername, cloudSearchPrefs);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ACTION_SEARCH_KEYSERVER_FROM_URL: {
|
case ACTION_SEARCH_KEYSERVER_FROM_URL: {
|
||||||
// need to process URL to get search query and keyserver authority
|
// get keyserver from URL
|
||||||
String query = dataUri.getQueryParameter("search");
|
ParcelableHkpKeyserver keyserver = new ParcelableHkpKeyserver(
|
||||||
// if query not specified, we still allow users to search the keyserver in the link
|
dataUri.getScheme() + "://" + dataUri.getAuthority());
|
||||||
if (query == null) {
|
|
||||||
Notify.create(this, R.string.import_url_warn_no_search_parameter, Notify.LENGTH_INDEFINITE,
|
|
||||||
Notify.Style.WARN).show();
|
|
||||||
}
|
|
||||||
ParcelableHkpKeyserver keyserver = new ParcelableHkpKeyserver(dataUri.getAuthority());
|
|
||||||
Preferences.CloudSearchPrefs cloudSearchPrefs = new Preferences.CloudSearchPrefs(
|
Preferences.CloudSearchPrefs cloudSearchPrefs = new Preferences.CloudSearchPrefs(
|
||||||
true, true, true, keyserver);
|
true, false, false, keyserver);
|
||||||
// we allow our users to edit the query if they wish
|
Log.d(Constants.TAG, "Using keyserver: " + keyserver);
|
||||||
startTopCloudFragment(query, false, cloudSearchPrefs);
|
|
||||||
// search immediately (if query is not null)
|
// process URL to get operation and query
|
||||||
startListFragment(null, null, query, cloudSearchPrefs);
|
String operation = dataUri.getQueryParameter("op");
|
||||||
|
String query = dataUri.getQueryParameter("search");
|
||||||
|
|
||||||
|
// if query or operation not specified, we still allow users to search
|
||||||
|
if (query == null || operation == null) {
|
||||||
|
startTopCloudFragment(null, cloudSearchPrefs);
|
||||||
|
startListFragment(null, null, null, cloudSearchPrefs);
|
||||||
|
} else {
|
||||||
|
if (operation.equalsIgnoreCase("get")) {
|
||||||
|
// don't allow searching here, only one key!
|
||||||
|
startListFragment(null, null, query, cloudSearchPrefs);
|
||||||
|
} else { // for example: operation: index
|
||||||
|
startTopCloudFragment(query, cloudSearchPrefs);
|
||||||
|
startListFragment(null, null, query, cloudSearchPrefs);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ACTION_IMPORT_KEY_FROM_FILE:
|
case ACTION_IMPORT_KEY_FROM_FILE:
|
||||||
@@ -251,24 +246,13 @@ public class ImportKeysActivity extends BaseActivity
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
startTopCloudFragment(null, false, null);
|
startTopCloudFragment(null, null);
|
||||||
startListFragment(null, null, null, null);
|
startListFragment(null, null, null, null);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
|
||||||
super.onRestoreInstanceState(savedInstanceState);
|
|
||||||
|
|
||||||
// the only thing we need to take care of for restoring state is
|
|
||||||
// that the top layout is shown iff it contains a fragment
|
|
||||||
Fragment topFragment = getSupportFragmentManager().findFragmentByTag(TAG_FRAG_TOP);
|
|
||||||
boolean hasTopFragment = topFragment != null;
|
|
||||||
findViewById(R.id.import_keys_top_layout).setVisibility(hasTopFragment ? View.VISIBLE : View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the list of keys to be imported.
|
* Shows the list of keys to be imported.
|
||||||
* If the fragment is started with non-null bytes/dataUri/serverQuery, it will immediately
|
* If the fragment is started with non-null bytes/dataUri/serverQuery, it will immediately
|
||||||
@@ -282,6 +266,7 @@ public class ImportKeysActivity extends BaseActivity
|
|||||||
*/
|
*/
|
||||||
private void startListFragment(byte[] bytes, Uri dataUri, String serverQuery,
|
private void startListFragment(byte[] bytes, Uri dataUri, String serverQuery,
|
||||||
Preferences.CloudSearchPrefs cloudSearchPrefs) {
|
Preferences.CloudSearchPrefs cloudSearchPrefs) {
|
||||||
|
|
||||||
Fragment listFragment =
|
Fragment listFragment =
|
||||||
ImportKeysListFragment.newInstance(bytes, dataUri, serverQuery, false,
|
ImportKeysListFragment.newInstance(bytes, dataUri, serverQuery, false,
|
||||||
cloudSearchPrefs);
|
cloudSearchPrefs);
|
||||||
@@ -291,30 +276,29 @@ public class ImportKeysActivity extends BaseActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void startTopFileFragment() {
|
private void startTopFileFragment() {
|
||||||
findViewById(R.id.import_keys_top_layout).setVisibility(View.VISIBLE);
|
FragmentManager fM = getSupportFragmentManager();
|
||||||
Fragment importFileFragment = ImportKeysFileFragment.newInstance();
|
if (fM.findFragmentByTag(TAG_FRAG_TOP) == null) {
|
||||||
getSupportFragmentManager().beginTransaction()
|
Fragment importFileFragment = ImportKeysFileFragment.newInstance();
|
||||||
.replace(R.id.import_keys_top_container, importFileFragment, TAG_FRAG_TOP)
|
fM.beginTransaction().add(importFileFragment, TAG_FRAG_TOP).commit();
|
||||||
.commit();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* loads the CloudFragment, which consists of the search bar, search button and settings icon
|
* loads the CloudFragment, which enables the search bar
|
||||||
* visually.
|
|
||||||
*
|
*
|
||||||
* @param query search query
|
* @param query search query
|
||||||
* @param disableQueryEdit if true, user will not be able to edit the search query
|
|
||||||
* @param cloudSearchPrefs keyserver authority to use for search, if null will use keyserver
|
* @param cloudSearchPrefs keyserver authority to use for search, if null will use keyserver
|
||||||
* specified in user preferences
|
* specified in user preferences
|
||||||
*/
|
*/
|
||||||
private void startTopCloudFragment(String query, boolean disableQueryEdit,
|
private void startTopCloudFragment(String query,
|
||||||
Preferences.CloudSearchPrefs cloudSearchPrefs) {
|
Preferences.CloudSearchPrefs cloudSearchPrefs) {
|
||||||
findViewById(R.id.import_keys_top_layout).setVisibility(View.VISIBLE);
|
|
||||||
Fragment importCloudFragment = ImportKeysCloudFragment.newInstance(query, disableQueryEdit,
|
FragmentManager fM = getSupportFragmentManager();
|
||||||
cloudSearchPrefs);
|
if (fM.findFragmentByTag(TAG_FRAG_TOP) == null) {
|
||||||
getSupportFragmentManager().beginTransaction()
|
Fragment importCloudFragment = ImportKeysSearchFragment.newInstance(query,
|
||||||
.replace(R.id.import_keys_top_container, importCloudFragment, TAG_FRAG_TOP)
|
cloudSearchPrefs);
|
||||||
.commit();
|
fM.beginTransaction().add(importCloudFragment, TAG_FRAG_TOP).commit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFingerprintValid(String fingerprint) {
|
private boolean isFingerprintValid(String fingerprint) {
|
||||||
@@ -327,96 +311,68 @@ public class ImportKeysActivity extends BaseActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadCallback(final ImportKeysListFragment.LoaderState loaderState) {
|
|
||||||
FragmentManager fragMan = getSupportFragmentManager();
|
|
||||||
ImportKeysListFragment keyListFragment = (ImportKeysListFragment) fragMan.findFragmentByTag(TAG_FRAG_LIST);
|
|
||||||
keyListFragment.loadNew(loaderState);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void importSelectedKeys() {
|
|
||||||
|
|
||||||
FragmentManager fragMan = getSupportFragmentManager();
|
|
||||||
ImportKeysListFragment keyListFragment = (ImportKeysListFragment) fragMan.findFragmentByTag(TAG_FRAG_LIST);
|
|
||||||
|
|
||||||
if (keyListFragment.getSelectedEntries().size() == 0) {
|
|
||||||
Notify.create(this, R.string.error_nothing_import_selected, Notify.Style.ERROR)
|
|
||||||
.show((ViewGroup) findViewById(R.id.import_snackbar));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mOperationHelper = new CryptoOperationHelper<>(
|
|
||||||
1, this, this, R.string.progress_importing
|
|
||||||
);
|
|
||||||
|
|
||||||
ImportKeysListFragment.LoaderState ls = keyListFragment.getLoaderState();
|
|
||||||
if (ls instanceof ImportKeysListFragment.BytesLoaderState) {
|
|
||||||
Log.d(Constants.TAG, "importKeys started");
|
|
||||||
|
|
||||||
// get DATA from selected key entries
|
|
||||||
IteratorWithSize<ParcelableKeyRing> selectedEntries = keyListFragment.getSelectedData();
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
try {
|
|
||||||
// We parcel this iteratively into a file - anything we can
|
|
||||||
// display here, we should be able to import.
|
|
||||||
ParcelableFileCache<ParcelableKeyRing> cache =
|
|
||||||
new ParcelableFileCache<>(this, "key_import.pcl");
|
|
||||||
cache.writeCache(selectedEntries);
|
|
||||||
|
|
||||||
mKeyList = null;
|
|
||||||
mKeyserver = null;
|
|
||||||
mOperationHelper.cryptoOperation();
|
|
||||||
|
|
||||||
} 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;
|
|
||||||
|
|
||||||
// get selected key entries
|
|
||||||
ArrayList<ParcelableKeyRing> keys = new ArrayList<>();
|
|
||||||
{
|
|
||||||
// change the format into ParcelableKeyRing
|
|
||||||
ArrayList<ImportKeysListEntry> entries = keyListFragment.getSelectedEntries();
|
|
||||||
for (ImportKeysListEntry entry : entries) {
|
|
||||||
keys.add(new ParcelableKeyRing(entry.getFingerprintHex(),
|
|
||||||
entry.getKeyIdHex(), entry.getKeybaseName(), entry.getFbUsername()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mKeyList = keys;
|
|
||||||
mKeyserver = sls.mCloudPrefs.keyserver;
|
|
||||||
mOperationHelper.cryptoOperation();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
if (mOperationHelper != null &&
|
if (mOpHelper != null &&
|
||||||
mOperationHelper.handleActivityResult(requestCode, resultCode, data)) {
|
mOpHelper.handleActivityResult(requestCode, resultCode, data)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Defines how the result of this activity is returned.
|
public void onBackPressed() {
|
||||||
* Is overwritten in RemoteImportKeysActivity
|
FragmentManager fM = getSupportFragmentManager();
|
||||||
*/
|
ImportKeysListFragment listFragment =
|
||||||
protected void handleResult(ImportKeyResult result) {
|
(ImportKeysListFragment) fM.findFragmentByTag(TAG_FRAG_LIST);
|
||||||
|
|
||||||
|
if ((listFragment == null) || listFragment.onBackPressed()) {
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadKeys(LoaderState loaderState) {
|
||||||
|
FragmentManager fM = getSupportFragmentManager();
|
||||||
|
((ImportKeysListFragment) fM.findFragmentByTag(TAG_FRAG_LIST)).loadState(loaderState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void importKeys(List<ImportKeysListEntry> entries) {
|
||||||
|
List<ParcelableKeyRing> keyRings = new ArrayList<>();
|
||||||
|
for (ImportKeysListEntry e : entries) {
|
||||||
|
keyRings.add(e.getParcelableKeyRing());
|
||||||
|
}
|
||||||
|
// 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.
|
||||||
|
try {
|
||||||
|
// We parcel this iteratively into a file - anything we can
|
||||||
|
// display here, we should be able to import.
|
||||||
|
ParcelableFileCache<ParcelableKeyRing> cache =
|
||||||
|
new ParcelableFileCache<>(this, ImportOperation.CACHE_FILE_NAME);
|
||||||
|
cache.writeCache(entries.size(), keyRings.iterator());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(Constants.TAG, "Problem writing cache file", e);
|
||||||
|
Notify.create(this, "Problem writing cache file!", Notify.Style.ERROR).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportKeyringParcel inputParcel = new ImportKeyringParcel(null, null);
|
||||||
|
ImportKeysOperationCallback callback = new ImportKeysOperationCallback(this, inputParcel);
|
||||||
|
mOpHelper = new CryptoOperationHelper<>(1, this, callback, R.string.progress_importing);
|
||||||
|
mOpHelper.cryptoOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleResult(ImportKeyResult result) {
|
||||||
String intentAction = getIntent().getAction();
|
String intentAction = getIntent().getAction();
|
||||||
|
|
||||||
if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(intentAction)
|
if (ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(intentAction)
|
||||||
|| ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(intentAction)) {
|
|| ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(intentAction)) {
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.putExtra(ImportKeyResult.EXTRA_RESULT, result);
|
intent.putExtra(ImportKeyResult.EXTRA_RESULT, result);
|
||||||
setResult(RESULT_OK, intent);
|
setResult(Activity.RESULT_OK, intent);
|
||||||
finish();
|
finish();
|
||||||
} else if (result.isOkNew() || result.isOkUpdated()) {
|
} else if (result.isOkNew() || result.isOkUpdated()) {
|
||||||
// User has successfully imported a key, hide first time dialog
|
// User has successfully imported a key, hide first time dialog
|
||||||
@@ -424,38 +380,12 @@ public class ImportKeysActivity extends BaseActivity
|
|||||||
|
|
||||||
// Close activities opened for importing keys and go to the list of keys
|
// Close activities opened for importing keys and go to the list of keys
|
||||||
Intent intent = new Intent(this, MainActivity.class);
|
Intent intent = new Intent(this, MainActivity.class);
|
||||||
|
intent.putExtra(ImportKeyResult.EXTRA_RESULT, result);
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
} else {
|
} else {
|
||||||
result.createNotify(ImportKeysActivity.this)
|
result.createNotify(this).show();
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
-198
@@ -1,198 +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;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.preference.PreferenceActivity;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.inputmethod.EditorInfo;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.AutoCompleteTextView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.util.ContactHelper;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
import org.sufficientlysecure.keychain.util.Preferences;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Consists of the search bar, search button, and search settings button
|
|
||||||
*/
|
|
||||||
public class ImportKeysCloudFragment extends Fragment {
|
|
||||||
public static final String ARG_QUERY = "query";
|
|
||||||
public static final String ARG_DISABLE_QUERY_EDIT = "disable_query_edit";
|
|
||||||
public static final String ARG_CLOUD_SEARCH_PREFS = "cloud_search_prefs";
|
|
||||||
|
|
||||||
private ImportKeysActivity mImportActivity;
|
|
||||||
|
|
||||||
private AutoCompleteTextView mQueryEditText;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates new instance of this fragment
|
|
||||||
*
|
|
||||||
* @param query query to search for
|
|
||||||
* @param disableQueryEdit if true, user cannot edit query
|
|
||||||
* @param cloudSearchPrefs search parameters to use. If null will retrieve from user's
|
|
||||||
* preferences.
|
|
||||||
*/
|
|
||||||
public static ImportKeysCloudFragment newInstance(String query, boolean disableQueryEdit,
|
|
||||||
Preferences.CloudSearchPrefs
|
|
||||||
cloudSearchPrefs) {
|
|
||||||
ImportKeysCloudFragment frag = new ImportKeysCloudFragment();
|
|
||||||
|
|
||||||
Bundle args = new Bundle();
|
|
||||||
args.putString(ARG_QUERY, query);
|
|
||||||
args.putBoolean(ARG_DISABLE_QUERY_EDIT, disableQueryEdit);
|
|
||||||
args.putParcelable(ARG_CLOUD_SEARCH_PREFS, cloudSearchPrefs);
|
|
||||||
|
|
||||||
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.import_keys_cloud_fragment, container, false);
|
|
||||||
|
|
||||||
mQueryEditText = (AutoCompleteTextView) view.findViewById(R.id.cloud_import_server_query);
|
|
||||||
|
|
||||||
ContactHelper contactHelper = new ContactHelper(getActivity());
|
|
||||||
List<String> namesAndEmails = contactHelper.getContactNames();
|
|
||||||
namesAndEmails.addAll(contactHelper.getContactMails());
|
|
||||||
mQueryEditText.setThreshold(3);
|
|
||||||
mQueryEditText.setAdapter(
|
|
||||||
new ArrayAdapter<>
|
|
||||||
(getActivity(), android.R.layout.simple_spinner_dropdown_item,
|
|
||||||
namesAndEmails
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
View searchButton = view.findViewById(R.id.cloud_import_server_search);
|
|
||||||
searchButton.setOnClickListener(new OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
search(mQueryEditText.getText().toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mQueryEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
|
||||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
|
||||||
search(mQueryEditText.getText().toString());
|
|
||||||
|
|
||||||
// Don't return true to let the keyboard close itself after pressing search
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
View configButton = view.findViewById(R.id.cloud_import_server_config_button);
|
|
||||||
configButton.setOnClickListener(new OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
Intent intent = new Intent(mImportActivity, SettingsActivity.class);
|
|
||||||
intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, SettingsActivity.CloudSearchPrefsFragment.class.getName());
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
|
||||||
super.onActivityCreated(savedInstanceState);
|
|
||||||
|
|
||||||
// set displayed values
|
|
||||||
if (getArguments() != null) {
|
|
||||||
String query = getArguments().getString(ARG_QUERY);
|
|
||||||
if (query != null) {
|
|
||||||
mQueryEditText.setText(query, TextView.BufferType.EDITABLE);
|
|
||||||
|
|
||||||
Log.d(Constants.TAG, "query: " + query);
|
|
||||||
} else {
|
|
||||||
// open keyboard
|
|
||||||
mQueryEditText.requestFocus();
|
|
||||||
toggleKeyboard(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getArguments().getBoolean(ARG_DISABLE_QUERY_EDIT, false)) {
|
|
||||||
mQueryEditText.setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(Activity activity) {
|
|
||||||
super.onAttach(activity);
|
|
||||||
|
|
||||||
mImportActivity = (ImportKeysActivity) activity;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void search(String query) {
|
|
||||||
Preferences.CloudSearchPrefs cloudSearchPrefs
|
|
||||||
= getArguments().getParcelable(ARG_CLOUD_SEARCH_PREFS);
|
|
||||||
|
|
||||||
// no explicit search preferences passed
|
|
||||||
if (cloudSearchPrefs == null) {
|
|
||||||
cloudSearchPrefs = Preferences.getPreferences(getActivity()).getCloudSearchPrefs();
|
|
||||||
}
|
|
||||||
|
|
||||||
mImportActivity.loadCallback(
|
|
||||||
new ImportKeysListFragment.CloudLoaderState(query, cloudSearchPrefs));
|
|
||||||
toggleKeyboard(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toggleKeyboard(boolean show) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (show) {
|
|
||||||
inputManager.showSoftInput(v, InputMethodManager.SHOW_IMPLICIT);
|
|
||||||
} else {
|
|
||||||
inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+55
-92
@@ -17,46 +17,42 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ContentResolver;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.content.ContextCompat;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.BytesLoaderState;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.ImportKeysListener;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||||
import org.sufficientlysecure.keychain.ui.ImportKeysListFragment.BytesLoaderState;
|
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.PermissionsUtil;
|
||||||
import org.sufficientlysecure.keychain.util.FileHelper;
|
import org.sufficientlysecure.keychain.util.FileHelper;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
public class ImportKeysFileFragment extends Fragment {
|
public class ImportKeysFileFragment extends Fragment {
|
||||||
private ImportKeysActivity mImportActivity;
|
|
||||||
private View mBrowse;
|
private Activity mActivity;
|
||||||
private View mClipboardButton;
|
private ImportKeysListener mCallback;
|
||||||
|
|
||||||
private Uri mCurrentUri;
|
private Uri mCurrentUri;
|
||||||
|
|
||||||
private static final int REQUEST_CODE_FILE = 0x00007003;
|
private static final int REQUEST_CODE_FILE = 0x00007003;
|
||||||
private static final int REQUEST_PERMISSION_READ_EXTERNAL_STORAGE = 12;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new instance of this fragment
|
* Creates new instance of this fragment
|
||||||
@@ -70,52 +66,60 @@ public class ImportKeysFileFragment extends Fragment {
|
|||||||
return frag;
|
return frag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Inflate the layout for this fragment
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater i, ViewGroup c, Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.import_keys_file_fragment, container, false);
|
setHasOptionsMenu(true);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
mBrowse = view.findViewById(R.id.import_keys_file_browse);
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
|
||||||
mBrowse.setOnClickListener(new View.OnClickListener() {
|
try {
|
||||||
public void onClick(View v) {
|
mCallback = (ImportKeysListener) context;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new ClassCastException(context.toString()
|
||||||
|
+ " must implement ImportKeysListener");
|
||||||
|
}
|
||||||
|
|
||||||
|
mActivity = (Activity) context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.import_keys_file_fragment, menu);
|
||||||
|
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
int itemId = item.getItemId();
|
||||||
|
switch (itemId) {
|
||||||
|
case R.id.menu_import_keys_file_open:
|
||||||
// open .asc or .gpg files
|
// open .asc or .gpg files
|
||||||
// setting it to text/plain prevents Cyanogenmod's file manager from selecting asc
|
// setting it to text/plain prevents Cyanogenmod's file manager from selecting asc
|
||||||
// or gpg types!
|
// or gpg types!
|
||||||
FileHelper.openDocument(ImportKeysFileFragment.this,
|
FileHelper.openDocument(ImportKeysFileFragment.this,
|
||||||
Uri.fromFile(Constants.Path.APP_DIR), "*/*", false, REQUEST_CODE_FILE);
|
Uri.fromFile(Constants.Path.APP_DIR), "*/*", false, REQUEST_CODE_FILE);
|
||||||
}
|
return true;
|
||||||
});
|
case R.id.menu_import_keys_file_paste:
|
||||||
|
|
||||||
mClipboardButton = view.findViewById(R.id.import_clipboard_button);
|
|
||||||
mClipboardButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity());
|
CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity());
|
||||||
String sendText = "";
|
String sendText = "";
|
||||||
if (clipboardText != null) {
|
if (clipboardText != null) {
|
||||||
sendText = clipboardText.toString();
|
sendText = clipboardText.toString();
|
||||||
sendText = PgpHelper.getPgpKeyContent(sendText);
|
sendText = PgpHelper.getPgpKeyContent(sendText);
|
||||||
if (sendText == null) {
|
if (sendText == null) {
|
||||||
Notify.create(mImportActivity, R.string.error_bad_data, Style.ERROR).show();
|
Notify.create(mActivity, R.string.error_bad_data, Style.ERROR).show();
|
||||||
return;
|
} else {
|
||||||
|
mCallback.loadKeys(new BytesLoaderState(sendText.getBytes(), null));
|
||||||
}
|
}
|
||||||
mImportActivity.loadCallback(new BytesLoaderState(sendText.getBytes(), null));
|
|
||||||
}
|
}
|
||||||
}
|
return true;
|
||||||
});
|
}
|
||||||
|
|
||||||
return view;
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(Activity activity) {
|
|
||||||
super.onAttach(activity);
|
|
||||||
|
|
||||||
mImportActivity = (ImportKeysActivity) activity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -125,90 +129,49 @@ public class ImportKeysFileFragment extends Fragment {
|
|||||||
if (resultCode == Activity.RESULT_OK && data != null && data.getData() != null) {
|
if (resultCode == Activity.RESULT_OK && data != null && data.getData() != null) {
|
||||||
mCurrentUri = data.getData();
|
mCurrentUri = data.getData();
|
||||||
|
|
||||||
if (checkAndRequestReadPermission(mCurrentUri)) {
|
if (PermissionsUtil.checkAndRequestReadPermission(this, mCurrentUri)) {
|
||||||
startImportingKeys();
|
startImportingKeys();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startImportingKeys() {
|
private void startImportingKeys() {
|
||||||
boolean isEncrypted;
|
boolean isEncrypted;
|
||||||
try {
|
try {
|
||||||
isEncrypted = FileHelper.isEncryptedFile(mImportActivity, mCurrentUri);
|
isEncrypted = FileHelper.isEncryptedFile(mActivity, mCurrentUri);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(Constants.TAG, "Error opening file", e);
|
Log.e(Constants.TAG, "Error opening file", e);
|
||||||
|
|
||||||
Notify.create(mImportActivity, R.string.error_bad_data, Style.ERROR).show();
|
Notify.create(mActivity, R.string.error_bad_data, Style.ERROR).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEncrypted) {
|
if (isEncrypted) {
|
||||||
Intent intent = new Intent(mImportActivity, DecryptActivity.class);
|
Intent intent = new Intent(mActivity, DecryptActivity.class);
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
intent.setData(mCurrentUri);
|
intent.setData(mCurrentUri);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
} else {
|
} else {
|
||||||
mImportActivity.loadCallback(new BytesLoaderState(null, mCurrentUri));
|
mCallback.loadKeys(new BytesLoaderState(null, mCurrentUri));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Request READ_EXTERNAL_STORAGE permission on Android >= 6.0 to read content from "file" Uris.
|
|
||||||
* <p/>
|
|
||||||
* This method returns true on Android < 6, or if permission is already granted. It
|
|
||||||
* requests the permission and returns false otherwise.
|
|
||||||
* <p/>
|
|
||||||
* see https://commonsware.com/blog/2015/10/07/runtime-permissions-files-action-send.html
|
|
||||||
*/
|
|
||||||
private boolean checkAndRequestReadPermission(final Uri uri) {
|
|
||||||
if (!ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Additional check due to https://commonsware.com/blog/2015/11/09/you-cannot-hold-nonexistent-permissions.html
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE)
|
|
||||||
== PackageManager.PERMISSION_GRANTED) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
requestPermissions(
|
|
||||||
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
|
|
||||||
REQUEST_PERMISSION_READ_EXTERNAL_STORAGE);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode,
|
public void onRequestPermissionsResult(int requestCode,
|
||||||
@NonNull String[] permissions,
|
@NonNull String[] permissions,
|
||||||
@NonNull int[] grantResults) {
|
@NonNull int[] grantResults) {
|
||||||
|
|
||||||
if (requestCode != REQUEST_PERMISSION_READ_EXTERNAL_STORAGE) {
|
if (PermissionsUtil.checkReadPermissionResult(mActivity, requestCode, grantResults)) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean permissionWasGranted = grantResults.length > 0
|
|
||||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
|
||||||
|
|
||||||
if (permissionWasGranted) {
|
|
||||||
startImportingKeys();
|
startImportingKeys();
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(getActivity(), R.string.error_denied_storage_permission, Toast.LENGTH_LONG).show();
|
mActivity.setResult(Activity.RESULT_CANCELED);
|
||||||
getActivity().setResult(Activity.RESULT_CANCELED);
|
mActivity.finish();
|
||||||
getActivity().finish();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+128
-280
@@ -18,47 +18,45 @@
|
|||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ContentResolver;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
import android.databinding.DataBindingUtil;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.ListFragment;
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.app.FragmentActivity;
|
||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.ContextCompat;
|
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
import android.support.v4.util.LongSparseArray;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.view.MotionEvent;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnTouchListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.widget.ListView;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.databinding.ImportKeysListFragmentBinding;
|
||||||
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
||||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
import org.sufficientlysecure.keychain.keyimport.processing.AsyncTaskResultWrapper;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.BytesLoaderState;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.CloudLoaderState;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.ImportKeysListCloudLoader;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.ImportKeysListLoader;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.ImportKeysListener;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.LoaderState;
|
||||||
import org.sufficientlysecure.keychain.operations.results.GetKeyResult;
|
import org.sufficientlysecure.keychain.operations.results.GetKeyResult;
|
||||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
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.ImportKeysAdapter;
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListCloudLoader;
|
import org.sufficientlysecure.keychain.ui.util.PermissionsUtil;
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
|
|
||||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||||
import org.sufficientlysecure.keychain.util.Preferences;
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
|
import org.sufficientlysecure.keychain.util.Preferences.CloudSearchPrefs;
|
||||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||||
|
|
||||||
public class ImportKeysListFragment extends ListFragment implements
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class ImportKeysListFragment extends Fragment implements
|
||||||
LoaderManager.LoaderCallbacks<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
|
LoaderManager.LoaderCallbacks<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
|
||||||
|
|
||||||
private static final String ARG_DATA_URI = "uri";
|
private static final String ARG_DATA_URI = "uri";
|
||||||
@@ -67,74 +65,25 @@ public class ImportKeysListFragment extends ListFragment implements
|
|||||||
public static final String ARG_NON_INTERACTIVE = "non_interactive";
|
public static final String ARG_NON_INTERACTIVE = "non_interactive";
|
||||||
public static final String ARG_CLOUD_SEARCH_PREFS = "cloud_search_prefs";
|
public static final String ARG_CLOUD_SEARCH_PREFS = "cloud_search_prefs";
|
||||||
|
|
||||||
private static final int REQUEST_PERMISSION_READ_EXTERNAL_STORAGE = 12;
|
private FragmentActivity mActivity;
|
||||||
|
private ImportKeysListener mListener;
|
||||||
|
|
||||||
private Activity mActivity;
|
private ImportKeysListFragmentBinding mBinding;
|
||||||
private ImportKeysAdapter mAdapter;
|
|
||||||
private ParcelableProxy mParcelableProxy;
|
private ParcelableProxy mParcelableProxy;
|
||||||
|
|
||||||
|
private ImportKeysAdapter mAdapter;
|
||||||
private LoaderState mLoaderState;
|
private LoaderState mLoaderState;
|
||||||
|
|
||||||
|
public static final int STATUS_FIRST = 0;
|
||||||
|
public static final int STATUS_LOADING = 1;
|
||||||
|
public static final int STATUS_LOADED = 2;
|
||||||
|
public static final int STATUS_EMPTY = 3;
|
||||||
|
|
||||||
private static final int LOADER_ID_BYTES = 0;
|
private static final int LOADER_ID_BYTES = 0;
|
||||||
private static final int LOADER_ID_CLOUD = 1;
|
private static final int LOADER_ID_CLOUD = 1;
|
||||||
|
|
||||||
private LongSparseArray<ParcelableKeyRing> mCachedKeyData;
|
|
||||||
private boolean mNonInteractive;
|
|
||||||
|
|
||||||
private boolean mShowingOrbotDialog;
|
private boolean mShowingOrbotDialog;
|
||||||
|
|
||||||
public LoaderState getLoaderState() {
|
|
||||||
return mLoaderState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ImportKeysListEntry> getData() {
|
|
||||||
return mAdapter.getData();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an Iterator (with size) of the selected data items.
|
|
||||||
* This iterator is sort of a tradeoff, it's slightly more complex than an
|
|
||||||
* ArrayList would have been, but we save some memory by just returning
|
|
||||||
* relevant elements on demand.
|
|
||||||
*/
|
|
||||||
public IteratorWithSize<ParcelableKeyRing> getSelectedData() {
|
|
||||||
final ArrayList<ImportKeysListEntry> entries = getSelectedEntries();
|
|
||||||
final Iterator<ImportKeysListEntry> it = entries.iterator();
|
|
||||||
return new IteratorWithSize<ParcelableKeyRing>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSize() {
|
|
||||||
return entries.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
return it.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ParcelableKeyRing next() {
|
|
||||||
// throws NoSuchElementException if it doesn't exist, but that's not our problem
|
|
||||||
return mCachedKeyData.get(it.next().hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove() {
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<ImportKeysListEntry> getSelectedEntries() {
|
|
||||||
if (mAdapter != null) {
|
|
||||||
return mAdapter.getSelectedEntries();
|
|
||||||
} else {
|
|
||||||
Log.e(Constants.TAG, "Adapter not initialized, returning empty list");
|
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an interactive ImportKeyListFragment which reads keyrings from bytes, or file specified
|
* 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
|
* by dataUri, or searches a keyserver for serverQuery, if parameter is not null, in that order
|
||||||
@@ -148,7 +97,8 @@ public class ImportKeysListFragment extends ListFragment implements
|
|||||||
* @return fragment with arguments set based on passed parameters
|
* @return fragment with arguments set based on passed parameters
|
||||||
*/
|
*/
|
||||||
public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery,
|
public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery,
|
||||||
Preferences.CloudSearchPrefs cloudSearchPrefs) {
|
CloudSearchPrefs cloudSearchPrefs) {
|
||||||
|
|
||||||
return newInstance(bytes, dataUri, serverQuery, false, cloudSearchPrefs);
|
return newInstance(bytes, dataUri, serverQuery, false, cloudSearchPrefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,8 +118,7 @@ public class ImportKeysListFragment extends ListFragment implements
|
|||||||
Uri dataUri,
|
Uri dataUri,
|
||||||
String serverQuery,
|
String serverQuery,
|
||||||
boolean nonInteractive,
|
boolean nonInteractive,
|
||||||
Preferences.CloudSearchPrefs cloudSearchPrefs) {
|
CloudSearchPrefs cloudSearchPrefs) {
|
||||||
ImportKeysListFragment frag = new ImportKeysListFragment();
|
|
||||||
|
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putByteArray(ARG_BYTES, bytes);
|
args.putByteArray(ARG_BYTES, bytes);
|
||||||
@@ -178,115 +127,68 @@ public class ImportKeysListFragment extends ListFragment implements
|
|||||||
args.putBoolean(ARG_NON_INTERACTIVE, nonInteractive);
|
args.putBoolean(ARG_NON_INTERACTIVE, nonInteractive);
|
||||||
args.putParcelable(ARG_CLOUD_SEARCH_PREFS, cloudSearchPrefs);
|
args.putParcelable(ARG_CLOUD_SEARCH_PREFS, cloudSearchPrefs);
|
||||||
|
|
||||||
|
ImportKeysListFragment frag = new ImportKeysListFragment();
|
||||||
frag.setArguments(args);
|
frag.setArguments(args);
|
||||||
|
|
||||||
return frag;
|
return frag;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public class LoaderState {
|
|
||||||
}
|
|
||||||
|
|
||||||
static public class BytesLoaderState extends LoaderState {
|
|
||||||
public byte[] mKeyBytes;
|
|
||||||
public Uri mDataUri;
|
|
||||||
|
|
||||||
BytesLoaderState(byte[] keyBytes, Uri dataUri) {
|
|
||||||
mKeyBytes = keyBytes;
|
|
||||||
mDataUri = dataUri;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static public class CloudLoaderState extends LoaderState {
|
|
||||||
Preferences.CloudSearchPrefs mCloudPrefs;
|
|
||||||
String mServerQuery;
|
|
||||||
|
|
||||||
CloudLoaderState(String serverQuery, Preferences.CloudSearchPrefs cloudPrefs) {
|
|
||||||
mServerQuery = serverQuery;
|
|
||||||
mCloudPrefs = cloudPrefs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define Adapter and Loader on create of Activity
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
mBinding = DataBindingUtil.inflate(inflater, R.layout.import_keys_list_fragment, container, false);
|
||||||
|
mBinding.setStatus(STATUS_FIRST);
|
||||||
|
View view = mBinding.getRoot();
|
||||||
|
|
||||||
mActivity = getActivity();
|
mActivity = getActivity();
|
||||||
|
|
||||||
// Give some text to display if there is no data.
|
|
||||||
setEmptyText(mActivity.getString(R.string.error_nothing_import));
|
|
||||||
|
|
||||||
// Create an empty adapter we will use to display the loaded data.
|
|
||||||
mAdapter = new ImportKeysAdapter(mActivity);
|
|
||||||
setListAdapter(mAdapter);
|
|
||||||
|
|
||||||
Bundle args = getArguments();
|
Bundle args = getArguments();
|
||||||
Uri dataUri = args.getParcelable(ARG_DATA_URI);
|
Uri dataUri = args.getParcelable(ARG_DATA_URI);
|
||||||
byte[] bytes = args.getByteArray(ARG_BYTES);
|
byte[] bytes = args.getByteArray(ARG_BYTES);
|
||||||
String query = args.getString(ARG_SERVER_QUERY);
|
String query = args.getString(ARG_SERVER_QUERY);
|
||||||
mNonInteractive = args.getBoolean(ARG_NON_INTERACTIVE, false);
|
boolean nonInteractive = args.getBoolean(ARG_NON_INTERACTIVE, false);
|
||||||
|
mBinding.basic.setNonInteractive(nonInteractive);
|
||||||
|
|
||||||
getListView().setOnTouchListener(new OnTouchListener() {
|
// Create an empty adapter we will use to display the loaded data.
|
||||||
|
mAdapter = new ImportKeysAdapter(mActivity, mListener, nonInteractive);
|
||||||
|
mBinding.recyclerView.setAdapter(mAdapter);
|
||||||
|
mBinding.recyclerView.setLayoutManager(new LinearLayoutManager(mActivity));
|
||||||
|
|
||||||
|
if (dataUri != null || bytes != null) {
|
||||||
|
loadState(new BytesLoaderState(bytes, dataUri));
|
||||||
|
} else if (query != null) {
|
||||||
|
CloudSearchPrefs cloudSearchPrefs = args.getParcelable(ARG_CLOUD_SEARCH_PREFS);
|
||||||
|
if (cloudSearchPrefs == null) {
|
||||||
|
cloudSearchPrefs = Preferences.getPreferences(mActivity).getCloudSearchPrefs();
|
||||||
|
}
|
||||||
|
loadState(new CloudLoaderState(query, cloudSearchPrefs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// mBinding.basic is only used for file import
|
||||||
|
mBinding.basic.importKeys.setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouch(View v, MotionEvent event) {
|
public void onClick(View view) {
|
||||||
if (!mAdapter.isEmpty()) {
|
mListener.importKeys(mAdapter.getEntries());
|
||||||
mActivity.onTouchEvent(event);
|
}
|
||||||
}
|
});
|
||||||
return false;
|
mBinding.basic.listKeys.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
mBinding.setAdvanced(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
getListView().setFastScrollEnabled(true);
|
return view;
|
||||||
|
|
||||||
if (dataUri != null || bytes != null) {
|
|
||||||
mLoaderState = new BytesLoaderState(bytes, dataUri);
|
|
||||||
} else if (query != null) {
|
|
||||||
Preferences.CloudSearchPrefs cloudSearchPrefs
|
|
||||||
= args.getParcelable(ARG_CLOUD_SEARCH_PREFS);
|
|
||||||
if (cloudSearchPrefs == null) {
|
|
||||||
cloudSearchPrefs = Preferences.getPreferences(getActivity()).getCloudSearchPrefs();
|
|
||||||
}
|
|
||||||
|
|
||||||
mLoaderState = new CloudLoaderState(query, cloudSearchPrefs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataUri != null && ! checkAndRequestReadPermission(dataUri)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
restartLoaders();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Request READ_EXTERNAL_STORAGE permission on Android >= 6.0 to read content from "file" Uris.
|
public void onAttach(Context context) {
|
||||||
*
|
super.onAttach(context);
|
||||||
* This method returns true on Android < 6, or if permission is already granted. It
|
|
||||||
* requests the permission and returns false otherwise.
|
try {
|
||||||
*
|
mListener = (ImportKeysListener) context;
|
||||||
* see https://commonsware.com/blog/2015/10/07/runtime-permissions-files-action-send.html
|
} catch (ClassCastException e) {
|
||||||
*/
|
throw new ClassCastException(context.toString()
|
||||||
private boolean checkAndRequestReadPermission(final Uri uri) {
|
+ " must implement ImportKeysListener");
|
||||||
if ( ! ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additional check due to https://commonsware.com/blog/2015/11/09/you-cannot-hold-nonexistent-permissions.html
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE)
|
|
||||||
== PackageManager.PERMISSION_GRANTED) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
requestPermissions(
|
|
||||||
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
|
|
||||||
REQUEST_PERMISSION_READ_EXTERNAL_STORAGE);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -294,140 +196,99 @@ public class ImportKeysListFragment extends ListFragment implements
|
|||||||
@NonNull String[] permissions,
|
@NonNull String[] permissions,
|
||||||
@NonNull int[] grantResults) {
|
@NonNull int[] grantResults) {
|
||||||
|
|
||||||
if (requestCode != REQUEST_PERMISSION_READ_EXTERNAL_STORAGE) {
|
if (PermissionsUtil.checkReadPermissionResult(mActivity, requestCode, grantResults)) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean permissionWasGranted = grantResults.length > 0
|
|
||||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
|
||||||
|
|
||||||
if (permissionWasGranted) {
|
|
||||||
// permission granted -> load key
|
|
||||||
restartLoaders();
|
restartLoaders();
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(getActivity(), R.string.error_denied_storage_permission, Toast.LENGTH_LONG).show();
|
mActivity.setResult(Activity.RESULT_CANCELED);
|
||||||
getActivity().setResult(Activity.RESULT_CANCELED);
|
mActivity.finish();
|
||||||
getActivity().finish();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
* User may want to go back to single card view if he's now in full key list
|
||||||
super.onListItemClick(l, v, position, id);
|
* Check if we are in full key list and if this import operation supports basic mode
|
||||||
|
*
|
||||||
if (mNonInteractive) {
|
* @return true if activity's back pressed can be performed
|
||||||
return;
|
*/
|
||||||
|
public boolean onBackPressed() {
|
||||||
|
boolean advanced = mBinding.getAdvanced();
|
||||||
|
if (advanced && mLoaderState.isBasicModeSupported()) {
|
||||||
|
mBinding.setAdvanced(false);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
// Select checkbox!
|
|
||||||
// Update underlying data and notify adapter of change. The adapter will
|
|
||||||
// update the view automatically.
|
|
||||||
|
|
||||||
ImportKeysListEntry entry = mAdapter.getItem(position);
|
|
||||||
entry.setSelected(!entry.isSelected());
|
|
||||||
mAdapter.notifyDataSetChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadNew(LoaderState loaderState) {
|
public void loadState(LoaderState loaderState) {
|
||||||
mLoaderState = loaderState;
|
mLoaderState = loaderState;
|
||||||
|
|
||||||
if (mLoaderState instanceof BytesLoaderState) {
|
if (mLoaderState instanceof BytesLoaderState) {
|
||||||
BytesLoaderState ls = (BytesLoaderState) mLoaderState;
|
BytesLoaderState ls = (BytesLoaderState) mLoaderState;
|
||||||
|
|
||||||
if ( ls.mDataUri != null && ! checkAndRequestReadPermission(ls.mDataUri)) {
|
if (ls.mDataUri != null &&
|
||||||
|
!PermissionsUtil.checkAndRequestReadPermission(this, ls.mDataUri)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mBinding.setAdvanced(!mLoaderState.isBasicModeSupported());
|
||||||
|
|
||||||
restartLoaders();
|
restartLoaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroyLoader() {
|
|
||||||
if (getLoaderManager().getLoader(LOADER_ID_BYTES) != null) {
|
|
||||||
getLoaderManager().destroyLoader(LOADER_ID_BYTES);
|
|
||||||
}
|
|
||||||
if (getLoaderManager().getLoader(LOADER_ID_CLOUD) != null) {
|
|
||||||
getLoaderManager().destroyLoader(LOADER_ID_CLOUD);
|
|
||||||
}
|
|
||||||
if (getView() != null) {
|
|
||||||
setListShown(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void restartLoaders() {
|
private void restartLoaders() {
|
||||||
|
LoaderManager loaderManager = getLoaderManager();
|
||||||
|
|
||||||
if (mLoaderState instanceof BytesLoaderState) {
|
if (mLoaderState instanceof BytesLoaderState) {
|
||||||
// Start out with a progress indicator.
|
loaderManager.restartLoader(LOADER_ID_BYTES, null, this);
|
||||||
setListShown(false);
|
|
||||||
|
|
||||||
getLoaderManager().restartLoader(LOADER_ID_BYTES, null, this);
|
|
||||||
} else if (mLoaderState instanceof CloudLoaderState) {
|
} else if (mLoaderState instanceof CloudLoaderState) {
|
||||||
// Start out with a progress indicator.
|
loaderManager.restartLoader(LOADER_ID_CLOUD, null, this);
|
||||||
setListShown(false);
|
|
||||||
|
|
||||||
getLoaderManager().restartLoader(LOADER_ID_CLOUD, null, this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>>
|
public Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> onCreateLoader(
|
||||||
onCreateLoader(int id, Bundle args) {
|
int id, Bundle args) {
|
||||||
|
|
||||||
|
Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> loader = null;
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case LOADER_ID_BYTES: {
|
case LOADER_ID_BYTES: {
|
||||||
return new ImportKeysListLoader(mActivity, (BytesLoaderState) mLoaderState);
|
loader = new ImportKeysListLoader(mActivity, (BytesLoaderState) mLoaderState);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case LOADER_ID_CLOUD: {
|
case LOADER_ID_CLOUD: {
|
||||||
CloudLoaderState ls = (CloudLoaderState) mLoaderState;
|
loader = new ImportKeysListCloudLoader(mActivity, (CloudLoaderState) mLoaderState,
|
||||||
return new ImportKeysListCloudLoader(getActivity(), ls.mServerQuery, ls.mCloudPrefs,
|
|
||||||
mParcelableProxy);
|
mParcelableProxy);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (loader != null) {
|
||||||
|
mBinding.setStatus(STATUS_LOADING);
|
||||||
|
}
|
||||||
|
|
||||||
|
return loader;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadFinished(Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> loader,
|
public void onLoadFinished(
|
||||||
AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> data) {
|
Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> loader,
|
||||||
// Swap the new cursor in. (The framework will take care of closing the
|
AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> data) {
|
||||||
// old cursor once we return.)
|
|
||||||
|
|
||||||
Log.d(Constants.TAG, "data: " + data.getResult());
|
|
||||||
|
|
||||||
// swap in the real data!
|
|
||||||
mAdapter.setData(data.getResult());
|
mAdapter.setData(data.getResult());
|
||||||
mAdapter.notifyDataSetChanged();
|
int size = mAdapter.getItemCount();
|
||||||
|
|
||||||
setListAdapter(mAdapter);
|
mBinding.setNumber(size);
|
||||||
|
mBinding.setStatus(size > 0 ? STATUS_LOADED : STATUS_EMPTY);
|
||||||
// The list should now be shown.
|
|
||||||
if (isResumed()) {
|
|
||||||
setListShown(true);
|
|
||||||
} else {
|
|
||||||
setListShownNoAnimation(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// free old cached key data
|
|
||||||
mCachedKeyData = null;
|
|
||||||
|
|
||||||
GetKeyResult getKeyResult = (GetKeyResult) data.getOperationResult();
|
GetKeyResult getKeyResult = (GetKeyResult) data.getOperationResult();
|
||||||
switch (loader.getId()) {
|
switch (loader.getId()) {
|
||||||
case LOADER_ID_BYTES:
|
case LOADER_ID_BYTES:
|
||||||
|
if (!getKeyResult.success()) {
|
||||||
if (getKeyResult.success()) {
|
getKeyResult.createNotify(mActivity).show();
|
||||||
// No error
|
|
||||||
mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings();
|
|
||||||
} else {
|
|
||||||
getKeyResult.createNotify(getActivity()).show();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LOADER_ID_CLOUD:
|
case LOADER_ID_CLOUD:
|
||||||
|
if (getKeyResult.isPending()) {
|
||||||
if (getKeyResult.success()) {
|
|
||||||
// No error
|
|
||||||
} else if (getKeyResult.isPending()) {
|
|
||||||
if (getKeyResult.getRequiredInputParcel().mType ==
|
if (getKeyResult.getRequiredInputParcel().mType ==
|
||||||
RequiredInputParcel.RequiredInputType.ENABLE_ORBOT) {
|
RequiredInputParcel.RequiredInputType.ENABLE_ORBOT) {
|
||||||
if (mShowingOrbotDialog) {
|
if (mShowingOrbotDialog) {
|
||||||
@@ -461,8 +322,7 @@ public class ImportKeysListFragment extends ListFragment implements
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (OrbotHelper.putOrbotInRequiredState(dialogActions,
|
if (OrbotHelper.putOrbotInRequiredState(dialogActions, mActivity)) {
|
||||||
getActivity())) {
|
|
||||||
// looks like we didn't have to show the
|
// looks like we didn't have to show the
|
||||||
// dialog after all
|
// dialog after all
|
||||||
mShowingOrbotDialog = false;
|
mShowingOrbotDialog = false;
|
||||||
@@ -473,30 +333,18 @@ public class ImportKeysListFragment extends ListFragment implements
|
|||||||
new Handler().post(showOrbotDialog);
|
new Handler().post(showOrbotDialog);
|
||||||
mShowingOrbotDialog = true;
|
mShowingOrbotDialog = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!getKeyResult.success()) {
|
||||||
getKeyResult.createNotify(getActivity()).show();
|
getKeyResult.createNotify(mActivity).show();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoaderReset(Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> loader) {
|
public void onLoaderReset(
|
||||||
switch (loader.getId()) {
|
Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> loader) {
|
||||||
case LOADER_ID_BYTES:
|
|
||||||
// Clear the data in the adapter.
|
mAdapter.clearData();
|
||||||
mAdapter.clear();
|
|
||||||
break;
|
|
||||||
case LOADER_ID_CLOUD:
|
|
||||||
// Clear the data in the adapter.
|
|
||||||
mAdapter.clear();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -198,7 +198,7 @@ public class ImportKeysProxyActivity extends FragmentActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void importKeys(String fingerprint) {
|
public void importKeys(String fingerprint) {
|
||||||
ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null);
|
ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null, null);
|
||||||
ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
|
ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
|
||||||
selectedEntries.add(keyEntry);
|
selectedEntries.add(keyEntry);
|
||||||
|
|
||||||
|
|||||||
+245
@@ -0,0 +1,245 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.MatrixCursor;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceActivity;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.view.MenuItemCompat;
|
||||||
|
import android.support.v4.view.MenuItemCompat.OnActionExpandListener;
|
||||||
|
import android.support.v4.widget.CursorAdapter;
|
||||||
|
import android.support.v4.widget.SimpleCursorAdapter;
|
||||||
|
import android.support.v7.widget.SearchView;
|
||||||
|
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.view.inputmethod.InputMethodManager;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.CloudLoaderState;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.ImportKeysListener;
|
||||||
|
import org.sufficientlysecure.keychain.util.ContactHelper;
|
||||||
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
|
import org.sufficientlysecure.keychain.util.Preferences.CloudSearchPrefs;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static android.support.v7.widget.SearchView.OnQueryTextListener;
|
||||||
|
import static android.support.v7.widget.SearchView.OnSuggestionListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consists of the search bar, search button, and search settings button
|
||||||
|
*/
|
||||||
|
public class ImportKeysSearchFragment extends Fragment {
|
||||||
|
|
||||||
|
public static final String ARG_QUERY = "query";
|
||||||
|
public static final String ARG_CLOUD_SEARCH_PREFS = "cloud_search_prefs";
|
||||||
|
|
||||||
|
private static final String CURSOR_SUGGESTION = "suggestion";
|
||||||
|
|
||||||
|
private Activity mActivity;
|
||||||
|
private ImportKeysListener mCallback;
|
||||||
|
|
||||||
|
private List<String> mNamesAndEmails;
|
||||||
|
private SimpleCursorAdapter mSearchAdapter;
|
||||||
|
|
||||||
|
private List<String> mCurrentSuggestions = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new instance of this fragment
|
||||||
|
*
|
||||||
|
* @param query query to search for
|
||||||
|
* @param cloudSearchPrefs search parameters to use. If null will retrieve from user's
|
||||||
|
* preferences.
|
||||||
|
*/
|
||||||
|
public static ImportKeysSearchFragment newInstance(String query,
|
||||||
|
CloudSearchPrefs cloudSearchPrefs) {
|
||||||
|
|
||||||
|
ImportKeysSearchFragment frag = new ImportKeysSearchFragment();
|
||||||
|
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putString(ARG_QUERY, query);
|
||||||
|
args.putParcelable(ARG_CLOUD_SEARCH_PREFS, cloudSearchPrefs);
|
||||||
|
|
||||||
|
frag.setArguments(args);
|
||||||
|
|
||||||
|
return frag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater i, ViewGroup c, Bundle savedInstanceState) {
|
||||||
|
ContactHelper contactHelper = new ContactHelper(mActivity);
|
||||||
|
mNamesAndEmails = contactHelper.getContactNames();
|
||||||
|
mNamesAndEmails.addAll(contactHelper.getContactMails());
|
||||||
|
|
||||||
|
mSearchAdapter = new SimpleCursorAdapter(mActivity,
|
||||||
|
R.layout.import_keys_cloud_suggestions_item, null, new String[]{CURSOR_SUGGESTION},
|
||||||
|
new int[]{android.R.id.text1}, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
|
||||||
|
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
|
// no view, just search view
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
|
||||||
|
try {
|
||||||
|
mCallback = (ImportKeysListener) context;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new ClassCastException(context.toString()
|
||||||
|
+ " must implement ImportKeysListener");
|
||||||
|
}
|
||||||
|
|
||||||
|
mActivity = (Activity) context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.import_keys_cloud_fragment, menu);
|
||||||
|
|
||||||
|
MenuItem searchItem = menu.findItem(R.id.menu_import_keys_cloud_search);
|
||||||
|
final SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
|
||||||
|
searchView.setSuggestionsAdapter(mSearchAdapter);
|
||||||
|
|
||||||
|
searchView.setOnSuggestionListener(new OnSuggestionListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onSuggestionSelect(int position) {
|
||||||
|
searchView.setQuery(mCurrentSuggestions.get(position), true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSuggestionClick(int position) {
|
||||||
|
searchView.setQuery(mCurrentSuggestions.get(position), true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchView.setOnQueryTextListener(new OnQueryTextListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextSubmit(String query) {
|
||||||
|
searchView.clearFocus();
|
||||||
|
search(searchView.getQuery().toString().trim());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextChange(String newText) {
|
||||||
|
updateAdapter(newText);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
MenuItemCompat.setOnActionExpandListener(searchItem, new OnActionExpandListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemActionExpand(MenuItem item) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemActionCollapse(MenuItem item) {
|
||||||
|
mActivity.finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchItem.expandActionView();
|
||||||
|
|
||||||
|
String query = getArguments().getString(ARG_QUERY);
|
||||||
|
if (query != null) {
|
||||||
|
searchView.setQuery(query, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAdapter(String query) {
|
||||||
|
mCurrentSuggestions.clear();
|
||||||
|
MatrixCursor c = new MatrixCursor(new String[]{BaseColumns._ID, CURSOR_SUGGESTION});
|
||||||
|
for (int i = 0; i < mNamesAndEmails.size(); i++) {
|
||||||
|
String s = mNamesAndEmails.get(i);
|
||||||
|
if (s.toLowerCase().startsWith(query.toLowerCase())) {
|
||||||
|
mCurrentSuggestions.add(s);
|
||||||
|
c.addRow(new Object[]{i, s});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
mSearchAdapter.changeCursor(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
int itemId = item.getItemId();
|
||||||
|
switch (itemId) {
|
||||||
|
case R.id.menu_import_keys_cloud_settings:
|
||||||
|
Intent intent = new Intent(mActivity, SettingsActivity.class);
|
||||||
|
intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT,
|
||||||
|
SettingsActivity.CloudSearchPrefsFragment.class.getName());
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void search(String query) {
|
||||||
|
CloudSearchPrefs cloudSearchPrefs
|
||||||
|
= getArguments().getParcelable(ARG_CLOUD_SEARCH_PREFS);
|
||||||
|
|
||||||
|
// no explicit search preferences passed
|
||||||
|
if (cloudSearchPrefs == null) {
|
||||||
|
cloudSearchPrefs = Preferences.getPreferences(getActivity()).getCloudSearchPrefs();
|
||||||
|
}
|
||||||
|
|
||||||
|
mCallback.loadKeys(new CloudLoaderState(query, cloudSearchPrefs));
|
||||||
|
toggleKeyboard(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toggleKeyboard(boolean show) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show) {
|
||||||
|
inputManager.showSoftInput(v, InputMethodManager.SHOW_IMPLICIT);
|
||||||
|
} else {
|
||||||
|
inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -54,6 +54,7 @@ import android.widget.ViewAnimator;
|
|||||||
|
|
||||||
import com.getbase.floatingactionbutton.FloatingActionButton;
|
import com.getbase.floatingactionbutton.FloatingActionButton;
|
||||||
import com.getbase.floatingactionbutton.FloatingActionsMenu;
|
import com.getbase.floatingactionbutton.FloatingActionsMenu;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||||
@@ -78,6 +79,7 @@ import org.sufficientlysecure.keychain.util.FabContainer;
|
|||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
|
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
|
||||||
import org.sufficientlysecure.keychain.util.Preferences;
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
|
|
||||||
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
|
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
|
||||||
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
||||||
|
|
||||||
@@ -245,7 +247,7 @@ public class KeyListFragment extends LoaderFragment
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
|
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
|
||||||
boolean checked) {
|
boolean checked) {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
mAdapter.setNewSelection(position, true);
|
mAdapter.setNewSelection(position, true);
|
||||||
} else {
|
} else {
|
||||||
@@ -334,7 +336,7 @@ public class KeyListFragment extends LoaderFragment
|
|||||||
headerCursor.addRow(row);
|
headerCursor.addRow(row);
|
||||||
|
|
||||||
Cursor dataCursor = data;
|
Cursor dataCursor = data;
|
||||||
data = new MergeCursor(new Cursor[] {
|
data = new MergeCursor(new Cursor[]{
|
||||||
headerCursor, dataCursor
|
headerCursor, dataCursor
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -576,7 +578,7 @@ public class KeyListFragment extends LoaderFragment
|
|||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
byte[] blob = cursor.getBlob(0);//fingerprint column is 0
|
byte[] blob = cursor.getBlob(0);//fingerprint column is 0
|
||||||
String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
|
String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
|
||||||
ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null);
|
ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null, null);
|
||||||
keyList.add(keyEntry);
|
keyList.add(keyEntry);
|
||||||
}
|
}
|
||||||
mKeyList = keyList;
|
mKeyList = keyList;
|
||||||
|
|||||||
+2
-1
@@ -30,6 +30,7 @@ import android.widget.NumberPicker;
|
|||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
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.ImportKeyResult;
|
||||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
@@ -142,7 +143,7 @@ public class SafeSlingerActivity extends BaseActivity
|
|||||||
// We parcel this iteratively into a file - anything we can
|
// We parcel this iteratively into a file - anything we can
|
||||||
// display here, we should be able to import.
|
// display here, we should be able to import.
|
||||||
ParcelableFileCache<ParcelableKeyRing> cache =
|
ParcelableFileCache<ParcelableKeyRing> cache =
|
||||||
new ParcelableFileCache<>(this, "key_import.pcl");
|
new ParcelableFileCache<>(this, ImportOperation.CACHE_FILE_NAME);
|
||||||
cache.writeCache(it.size(), it.iterator());
|
cache.writeCache(it.size(), it.iterator());
|
||||||
|
|
||||||
mOperationHelper =
|
mOperationHelper =
|
||||||
|
|||||||
+12
-10
@@ -19,11 +19,6 @@
|
|||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import android.animation.ArgbEvaluator;
|
import android.animation.ArgbEvaluator;
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
@@ -89,10 +84,10 @@ import org.sufficientlysecure.keychain.ui.ViewKeyFragment.PostponeType;
|
|||||||
import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
|
import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
|
||||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
|
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.ContentDescriptionHint;
|
||||||
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
|
||||||
import org.sufficientlysecure.keychain.ui.util.ContentDescriptionHint;
|
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
|
import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||||
@@ -104,6 +99,11 @@ import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
|
|||||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||||
import org.sufficientlysecure.keychain.util.Preferences;
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
|
||||||
public class ViewKeyActivity extends BaseSecurityTokenActivity implements
|
public class ViewKeyActivity extends BaseSecurityTokenActivity implements
|
||||||
LoaderManager.LoaderCallbacks<Cursor>,
|
LoaderManager.LoaderCallbacks<Cursor>,
|
||||||
@@ -115,7 +115,9 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
|
|||||||
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@IntDef({REQUEST_QR_FINGERPRINT, REQUEST_BACKUP, REQUEST_CERTIFY, REQUEST_DELETE})
|
@IntDef({REQUEST_QR_FINGERPRINT, REQUEST_BACKUP, REQUEST_CERTIFY, REQUEST_DELETE})
|
||||||
private @interface RequestType {}
|
private @interface RequestType {
|
||||||
|
}
|
||||||
|
|
||||||
static final int REQUEST_QR_FINGERPRINT = 1;
|
static final int REQUEST_QR_FINGERPRINT = 1;
|
||||||
static final int REQUEST_BACKUP = 2;
|
static final int REQUEST_BACKUP = 2;
|
||||||
static final int REQUEST_CERTIFY = 3;
|
static final int REQUEST_CERTIFY = 3;
|
||||||
@@ -565,7 +567,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
|
|||||||
|
|
||||||
private void startBackupActivity() {
|
private void startBackupActivity() {
|
||||||
Intent intent = new Intent(this, BackupActivity.class);
|
Intent intent = new Intent(this, BackupActivity.class);
|
||||||
intent.putExtra(BackupActivity.EXTRA_MASTER_KEY_IDS, new long[] { mMasterKeyId });
|
intent.putExtra(BackupActivity.EXTRA_MASTER_KEY_IDS, new long[]{mMasterKeyId});
|
||||||
intent.putExtra(BackupActivity.EXTRA_SECRET, true);
|
intent.putExtra(BackupActivity.EXTRA_SECRET, true);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
@@ -722,7 +724,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
|
|||||||
manager.beginTransaction()
|
manager.beginTransaction()
|
||||||
.addToBackStack("security_token")
|
.addToBackStack("security_token")
|
||||||
.replace(R.id.view_key_fragment, frag)
|
.replace(R.id.view_key_fragment, frag)
|
||||||
// if this is called while the activity wasn't resumed, just forget it happened
|
// if this is called while the activity wasn't resumed, just forget it happened
|
||||||
.commitAllowingStateLoss();
|
.commitAllowingStateLoss();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1105,7 +1107,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
|
|||||||
KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
|
KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
|
||||||
String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
|
String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
|
||||||
|
|
||||||
ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null);
|
ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null, null);
|
||||||
ArrayList<ParcelableKeyRing> entries = new ArrayList<>();
|
ArrayList<ParcelableKeyRing> entries = new ArrayList<>();
|
||||||
entries.add(keyEntry);
|
entries.add(keyEntry);
|
||||||
mKeyList = entries;
|
mKeyList = entries;
|
||||||
|
|||||||
+2
-1
@@ -225,7 +225,8 @@ public class ViewKeyAdvActivity extends BaseActivity implements
|
|||||||
|
|
||||||
// get key id from MASTER_KEY_ID
|
// get key id from MASTER_KEY_ID
|
||||||
long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
|
long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
|
||||||
getSupportActionBar().setSubtitle(KeyFormattingUtils.beautifyKeyIdWithPrefix(this, masterKeyId));
|
String formattedKeyId = KeyFormattingUtils.beautifyKeyIdWithPrefix(masterKeyId);
|
||||||
|
getSupportActionBar().setSubtitle(formattedKeyId);
|
||||||
|
|
||||||
mHasSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0;
|
mHasSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0;
|
||||||
boolean isRevoked = data.getInt(INDEX_IS_REVOKED) > 0;
|
boolean isRevoked = data.getInt(INDEX_IS_REVOKED) > 0;
|
||||||
|
|||||||
+2
-1
@@ -237,7 +237,8 @@ public class ViewKeyAdvCertsFragment extends LoaderFragment implements
|
|||||||
TextView wSignerName = (TextView) view.findViewById(R.id.signerName);
|
TextView wSignerName = (TextView) view.findViewById(R.id.signerName);
|
||||||
TextView wSignStatus = (TextView) view.findViewById(R.id.signStatus);
|
TextView wSignStatus = (TextView) view.findViewById(R.id.signStatus);
|
||||||
|
|
||||||
String signerKeyId = KeyFormattingUtils.beautifyKeyIdWithPrefix(getActivity(), cursor.getLong(mIndexSignerKeyId));
|
String signerKeyId = KeyFormattingUtils.beautifyKeyIdWithPrefix(
|
||||||
|
cursor.getLong(mIndexSignerKeyId));
|
||||||
OpenPgpUtils.UserId userId = KeyRing.splitUserId(cursor.getString(mIndexSignerUserId));
|
OpenPgpUtils.UserId userId = KeyRing.splitUserId(cursor.getString(mIndexSignerUserId));
|
||||||
if (userId.name != null) {
|
if (userId.name != null) {
|
||||||
wSignerName.setText(userId.name);
|
wSignerName.setText(userId.name);
|
||||||
|
|||||||
+233
-199
@@ -17,261 +17,295 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui.adapter;
|
package org.sufficientlysecure.keychain.ui.adapter;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.content.Intent;
|
||||||
import android.app.Activity;
|
import android.databinding.DataBindingUtil;
|
||||||
import android.content.Context;
|
import android.support.v4.app.FragmentActivity;
|
||||||
import android.graphics.Color;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.os.Build;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.openintents.openpgp.util.OpenPgpUtils;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.databinding.ImportKeysListItemBinding;
|
||||||
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.ImportKeysListener;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.ImportKeysOperationCallback;
|
||||||
|
import org.sufficientlysecure.keychain.keyimport.processing.ImportKeysResultListener;
|
||||||
import org.sufficientlysecure.keychain.operations.ImportOperation;
|
import org.sufficientlysecure.keychain.operations.ImportOperation;
|
||||||
|
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||||
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||||
import org.sufficientlysecure.keychain.ui.util.Highlighter;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||||
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||||
|
import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
|
||||||
|
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
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.util.Log;
|
||||||
|
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
|
public class ImportKeysAdapter extends RecyclerView.Adapter<ImportKeysAdapter.ViewHolder> implements ImportKeysResultListener {
|
||||||
protected LayoutInflater mInflater;
|
|
||||||
protected Activity mActivity;
|
|
||||||
|
|
||||||
protected List<ImportKeysListEntry> mData;
|
private FragmentActivity mActivity;
|
||||||
|
private ImportKeysResultListener mListener;
|
||||||
|
private boolean mNonInteractive;
|
||||||
|
|
||||||
static class ViewHolder {
|
private List<ImportKeysListEntry> mData;
|
||||||
public TextView mainUserId;
|
private KeyState[] mKeyStates;
|
||||||
public TextView mainUserIdRest;
|
private int mCurrent;
|
||||||
public TextView keyId;
|
|
||||||
public TextView fingerprint;
|
private ProviderHelper mProviderHelper;
|
||||||
public TextView algorithm;
|
|
||||||
public ImageView status;
|
public ImportKeysAdapter(FragmentActivity activity, ImportKeysListener listener,
|
||||||
public View userIdsDivider;
|
boolean nonInteractive) {
|
||||||
public LinearLayout userIdsList;
|
|
||||||
public CheckBox checkBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImportKeysAdapter(Activity activity) {
|
|
||||||
super(activity, -1);
|
|
||||||
mActivity = activity;
|
mActivity = activity;
|
||||||
mInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
mListener = listener;
|
||||||
|
mNonInteractive = nonInteractive;
|
||||||
|
|
||||||
|
mProviderHelper = new ProviderHelper(activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
|
||||||
public void setData(List<ImportKeysListEntry> data) {
|
public void setData(List<ImportKeysListEntry> data) {
|
||||||
|
mData = data;
|
||||||
|
|
||||||
clear();
|
mKeyStates = new KeyState[data.size()];
|
||||||
if (data != null) {
|
for (int i = 0; i < mKeyStates.length; i++) {
|
||||||
this.mData = data;
|
mKeyStates[i] = new KeyState();
|
||||||
|
|
||||||
// add data to extended ArrayAdapter
|
ImportKeysListEntry entry = mData.get(i);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
long keyId = KeyFormattingUtils.convertKeyIdHexToKeyId(entry.getKeyIdHex());
|
||||||
addAll(data);
|
try {
|
||||||
} else {
|
KeyRing keyRing;
|
||||||
for (ImportKeysListEntry entry : data) {
|
if (entry.isSecretKey()) {
|
||||||
add(entry);
|
keyRing = mProviderHelper.getCanonicalizedSecretKeyRing(keyId);
|
||||||
|
} else {
|
||||||
|
keyRing = mProviderHelper.getCachedPublicKeyRing(keyId);
|
||||||
}
|
}
|
||||||
|
mKeyStates[i].mAlreadyPresent = true;
|
||||||
|
mKeyStates[i].mVerified = keyRing.getVerified() > 0;
|
||||||
|
} catch (ProviderHelper.NotFoundException | PgpKeyNotFoundException ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is only one key, get it automatically
|
||||||
|
if (mData.size() == 1) {
|
||||||
|
mCurrent = 0;
|
||||||
|
getKeyWithProgress(0, mData.get(0), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImportKeysListEntry> getData() {
|
public void clearData() {
|
||||||
return mData;
|
mData = null;
|
||||||
|
mKeyStates = null;
|
||||||
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** This method returns a list of all selected entries, with public keys sorted
|
/**
|
||||||
|
* This method returns a list of all selected entries, with public keys sorted
|
||||||
* before secret keys, see ImportOperation for specifics.
|
* before secret keys, see ImportOperation for specifics.
|
||||||
|
*
|
||||||
* @see ImportOperation
|
* @see ImportOperation
|
||||||
*/
|
*/
|
||||||
public ArrayList<ImportKeysListEntry> getSelectedEntries() {
|
public List<ImportKeysListEntry> getEntries() {
|
||||||
ArrayList<ImportKeysListEntry> result = new ArrayList<>();
|
ArrayList<ImportKeysListEntry> result = new ArrayList<>();
|
||||||
ArrayList<ImportKeysListEntry> secrets = new ArrayList<>();
|
ArrayList<ImportKeysListEntry> secrets = new ArrayList<>();
|
||||||
if (mData == null) {
|
if (mData == null) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
for (ImportKeysListEntry entry : mData) {
|
for (ImportKeysListEntry entry : mData) {
|
||||||
if (entry.isSelected()) {
|
// add this entry to either the secret or the public list
|
||||||
// add this entry to either the secret or the public list
|
(entry.isSecretKey() ? secrets : result).add(entry);
|
||||||
(entry.isSecretKey() ? secrets : result).add(entry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// add secret keys at the end of the list
|
// add secret keys at the end of the list
|
||||||
result.addAll(secrets);
|
result.addAll(secrets);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
public boolean hasStableIds() {
|
public ImportKeysListItemBinding b;
|
||||||
return true;
|
|
||||||
|
public ViewHolder(View view) {
|
||||||
|
super(view);
|
||||||
|
b = DataBindingUtil.bind(view);
|
||||||
|
b.setNonInteractive(mNonInteractive);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
@Override
|
||||||
ImportKeysListEntry entry = mData.get(position);
|
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
Highlighter highlighter = new Highlighter(mActivity, entry.getQuery());
|
LayoutInflater inflater = LayoutInflater.from(mActivity);
|
||||||
ViewHolder holder;
|
return new ViewHolder(inflater.inflate(R.layout.import_keys_list_item, parent, false));
|
||||||
if (convertView == null) {
|
}
|
||||||
holder = new ViewHolder();
|
|
||||||
convertView = mInflater.inflate(R.layout.import_keys_list_item, null);
|
|
||||||
holder.mainUserId = (TextView) convertView.findViewById(R.id.import_item_user_id);
|
|
||||||
holder.mainUserIdRest = (TextView) convertView.findViewById(R.id.import_item_user_id_email);
|
|
||||||
holder.keyId = (TextView) convertView.findViewById(R.id.import_item_key_id);
|
|
||||||
holder.fingerprint = (TextView) convertView.findViewById(R.id.import_item_fingerprint);
|
|
||||||
holder.algorithm = (TextView) convertView.findViewById(R.id.import_item_algorithm);
|
|
||||||
holder.status = (ImageView) convertView.findViewById(R.id.import_item_status);
|
|
||||||
holder.userIdsDivider = convertView.findViewById(R.id.import_item_status_divider);
|
|
||||||
holder.userIdsList = (LinearLayout) convertView.findViewById(R.id.import_item_user_ids_list);
|
|
||||||
holder.checkBox = (CheckBox) convertView.findViewById(R.id.import_item_selected);
|
|
||||||
convertView.setTag(holder);
|
|
||||||
} else {
|
|
||||||
holder = (ViewHolder) convertView.getTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
// main user id
|
@Override
|
||||||
String userId = entry.getUserIds().get(0);
|
public void onBindViewHolder(ViewHolder holder, final int position) {
|
||||||
OpenPgpUtils.UserId userIdSplit = KeyRing.splitUserId(userId);
|
final ImportKeysListItemBinding b = holder.b;
|
||||||
|
final ImportKeysListEntry entry = mData.get(position);
|
||||||
|
b.setEntry(entry);
|
||||||
|
|
||||||
// name
|
final KeyState keyState = mKeyStates[position];
|
||||||
if (userIdSplit.name != null) {
|
|
||||||
// show red user id if it is a secret key
|
|
||||||
if (entry.isSecretKey()) {
|
|
||||||
holder.mainUserId.setText(mActivity.getString(R.string.secret_key)
|
|
||||||
+ " " + userIdSplit.name);
|
|
||||||
} else {
|
|
||||||
holder.mainUserId.setText(highlighter.highlight(userIdSplit.name));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
holder.mainUserId.setText(R.string.user_id_no_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// email
|
b.card.setOnClickListener(new OnClickListener() {
|
||||||
if (userIdSplit.email != null) {
|
@Override
|
||||||
holder.mainUserIdRest.setVisibility(View.VISIBLE);
|
public void onClick(View v) {
|
||||||
holder.mainUserIdRest.setText(highlighter.highlight(userIdSplit.email));
|
if (!keyState.mDownloaded) {
|
||||||
} else {
|
mCurrent = position;
|
||||||
holder.mainUserIdRest.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.keyId.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix(getContext(), entry.getKeyIdHex()));
|
getKeyWithProgress(position, entry, true);
|
||||||
|
|
||||||
// don't show full fingerprint on key import
|
|
||||||
holder.fingerprint.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
if (entry.getAlgorithm() != null) {
|
|
||||||
holder.algorithm.setText(entry.getAlgorithm());
|
|
||||||
holder.algorithm.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
holder.algorithm.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.isRevoked()) {
|
|
||||||
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.key_flag_gray);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.isRevoked() || entry.isExpired()) {
|
|
||||||
holder.status.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
// no more space for algorithm display
|
|
||||||
holder.algorithm.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (entry.isSecretKey()) {
|
|
||||||
holder.mainUserId.setTextColor(Color.RED);
|
|
||||||
} else {
|
|
||||||
holder.mainUserId.setTextColor(FormattingUtils.getColorFromAttr(mActivity, R.attr.colorText));
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.mainUserIdRest.setTextColor(FormattingUtils.getColorFromAttr(mActivity, R.attr.colorText));
|
|
||||||
holder.keyId.setTextColor(FormattingUtils.getColorFromAttr(mActivity, R.attr.colorText));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.getUserIds().size() == 1) {
|
|
||||||
holder.userIdsList.setVisibility(View.GONE);
|
|
||||||
holder.userIdsDivider.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
holder.userIdsList.setVisibility(View.VISIBLE);
|
|
||||||
holder.userIdsDivider.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
// destroyLoader view from holder
|
|
||||||
holder.userIdsList.removeAllViews();
|
|
||||||
|
|
||||||
// we want conventional gpg UserIDs first, then Keybase ”proofs”
|
|
||||||
HashMap<String, HashSet<String>> mergedUserIds = entry.getMergedUserIds();
|
|
||||||
ArrayList<Map.Entry<String, HashSet<String>>> sortedIds = new ArrayList<Map.Entry<String, HashSet<String>>>(mergedUserIds.entrySet());
|
|
||||||
java.util.Collections.sort(sortedIds, new java.util.Comparator<Map.Entry<String, HashSet<String>>>() {
|
|
||||||
@Override
|
|
||||||
public int compare(Map.Entry<String, HashSet<String>> entry1, Map.Entry<String, HashSet<String>> entry2) {
|
|
||||||
|
|
||||||
// sort keybase UserIds after non-Keybase
|
|
||||||
boolean e1IsKeybase = entry1.getKey().contains(":");
|
|
||||||
boolean e2IsKeybase = entry2.getKey().contains(":");
|
|
||||||
if (e1IsKeybase != e2IsKeybase) {
|
|
||||||
return (e1IsKeybase) ? 1 : -1;
|
|
||||||
}
|
|
||||||
return entry1.getKey().compareTo(entry2.getKey());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for (Map.Entry<String, HashSet<String>> pair : sortedIds) {
|
|
||||||
String cUserId = pair.getKey();
|
|
||||||
HashSet<String> cEmails = pair.getValue();
|
|
||||||
|
|
||||||
TextView uidView = (TextView) mInflater.inflate(
|
|
||||||
R.layout.import_keys_list_entry_user_id, null);
|
|
||||||
uidView.setText(highlighter.highlight(cUserId));
|
|
||||||
uidView.setPadding(0, 0, FormattingUtils.dpToPx(getContext(), 8), 0);
|
|
||||||
|
|
||||||
if (entry.isRevoked() || entry.isExpired()) {
|
|
||||||
uidView.setTextColor(getContext().getResources().getColor(R.color.key_flag_gray));
|
|
||||||
} else {
|
} else {
|
||||||
uidView.setTextColor(FormattingUtils.getColorFromAttr(getContext(), R.attr.colorText));
|
changeShowed(position, !keyState.mShowed);
|
||||||
}
|
|
||||||
|
|
||||||
holder.userIdsList.addView(uidView);
|
|
||||||
|
|
||||||
for (String email : cEmails) {
|
|
||||||
TextView emailView = (TextView) mInflater.inflate(
|
|
||||||
R.layout.import_keys_list_entry_user_id, null);
|
|
||||||
emailView.setPadding(
|
|
||||||
FormattingUtils.dpToPx(getContext(), 16), 0,
|
|
||||||
FormattingUtils.dpToPx(getContext(), 8), 0);
|
|
||||||
emailView.setText(highlighter.highlight(email));
|
|
||||||
|
|
||||||
if (entry.isRevoked() || entry.isExpired()) {
|
|
||||||
emailView.setTextColor(getContext().getResources().getColor(R.color.key_flag_gray));
|
|
||||||
} else {
|
|
||||||
emailView.setTextColor(FormattingUtils.getColorFromAttr(getContext(), R.attr.colorText));
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.userIdsList.addView(emailView);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
b.extra.importKey.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
getKeyWithProgress(position, entry, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
b.extra.showKey.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
long keyId = KeyFormattingUtils.convertKeyIdHexToKeyId(entry.getKeyIdHex());
|
||||||
|
Intent intent = new Intent(mActivity, ViewKeyActivity.class);
|
||||||
|
intent.setData(KeyRings.buildGenericKeyRingUri(keyId));
|
||||||
|
mActivity.startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
b.extraContainer.setVisibility(keyState.mShowed ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
b.progress.setVisibility(keyState.mProgress ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return mData != null ? mData.size() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getKeyWithProgress(int position, ImportKeysListEntry entry, boolean skipSave) {
|
||||||
|
changeProgress(position, true);
|
||||||
|
getKey(entry, skipSave);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getKey(ImportKeysListEntry entry, boolean skipSave) {
|
||||||
|
ImportKeyringParcel inputParcel = prepareKeyOperation(entry, skipSave);
|
||||||
|
ImportKeysResultListener listener = skipSave ? this : mListener;
|
||||||
|
ImportKeysOperationCallback cb = new ImportKeysOperationCallback(listener, inputParcel);
|
||||||
|
CryptoOperationHelper opHelper = new CryptoOperationHelper<>(1, mActivity, cb, null);
|
||||||
|
opHelper.cryptoOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImportKeyringParcel prepareKeyOperation(ImportKeysListEntry entry, boolean skipSave) {
|
||||||
|
ArrayList<ParcelableKeyRing> keysList = null;
|
||||||
|
ParcelableHkpKeyserver keyserver = null;
|
||||||
|
|
||||||
|
ParcelableKeyRing keyRing = entry.getParcelableKeyRing();
|
||||||
|
if (keyRing.mBytes != null) {
|
||||||
|
// 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.
|
||||||
|
try {
|
||||||
|
// We parcel this iteratively into a file - anything we can
|
||||||
|
// display here, we should be able to import.
|
||||||
|
ParcelableFileCache<ParcelableKeyRing> cache =
|
||||||
|
new ParcelableFileCache<>(mActivity, ImportOperation.CACHE_FILE_NAME);
|
||||||
|
cache.writeCache(keyRing);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(Constants.TAG, "Problem writing cache file", e);
|
||||||
|
Notify.create(mActivity, "Problem writing cache file!", Notify.Style.ERROR).show();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
keysList = new ArrayList<>();
|
||||||
|
keysList.add(keyRing);
|
||||||
|
keyserver = entry.getKeyserver();
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.checkBox.setChecked(entry.isSelected());
|
return new ImportKeyringParcel(keysList, keyserver, skipSave);
|
||||||
|
}
|
||||||
|
|
||||||
return convertView;
|
@Override
|
||||||
|
public void handleResult(ImportKeyResult result) {
|
||||||
|
boolean resultStatus = result.success();
|
||||||
|
Log.e(Constants.TAG, "getKey result: " + resultStatus);
|
||||||
|
if (resultStatus) {
|
||||||
|
ArrayList<CanonicalizedKeyRing> canKeyRings = result.mCanonicalizedKeyRings;
|
||||||
|
if (canKeyRings.size() == 1) {
|
||||||
|
CanonicalizedKeyRing keyRing = canKeyRings.get(0);
|
||||||
|
Log.e(Constants.TAG, "Key ID: " + keyRing.getMasterKeyId() +
|
||||||
|
"| isRev: " + keyRing.isRevoked() + "| isExp: " + keyRing.isExpired());
|
||||||
|
|
||||||
|
ImportKeysListEntry entry = mData.get(mCurrent);
|
||||||
|
entry.setUpdated(result.isOkUpdated());
|
||||||
|
|
||||||
|
mergeEntryWithKey(entry, keyRing);
|
||||||
|
|
||||||
|
mKeyStates[mCurrent].mDownloaded = true;
|
||||||
|
changeShowed(mCurrent, true);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("getKey retrieved more than one key ("
|
||||||
|
+ canKeyRings.size() + ")");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.createNotify(mActivity).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
changeProgress(mCurrent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mergeEntryWithKey(ImportKeysListEntry entry, CanonicalizedKeyRing keyRing) {
|
||||||
|
entry.setRevoked(keyRing.isRevoked());
|
||||||
|
entry.setExpired(keyRing.isExpired());
|
||||||
|
|
||||||
|
Date expectedDate = entry.getDate();
|
||||||
|
Date creationDate = keyRing.getCreationDate();
|
||||||
|
if (expectedDate == null) {
|
||||||
|
entry.setDate(creationDate);
|
||||||
|
} else if (!expectedDate.equals(creationDate)) {
|
||||||
|
throw new AssertionError("Creation date doesn't match the expected one");
|
||||||
|
}
|
||||||
|
entry.setKeyId(keyRing.getMasterKeyId());
|
||||||
|
|
||||||
|
ArrayList<String> realUserIdsPlusKeybase = keyRing.getUnorderedUserIds();
|
||||||
|
realUserIdsPlusKeybase.addAll(entry.getKeybaseUserIds());
|
||||||
|
entry.setUserIds(realUserIdsPlusKeybase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class KeyState {
|
||||||
|
boolean mAlreadyPresent = false;
|
||||||
|
boolean mVerified = false;
|
||||||
|
|
||||||
|
boolean mProgress = false;
|
||||||
|
boolean mDownloaded = false;
|
||||||
|
boolean mShowed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void changeShowed(int position, boolean showed) {
|
||||||
|
KeyState keyState = mKeyStates[position];
|
||||||
|
keyState.mShowed = showed;
|
||||||
|
notifyItemChanged(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void changeProgress(int position, boolean progress) {
|
||||||
|
KeyState keyState = mKeyStates[position];
|
||||||
|
keyState.mProgress = progress;
|
||||||
|
notifyItemChanged(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+87
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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.bindings;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.databinding.BindingAdapter;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.text.format.DateFormat;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.Highlighter;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class ImportKeysBindings {
|
||||||
|
|
||||||
|
@BindingAdapter({"keyUserId", "keySecret", "keyRevokedOrExpired", "query"})
|
||||||
|
public static void setUserId(TextView textView, CharSequence userId, boolean secret,
|
||||||
|
boolean revokedOrExpired, String query) {
|
||||||
|
|
||||||
|
Context context = textView.getContext();
|
||||||
|
Resources resources = context.getResources();
|
||||||
|
|
||||||
|
if (userId == null) {
|
||||||
|
userId = resources.getString(R.string.user_id_no_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secret) {
|
||||||
|
userId = resources.getString(R.string.secret_key) + " " + userId;
|
||||||
|
} else {
|
||||||
|
Highlighter highlighter = ImportKeysBindingsUtils.getHighlighter(context, query);
|
||||||
|
userId = highlighter.highlight(userId);
|
||||||
|
}
|
||||||
|
textView.setText(userId);
|
||||||
|
textView.setTextColor(ImportKeysBindingsUtils.getColor(context, revokedOrExpired));
|
||||||
|
|
||||||
|
if (secret) {
|
||||||
|
textView.setTextColor(Color.RED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@BindingAdapter({"keyUserEmail", "keyRevokedOrExpired", "query"})
|
||||||
|
public static void setUserEmail(TextView textView, CharSequence userEmail,
|
||||||
|
boolean revokedOrExpired, String query) {
|
||||||
|
|
||||||
|
Context context = textView.getContext();
|
||||||
|
|
||||||
|
if (userEmail == null) {
|
||||||
|
userEmail = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Highlighter highlighter = ImportKeysBindingsUtils.getHighlighter(context, query);
|
||||||
|
textView.setText(highlighter.highlight(userEmail));
|
||||||
|
textView.setTextColor(ImportKeysBindingsUtils.getColor(context, revokedOrExpired));
|
||||||
|
}
|
||||||
|
|
||||||
|
@BindingAdapter({"keyCreation", "keyRevokedOrExpired"})
|
||||||
|
public static void setCreation(TextView textView, Date creationDate, boolean revokedOrExpired) {
|
||||||
|
Context context = textView.getContext();
|
||||||
|
|
||||||
|
String text = "";
|
||||||
|
if (creationDate != null) {
|
||||||
|
text = DateFormat.getDateFormat(context).format(creationDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
textView.setText(text);
|
||||||
|
textView.setTextColor(ImportKeysBindingsUtils.getColor(context, revokedOrExpired));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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.bindings;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.Highlighter;
|
||||||
|
import org.sufficientlysecure.keychain.util.LruCache;
|
||||||
|
|
||||||
|
public class ImportKeysBindingsUtils {
|
||||||
|
|
||||||
|
private static LruCache<String, Highlighter> highlighterCache = new LruCache<>(1);
|
||||||
|
|
||||||
|
public static Highlighter getHighlighter(Context context, String query) {
|
||||||
|
Highlighter highlighter = highlighterCache.get(query);
|
||||||
|
if (highlighter == null) {
|
||||||
|
highlighter = new Highlighter(context, query);
|
||||||
|
highlighterCache.put(query, highlighter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return highlighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getColor(Context context, boolean revokedOrExpired) {
|
||||||
|
if (revokedOrExpired) {
|
||||||
|
return context.getResources().getColor(R.color.key_flag_gray);
|
||||||
|
} else {
|
||||||
|
return FormattingUtils.getColorFromAttr(context, R.attr.colorText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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.bindings;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.databinding.BindingAdapter;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.Highlighter;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ImportKeysExtraBindings {
|
||||||
|
|
||||||
|
@BindingAdapter({"keyRevoked", "keyExpired"})
|
||||||
|
public static void setStatus(ImageView imageView, boolean revoked, boolean expired) {
|
||||||
|
Context context = imageView.getContext();
|
||||||
|
|
||||||
|
if (revoked) {
|
||||||
|
KeyFormattingUtils.setStatusImage(context, imageView, null,
|
||||||
|
KeyFormattingUtils.State.REVOKED, R.color.key_flag_gray);
|
||||||
|
} else if (expired) {
|
||||||
|
KeyFormattingUtils.setStatusImage(context, imageView, null,
|
||||||
|
KeyFormattingUtils.State.EXPIRED, R.color.key_flag_gray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@BindingAdapter({"keyId"})
|
||||||
|
public static void setKeyId(TextView textView, String keyId) {
|
||||||
|
Context context = textView.getContext();
|
||||||
|
String text;
|
||||||
|
if (keyId != null) {
|
||||||
|
text = KeyFormattingUtils.beautifyKeyId(keyId);
|
||||||
|
} else {
|
||||||
|
Resources resources = context.getResources();
|
||||||
|
text = resources.getString(R.string.unknown);
|
||||||
|
}
|
||||||
|
textView.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BindingAdapter({"keyUserIds", "query"})
|
||||||
|
public static void setUserIds(LinearLayout linearLayout, ArrayList userIds, String query) {
|
||||||
|
|
||||||
|
linearLayout.removeAllViews();
|
||||||
|
|
||||||
|
if (userIds != null) {
|
||||||
|
Context context = linearLayout.getContext();
|
||||||
|
Highlighter highlighter = ImportKeysBindingsUtils.getHighlighter(context, query);
|
||||||
|
|
||||||
|
ArrayList<Map.Entry<String, HashSet<String>>> uIds = userIds;
|
||||||
|
for (Map.Entry<String, HashSet<String>> pair : uIds) {
|
||||||
|
String name = pair.getKey();
|
||||||
|
HashSet<String> emails = pair.getValue();
|
||||||
|
|
||||||
|
LayoutInflater inflater = LayoutInflater.from(context);
|
||||||
|
|
||||||
|
TextView uidView = (TextView) inflater.inflate(
|
||||||
|
R.layout.import_keys_list_entry_user_id, null);
|
||||||
|
uidView.setText(highlighter.highlight(name));
|
||||||
|
uidView.setPadding(0, 0, FormattingUtils.dpToPx(context, 8), 0);
|
||||||
|
uidView.setTextColor(FormattingUtils.getColorFromAttr(context, R.attr.colorText));
|
||||||
|
linearLayout.addView(uidView);
|
||||||
|
|
||||||
|
for (String email : emails) {
|
||||||
|
TextView emailView = (TextView) inflater.inflate(
|
||||||
|
R.layout.import_keys_list_entry_user_id, null);
|
||||||
|
emailView.setPadding(
|
||||||
|
FormattingUtils.dpToPx(context, 16), 0,
|
||||||
|
FormattingUtils.dpToPx(context, 8), 0);
|
||||||
|
emailView.setText(highlighter.highlight(email));
|
||||||
|
emailView.setTextColor(FormattingUtils.getColorFromAttr(context, R.attr.colorText));
|
||||||
|
linearLayout.addView(emailView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ public class Highlighter {
|
|||||||
mQuery = query;
|
mQuery = query;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Spannable highlight(String text) {
|
public Spannable highlight(CharSequence text) {
|
||||||
Spannable highlight = Spannable.Factory.getInstance().newSpannable(text);
|
Spannable highlight = Spannable.Factory.getInstance().newSpannable(text);
|
||||||
|
|
||||||
if (mQuery == null) {
|
if (mQuery == null) {
|
||||||
|
|||||||
+20
-10
@@ -30,13 +30,13 @@ import android.widget.ImageView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.ViewAnimator;
|
import android.widget.ViewAnimator;
|
||||||
|
|
||||||
import org.openintents.openpgp.OpenPgpDecryptionResult;
|
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
|
||||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||||
import org.bouncycastle.asn1.nist.NISTNamedCurves;
|
import org.bouncycastle.asn1.nist.NISTNamedCurves;
|
||||||
import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
|
import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
|
||||||
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
|
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
|
||||||
import org.bouncycastle.util.encoders.Hex;
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
|
import org.openintents.openpgp.OpenPgpDecryptionResult;
|
||||||
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
import org.openintents.openpgp.util.OpenPgpUtils;
|
import org.openintents.openpgp.util.OpenPgpUtils;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
@@ -46,6 +46,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
|
|||||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
|
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.security.DigestException;
|
import java.security.DigestException;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
@@ -99,7 +100,7 @@ public class KeyFormattingUtils {
|
|||||||
|
|
||||||
default: {
|
default: {
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
algorithmStr = context.getResources().getString(R.string.unknown_algorithm);
|
algorithmStr = context.getResources().getString(R.string.unknown);
|
||||||
} else {
|
} else {
|
||||||
algorithmStr = "unknown";
|
algorithmStr = "unknown";
|
||||||
}
|
}
|
||||||
@@ -154,7 +155,7 @@ public class KeyFormattingUtils {
|
|||||||
|
|
||||||
default: {
|
default: {
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
algorithmStr = context.getResources().getString(R.string.unknown_algorithm);
|
algorithmStr = context.getResources().getString(R.string.unknown);
|
||||||
} else {
|
} else {
|
||||||
algorithmStr = "unknown";
|
algorithmStr = "unknown";
|
||||||
}
|
}
|
||||||
@@ -189,7 +190,7 @@ public class KeyFormattingUtils {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
return context.getResources().getString(R.string.unknown_algorithm);
|
return context.getResources().getString(R.string.unknown);
|
||||||
} else {
|
} else {
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
@@ -208,7 +209,7 @@ public class KeyFormattingUtils {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
return context.getResources().getString(R.string.unknown_algorithm);
|
return context.getResources().getString(R.string.unknown);
|
||||||
} else {
|
} else {
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
@@ -272,6 +273,10 @@ public class KeyFormattingUtils {
|
|||||||
return hexString;
|
return hexString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long convertKeyIdHexToKeyId(String hex) {
|
||||||
|
return new BigInteger(hex.substring(2), 16).longValue();
|
||||||
|
}
|
||||||
|
|
||||||
public static long convertFingerprintToKeyId(byte[] fingerprint) {
|
public static long convertFingerprintToKeyId(byte[] fingerprint) {
|
||||||
return ByteBuffer.wrap(fingerprint, 12, 8).getLong();
|
return ByteBuffer.wrap(fingerprint, 12, 8).getLong();
|
||||||
}
|
}
|
||||||
@@ -312,12 +317,12 @@ public class KeyFormattingUtils {
|
|||||||
return beautifyKeyId(convertKeyIdToHex(keyId));
|
return beautifyKeyId(convertKeyIdToHex(keyId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String beautifyKeyIdWithPrefix(Context context, String idHex) {
|
public static String beautifyKeyIdWithPrefix(String idHex) {
|
||||||
return "Key ID: " + beautifyKeyId(idHex);
|
return "Key ID: " + beautifyKeyId(idHex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String beautifyKeyIdWithPrefix(Context context, long keyId) {
|
public static String beautifyKeyIdWithPrefix(long keyId) {
|
||||||
return beautifyKeyIdWithPrefix(context, convertKeyIdToHex(keyId));
|
return beautifyKeyIdWithPrefix(convertKeyIdToHex(keyId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SpannableStringBuilder colorizeFingerprint(String fingerprint) {
|
public static SpannableStringBuilder colorizeFingerprint(String fingerprint) {
|
||||||
@@ -434,14 +439,19 @@ public class KeyFormattingUtils {
|
|||||||
|
|
||||||
public interface StatusHolder {
|
public interface StatusHolder {
|
||||||
ImageView getEncryptionStatusIcon();
|
ImageView getEncryptionStatusIcon();
|
||||||
|
|
||||||
TextView getEncryptionStatusText();
|
TextView getEncryptionStatusText();
|
||||||
|
|
||||||
ImageView getSignatureStatusIcon();
|
ImageView getSignatureStatusIcon();
|
||||||
|
|
||||||
TextView getSignatureStatusText();
|
TextView getSignatureStatusText();
|
||||||
|
|
||||||
View getSignatureLayout();
|
View getSignatureLayout();
|
||||||
|
|
||||||
TextView getSignatureUserName();
|
TextView getSignatureUserName();
|
||||||
|
|
||||||
TextView getSignatureUserEmail();
|
TextView getSignatureUserEmail();
|
||||||
|
|
||||||
ViewAnimator getSignatureAction();
|
ViewAnimator getSignatureAction();
|
||||||
|
|
||||||
boolean hasEncrypt();
|
boolean hasEncrypt();
|
||||||
@@ -450,7 +460,7 @@ public class KeyFormattingUtils {
|
|||||||
|
|
||||||
@SuppressWarnings("deprecation") // context.getDrawable is api lvl 21, need to use deprecated
|
@SuppressWarnings("deprecation") // context.getDrawable is api lvl 21, need to use deprecated
|
||||||
public static void setStatus(Resources resources, StatusHolder holder, DecryptVerifyResult result,
|
public static void setStatus(Resources resources, StatusHolder holder, DecryptVerifyResult result,
|
||||||
boolean processingkeyLookup) {
|
boolean processingkeyLookup) {
|
||||||
|
|
||||||
if (holder.hasEncrypt()) {
|
if (holder.hasEncrypt()) {
|
||||||
OpenPgpDecryptionResult decryptionResult = result.getDecryptionResult();
|
OpenPgpDecryptionResult decryptionResult = result.getDecryptionResult();
|
||||||
|
|||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
package org.sufficientlysecure.keychain.ui.util;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
|
||||||
|
|
||||||
|
public class PermissionsUtil {
|
||||||
|
|
||||||
|
private static final int PERMISSION_READ_EXTERNAL_STORAGE = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request READ_EXTERNAL_STORAGE permission on Android >= 6.0 to read content from "file" Uris.
|
||||||
|
* <p/>
|
||||||
|
* This method returns true on Android < 6, or if permission is already granted. It
|
||||||
|
* requests the permission and returns false otherwise.
|
||||||
|
* <p/>
|
||||||
|
* see https://commonsware.com/blog/2015/10/07/runtime-permissions-files-action-send.html
|
||||||
|
*/
|
||||||
|
@SuppressLint("NewApi") // Api level is checked in checkReadPermission
|
||||||
|
public static boolean checkAndRequestReadPermission(Activity activity, Uri uri) {
|
||||||
|
boolean result = checkReadPermission(activity, uri);
|
||||||
|
if (!result) {
|
||||||
|
activity.requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
|
||||||
|
PERMISSION_READ_EXTERNAL_STORAGE);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkAndRequestReadPermission(Fragment fragment, Uri uri) {
|
||||||
|
boolean result = checkReadPermission(fragment.getContext(), uri);
|
||||||
|
if (!result) {
|
||||||
|
fragment.requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
|
||||||
|
PERMISSION_READ_EXTERNAL_STORAGE);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkReadPermission(Context context, Uri uri) {
|
||||||
|
if (!ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional check due to:
|
||||||
|
// https://commonsware.com/blog/2015/11/09/you-cannot-hold-nonexistent-permissions.html
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
|
== PackageManager.PERMISSION_GRANTED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkReadPermissionResult(Context context,
|
||||||
|
int requestCode,
|
||||||
|
int[] grantResults) {
|
||||||
|
|
||||||
|
if (requestCode != PERMISSION_READ_EXTERNAL_STORAGE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean permissionWasGranted = grantResults.length > 0
|
||||||
|
&& grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||||
|
|
||||||
|
if (permissionWasGranted) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, R.string.error_denied_storage_permission, Toast.LENGTH_LONG)
|
||||||
|
.show();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -57,7 +57,8 @@ public class EmailKeyHelper {
|
|||||||
// Put them in a list and import
|
// Put them in a list and import
|
||||||
ArrayList<ParcelableKeyRing> keys = new ArrayList<>(entries.size());
|
ArrayList<ParcelableKeyRing> keys = new ArrayList<>(entries.size());
|
||||||
for (ImportKeysListEntry entry : entries) {
|
for (ImportKeysListEntry entry : entries) {
|
||||||
keys.add(new ParcelableKeyRing(entry.getFingerprintHex(), entry.getKeyIdHex()));
|
keys.add(new ParcelableKeyRing(entry.getFingerprintHex(), entry.getKeyIdHex(), null,
|
||||||
|
null));
|
||||||
}
|
}
|
||||||
mKeyList = keys;
|
mKeyList = keys;
|
||||||
mKeyserver = keyserver;
|
mKeyserver = keyserver;
|
||||||
@@ -97,7 +98,7 @@ public class EmailKeyHelper {
|
|||||||
Set<ImportKeysListEntry> keys = new HashSet<>();
|
Set<ImportKeysListEntry> keys = new HashSet<>();
|
||||||
try {
|
try {
|
||||||
for (ImportKeysListEntry key : keyServer.search(mail, proxy)) {
|
for (ImportKeysListEntry key : keyServer.search(mail, proxy)) {
|
||||||
if (key.isRevoked() || key.isExpired()) continue;
|
if (key.isRevokedOrExpired()) continue;
|
||||||
for (String userId : key.getUserIds()) {
|
for (String userId : key.getUserIds()) {
|
||||||
if (userId.toLowerCase().contains(mail.toLowerCase(Locale.ENGLISH))) {
|
if (userId.toLowerCase().contains(mail.toLowerCase(Locale.ENGLISH))) {
|
||||||
keys.add(key);
|
keys.add(key);
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package org.sufficientlysecure.keychain.util;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extended iterator interface, which knows the total number of its entries beforehand.
|
||||||
|
*/
|
||||||
|
public interface IteratorWithSize<E> extends Iterator<E> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total number of entries in this iterator.
|
||||||
|
*
|
||||||
|
* @return the number of entries in this iterator.
|
||||||
|
*/
|
||||||
|
int getSize();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package org.sufficientlysecure.keychain.util;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
// Source: http://stackoverflow.com/a/1953516
|
||||||
|
public class LruCache<A, B> extends LinkedHashMap<A, B> {
|
||||||
|
|
||||||
|
private final int maxEntries;
|
||||||
|
|
||||||
|
public LruCache(final int maxEntries) {
|
||||||
|
super(maxEntries + 1, 1.0f, true);
|
||||||
|
this.maxEntries = maxEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean removeEldestEntry(final Map.Entry<A, B> eldest) {
|
||||||
|
return super.size() > maxEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+2
-2
@@ -46,8 +46,8 @@ public class OkHttpClientFactory {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OkHttpClient getClientPinnedIfAvailable(URL url, Proxy proxy) throws IOException,
|
public static OkHttpClient getClientPinnedIfAvailable(URL url, Proxy proxy)
|
||||||
TlsHelper.TlsHelperException {
|
throws IOException, TlsHelper.TlsHelperException {
|
||||||
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
||||||
|
|
||||||
// don't follow any redirects for keyservers, as discussed in the security audit
|
// don't follow any redirects for keyservers, as discussed in the security audit
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@ public class OkHttpKeybaseClient implements KeybaseUrlConnectionClient {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response getUrlResponse(URL url, Proxy proxy, boolean isKeybase) throws IOException {
|
public Response getUrlResponse(URL url, Proxy proxy, boolean isKeybase) throws IOException {
|
||||||
OkHttpClient client = null;
|
OkHttpClient client;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (proxy != null) {
|
if (proxy != null) {
|
||||||
|
|||||||
+35
-32
@@ -52,12 +52,40 @@ public class ParcelableFileCache<E extends Parcelable> {
|
|||||||
mFilename = filename;
|
mFilename = filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeCache(IteratorWithSize<E> it) throws IOException {
|
public void writeCache(int numEntries, Iterator<E> it) throws IOException {
|
||||||
writeCache(it.getSize(), it);
|
DataOutputStream oos = getOutputStream();
|
||||||
|
|
||||||
|
try {
|
||||||
|
oos.writeInt(numEntries);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
writeParcelable(it.next(), oos);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
oos.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeCache(int numEntries, Iterator<E> it) throws IOException {
|
public void writeCache(E obj) throws IOException {
|
||||||
|
DataOutputStream oos = getOutputStream();
|
||||||
|
|
||||||
|
try {
|
||||||
|
oos.writeInt(1);
|
||||||
|
writeParcelable(obj, oos);
|
||||||
|
} finally {
|
||||||
|
oos.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeParcelable(E obj, DataOutputStream oos) throws IOException {
|
||||||
|
Parcel p = Parcel.obtain(); // creating empty parcel object
|
||||||
|
p.writeParcelable(obj, 0); // saving bundle as parcel
|
||||||
|
byte[] buf = p.marshall();
|
||||||
|
oos.writeInt(buf.length);
|
||||||
|
oos.write(buf);
|
||||||
|
p.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataOutputStream getOutputStream() throws IOException {
|
||||||
File cacheDir = mContext.getCacheDir();
|
File cacheDir = mContext.getCacheDir();
|
||||||
if (cacheDir == null) {
|
if (cacheDir == null) {
|
||||||
// https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU
|
// https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU
|
||||||
@@ -65,29 +93,12 @@ public class ParcelableFileCache<E extends Parcelable> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
File tempFile = new File(mContext.getCacheDir(), mFilename);
|
File tempFile = new File(mContext.getCacheDir(), mFilename);
|
||||||
|
return new DataOutputStream(new FileOutputStream(tempFile));
|
||||||
|
|
||||||
DataOutputStream oos = new DataOutputStream(new FileOutputStream(tempFile));
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads from cache file and deletes it afterward. Convenience function for readCache(boolean).
|
* Reads from cache file and deletes it afterward. Convenience function for readCache(boolean).
|
||||||
|
*
|
||||||
* @return an IteratorWithSize object containing entries read from the cache file
|
* @return an IteratorWithSize object containing entries read from the cache file
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
@@ -97,10 +108,11 @@ public class ParcelableFileCache<E extends Parcelable> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads entries from a cache file and returns an IteratorWithSize object containing the entries
|
* Reads entries from a cache file and returns an IteratorWithSize object containing the entries
|
||||||
|
*
|
||||||
* @param deleteAfterRead if true, the cache file will be deleted after being read
|
* @param deleteAfterRead if true, the cache file will be deleted after being read
|
||||||
* @return an IteratorWithSize object containing entries read from the cache file
|
* @return an IteratorWithSize object containing entries read from the cache file
|
||||||
* @throws IOException if cache directory/parcel import file does not exist, or a read error
|
* @throws IOException if cache directory/parcel import file does not exist, or a read error
|
||||||
* occurs
|
* occurs
|
||||||
*/
|
*/
|
||||||
public IteratorWithSize<E> readCache(final boolean deleteAfterRead) throws IOException {
|
public IteratorWithSize<E> readCache(final boolean deleteAfterRead) throws IOException {
|
||||||
|
|
||||||
@@ -205,7 +217,6 @@ public class ParcelableFileCache<E extends Parcelable> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean delete() throws IOException {
|
public boolean delete() throws IOException {
|
||||||
|
|
||||||
File cacheDir = mContext.getCacheDir();
|
File cacheDir = mContext.getCacheDir();
|
||||||
if (cacheDir == null) {
|
if (cacheDir == null) {
|
||||||
// https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU
|
// https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU
|
||||||
@@ -216,12 +227,4 @@ public class ParcelableFileCache<E extends Parcelable> {
|
|||||||
return tempFile.delete();
|
return tempFile.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** As the name implies, this is an extended iterator interface, which
|
|
||||||
* knows the total number of its entries beforehand.
|
|
||||||
*/
|
|
||||||
public static interface IteratorWithSize<E> extends Iterator<E> {
|
|
||||||
/** Returns the number of total entries in this iterator. */
|
|
||||||
int getSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 227 B |
Binary file not shown.
|
After Width: | Height: | Size: 157 B |
Binary file not shown.
|
After Width: | Height: | Size: 244 B |
Binary file not shown.
|
After Width: | Height: | Size: 340 B |
Binary file not shown.
|
After Width: | Height: | Size: 460 B |
@@ -1,8 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<include
|
<include
|
||||||
android:id="@+id/toolbar_include"
|
android:id="@+id/toolbar_include"
|
||||||
@@ -13,79 +12,26 @@
|
|||||||
https://medium.com/@ngdelamo/using-drawerlayout-the-material-way-i-716bba2b5705
|
https://medium.com/@ngdelamo/using-drawerlayout-the-material-way-i-716bba2b5705
|
||||||
-->
|
-->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_below="@id/toolbar_include"
|
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
android:layout_marginTop="@dimen/minus_statusbar_height"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:layout_below="@id/toolbar_include"
|
||||||
|
android:layout_marginTop="@dimen/minus_statusbar_height"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<include layout="@layout/notify_area" />
|
<include layout="@layout/notify_area" />
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:id="@+id/import_keys_top_layout"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/import_keys_top_container"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="64dp"
|
|
||||||
android:orientation="vertical" />
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dip"
|
|
||||||
android:background="?android:attr/listDivider" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/import_keys_list_container"
|
android:id="@+id/import_keys_list_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_weight="1" />
|
android:layout_weight="1" />
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/import_footer"
|
android:id="@+id/import_snackbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content" />
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:id="@+id/import_divider"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dip"
|
|
||||||
android:background="?android:attr/listDivider" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/import_import"
|
|
||||||
android:paddingLeft="8dp"
|
|
||||||
android:paddingRight="8dp"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginLeft="16dp"
|
|
||||||
android:layout_marginRight="16dp"
|
|
||||||
android:layout_below="@id/import_divider"
|
|
||||||
android:text="@string/import_import"
|
|
||||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
|
||||||
android:drawableRight="@drawable/ic_file_download_grey_24dp"
|
|
||||||
android:drawablePadding="8dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:clickable="true"
|
|
||||||
android:background="?android:selectableItemBackground" />
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:id="@+id/import_snackbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignBottom="@id/import_import" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:paddingLeft="8dp"
|
|
||||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<AutoCompleteTextView
|
|
||||||
android:id="@+id/cloud_import_server_query"
|
|
||||||
android:layout_width="0dip"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="top|left"
|
|
||||||
android:hint="@string/hint_cloud_search_hint"
|
|
||||||
android:imeOptions="actionSearch"
|
|
||||||
android:inputType="textNoSuggestions"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:lines="1"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:minLines="1"
|
|
||||||
android:layout_gravity="center_vertical" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/cloud_import_server_search"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:padding="8dp"
|
|
||||||
android:src="@drawable/ic_search_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/cloud_import_server_config_button"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:padding="8dp"
|
|
||||||
android:src="@drawable/ic_settings_grey_24dp"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:background="?android:selectableItemBackground" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@android:id/text1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="56dp"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItemSmall" />
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/import_keys_file_browse"
|
|
||||||
android:paddingLeft="8dp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
|
||||||
android:clickable="true"
|
|
||||||
android:background="?android:selectableItemBackground"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:paddingLeft="8dp"
|
|
||||||
android:paddingRight="8dp"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:layout_width="0dip"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text="@string/filemanager_title_open"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:drawableRight="@drawable/ic_folder_grey_24dp"
|
|
||||||
android:drawablePadding="8dp"
|
|
||||||
android:gravity="center_vertical" />
|
|
||||||
|
|
||||||
<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/import_clipboard_button"
|
|
||||||
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"
|
|
||||||
android:background="?android:selectableItemBackground" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/import_qrcode_text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingLeft="16dp"
|
|
||||||
android:paddingRight="16dp"
|
|
||||||
android:paddingTop="8dp"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/import_qrcode_progress"
|
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingLeft="16dp"
|
|
||||||
android:paddingRight="16dp"
|
|
||||||
android:progress="0"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
<import type="android.view.View" alias="V" />
|
||||||
|
|
||||||
|
<variable name="nonInteractive" type="boolean" />
|
||||||
|
<variable name="number" type="int" />
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
card_view:cardCornerRadius="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:paddingLeft="8dp"
|
||||||
|
android:paddingRight="8dp"
|
||||||
|
android:paddingTop="24dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
android:paddingLeft="8dp"
|
||||||
|
android:paddingRight="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@{@string/import_found_keys(number)}"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:visibility="@{nonInteractive ? V.GONE : V.VISIBLE}">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/import_keys"
|
||||||
|
style="@style/CardViewActionButton"
|
||||||
|
android:text="@string/btn_import_keys" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/list_keys"
|
||||||
|
style="@style/CardViewActionButton"
|
||||||
|
android:text="@string/btn_view_list" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</android.support.v7.widget.CardView>
|
||||||
|
|
||||||
|
</layout>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
<import type="android.view.View" alias="V" />
|
||||||
|
<import type="org.sufficientlysecure.keychain.ui.ImportKeysListFragment" alias="I" />
|
||||||
|
|
||||||
|
<variable name="status" type="int" />
|
||||||
|
<variable name="number" type="int" />
|
||||||
|
<variable name="advanced" type="boolean" />
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/md_grey_100">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:visibility="@{status == I.STATUS_LOADING ? V.VISIBLE : V.GONE}" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:text="@string/error_nothing"
|
||||||
|
android:visibility="@{status == I.STATUS_FIRST ? V.VISIBLE : V.GONE}" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:text="@string/error_nothing_import"
|
||||||
|
android:visibility="@{status == I.STATUS_EMPTY ? V.VISIBLE : V.GONE}" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="8dp"
|
||||||
|
android:paddingRight="8dp"
|
||||||
|
android:visibility="@{status == I.STATUS_LOADED ? V.VISIBLE : V.GONE}">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="@{advanced || (number == 1) ? V.GONE : V.VISIBLE}">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/basic"
|
||||||
|
layout="@layout/import_keys_list_basic_item"
|
||||||
|
app:number="@{number}" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:id="@+id/recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
android:visibility="@{advanced || (number == 1) ? V.VISIBLE : V.GONE}" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</layout>
|
||||||
@@ -1,114 +1,98 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:custom="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:paddingLeft="8dp"
|
|
||||||
android:paddingRight="16dp"
|
|
||||||
android:paddingTop="4dp"
|
|
||||||
android:paddingBottom="4dp">
|
|
||||||
|
|
||||||
<CheckBox
|
<data>
|
||||||
android:id="@+id/import_item_selected"
|
<import type="android.view.View" alias="V" />
|
||||||
android:layout_width="wrap_content"
|
<import type="org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry" />
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:paddingRight="8dp"
|
|
||||||
android:paddingTop="2dp"
|
|
||||||
android:gravity="top|center"
|
|
||||||
android:clickable="false"
|
|
||||||
android:focusable="false"
|
|
||||||
android:focusableInTouchMode="false" />
|
|
||||||
<!-- focusable and clickable MUST be false to handle click and longClick in ListView Activity -->
|
|
||||||
|
|
||||||
<LinearLayout
|
<variable name="nonInteractive" type="boolean" />
|
||||||
|
<variable name="entry" type="ImportKeysListEntry" />
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:layout_marginBottom="4dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
card_view:cardCornerRadius="2dp">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<RelativeLayout
|
||||||
android:layout_width="0dip"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:layout_weight="1"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/import_item_user_id"
|
android:id="@+id/container"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Alice"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/import_item_user_id_email"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="alice@example.com"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/import_item_key_id"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Key ID: abcd abcd abcd abcd"
|
android:layout_alignParentLeft="true"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:paddingTop="16dp">
|
||||||
|
|
||||||
</LinearLayout>
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
app:keyRevokedOrExpired="@{entry.revokedOrExpired}"
|
||||||
|
app:keySecret="@{entry.secretKey}"
|
||||||
|
app:keyUserId="@{entry.primaryUserId.name}"
|
||||||
|
app:query="@{entry.query}" />
|
||||||
|
|
||||||
<LinearLayout
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="right"
|
android:layout_marginTop="4dp"
|
||||||
android:orientation="vertical"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:paddingLeft="4dp">
|
app:keyRevokedOrExpired="@{entry.revokedOrExpired}"
|
||||||
|
app:keyUserEmail="@{entry.primaryUserId.email}"
|
||||||
|
app:query="@{entry.query}" />
|
||||||
|
|
||||||
<ImageView
|
<TextView
|
||||||
android:id="@+id/import_item_status"
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
app:keyCreation="@{entry.date}"
|
||||||
|
app:keyRevokedOrExpired="@{entry.revokedOrExpired}" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/extra_container"
|
||||||
|
android:layout_below="@id/container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:animateLayoutChanges="true"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/extra"
|
||||||
|
layout="@layout/import_keys_list_item_extra"
|
||||||
|
app:entry="@{entry}" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:padding="8dp"
|
||||||
android:src="@drawable/status_signature_revoked_cutout_24dp"
|
android:layout_alignParentBottom="true"
|
||||||
android:padding="16dp" />
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_alignParentEnd="true" />
|
||||||
|
|
||||||
<TextView
|
</RelativeLayout>
|
||||||
android:id="@+id/import_item_algorithm"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="RSA"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<View
|
</android.support.v7.widget.CardView>
|
||||||
android:id="@+id/import_item_status_divider"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dip"
|
|
||||||
android:gravity="right"
|
|
||||||
android:layout_marginBottom="2dp"
|
|
||||||
android:layout_marginTop="2dp"
|
|
||||||
android:background="?android:attr/listDivider" />
|
|
||||||
|
|
||||||
<LinearLayout
|
</layout>
|
||||||
android:id="@+id/import_item_user_ids_list"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/import_item_fingerprint"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="0000 0000 0000 0000 0000\n0000 0000 0000 0000 0000"
|
|
||||||
android:typeface="monospace"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
<import type="android.view.View" alias="V" />
|
||||||
|
<import type="org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry" />
|
||||||
|
|
||||||
|
<variable name="entry" type="ImportKeysListEntry" />
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:paddingBottom="16dp">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_toLeftOf="@+id/status"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/label_key_id_colon"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:textColor="?attr/colorText" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:textColor="?attr/colorText"
|
||||||
|
app:keyId="@{entry.keyIdHex}" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/label_algorithm_colon"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:textColor="?attr/colorText" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:text="@{entry.algorithm ?? @string/unknown}"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:textColor="?attr/colorText" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/status"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:paddingLeft="8dp"
|
||||||
|
android:paddingRight="8dp"
|
||||||
|
android:visibility="@{entry.revokedOrExpired ? V.VISIBLE : V.GONE}"
|
||||||
|
app:keyExpired="@{entry.expired}"
|
||||||
|
app:keyRevoked="@{entry.revoked}" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:keyUserIds="@{entry.sortedUserIds}"
|
||||||
|
app:query="@{entry.query}" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/import_key"
|
||||||
|
style="@style/CardViewActionButton"
|
||||||
|
android:text="@{entry.updated ? @string/btn_refresh : @string/btn_import}" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/show_key"
|
||||||
|
style="@style/CardViewActionButton"
|
||||||
|
android:text="@string/btn_go_to_key"
|
||||||
|
android:visibility="@{entry.updated ? V.VISIBLE : V.GONE}" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</layout>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?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_import_keys_cloud_search"
|
||||||
|
android:icon="@drawable/ic_search_white_24dp"
|
||||||
|
android:title="@string/menu_search"
|
||||||
|
app:actionViewClass="android.support.v7.widget.SearchView"
|
||||||
|
app:showAsAction="collapseActionView|always" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_import_keys_cloud_settings"
|
||||||
|
android:title="@string/menu_preferences" />
|
||||||
|
|
||||||
|
</menu>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?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_import_keys_file_open"
|
||||||
|
android:icon="@drawable/ic_folder_white_24dp"
|
||||||
|
android:title="@string/menu_open"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_import_keys_file_paste"
|
||||||
|
android:title="@string/menu_read_clipboard" />
|
||||||
|
|
||||||
|
</menu>
|
||||||
@@ -909,7 +909,7 @@
|
|||||||
<string name="key_unavailable">nedostupný</string>
|
<string name="key_unavailable">nedostupný</string>
|
||||||
<string name="secret_cannot_multiple">Vaše vlastní klíče lze mazat pouze jednotlivě!</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>
|
<string name="title_view_cert">Zobrazit detaily certifikátu</string>
|
||||||
<string name="unknown_algorithm">neznámý</string>
|
<string name="unknown">neznámý</string>
|
||||||
<string name="can_sign_not">nelze podepsat</string>
|
<string name="can_sign_not">nelze podepsat</string>
|
||||||
<string name="error_no_encrypt_subkey">Není dostupný šifrovací podklíč!</string>
|
<string name="error_no_encrypt_subkey">Není dostupný šifrovací podklíč!</string>
|
||||||
<string name="contact_show_key">Zobrazit klíč (%s)</string>
|
<string name="contact_show_key">Zobrazit klíč (%s)</string>
|
||||||
|
|||||||
@@ -1384,7 +1384,7 @@
|
|||||||
<string name="key_unavailable">nicht verfügbar</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="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="title_view_cert">Beglaubigungsdetails anzeigen</string>
|
||||||
<string name="unknown_algorithm">unbekannt</string>
|
<string name="unknown">unbekannt</string>
|
||||||
<string name="can_sign_not">kann nicht signieren</string>
|
<string name="can_sign_not">kann nicht signieren</string>
|
||||||
<string name="error_no_encrypt_subkey">Kein Verschlüsselungs-Unterschlüssel verfügbar!</string>
|
<string name="error_no_encrypt_subkey">Kein Verschlüsselungs-Unterschlüssel verfügbar!</string>
|
||||||
<string name="contact_show_key">Schlüssel anzeigen (%s)</string>
|
<string name="contact_show_key">Schlüssel anzeigen (%s)</string>
|
||||||
|
|||||||
@@ -1266,7 +1266,7 @@
|
|||||||
<string name="key_unavailable">no disponible</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="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="title_view_cert">Ver detalles del certificado</string>
|
||||||
<string name="unknown_algorithm">desconocido</string>
|
<string name="unknown">desconocido</string>
|
||||||
<string name="can_sign_not">no puede firmarse</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="error_no_encrypt_subkey">¡No hay subclave de cifrado disponible!</string>
|
||||||
<string name="contact_show_key">Mostrar clave (%s)</string>
|
<string name="contact_show_key">Mostrar clave (%s)</string>
|
||||||
|
|||||||
@@ -1281,7 +1281,7 @@
|
|||||||
<string name="key_unavailable">eskuraezina</string>
|
<string name="key_unavailable">eskuraezina</string>
|
||||||
<string name="secret_cannot_multiple">Zure jabetzako giltzak banaka bakarrik ezabatu daitezke!</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="title_view_cert">Ikusi Egiaztagiriaren Xehetasunak</string>
|
||||||
<string name="unknown_algorithm">ezezaguna</string>
|
<string name="unknown">ezezaguna</string>
|
||||||
<string name="can_sign_not">ezin da sinatu</string>
|
<string name="can_sign_not">ezin da sinatu</string>
|
||||||
<string name="error_no_encrypt_subkey">Ez dago enkriptaketa azpigiltzarik eskuragarri!</string>
|
<string name="error_no_encrypt_subkey">Ez dago enkriptaketa azpigiltzarik eskuragarri!</string>
|
||||||
<string name="contact_show_key">Erakutsi (%s) giltza</string>
|
<string name="contact_show_key">Erakutsi (%s) giltza</string>
|
||||||
|
|||||||
@@ -253,7 +253,7 @@
|
|||||||
<string name="label_cert_type">Tyyppi</string>
|
<string name="label_cert_type">Tyyppi</string>
|
||||||
<string name="key_no_passphrase">ei salasanaa</string>
|
<string name="key_no_passphrase">ei salasanaa</string>
|
||||||
<string name="key_unavailable">ei saatavilla</string>
|
<string name="key_unavailable">ei saatavilla</string>
|
||||||
<string name="unknown_algorithm">tunnistamaton</string>
|
<string name="unknown">tunnistamaton</string>
|
||||||
<string name="contact_show_key">Näytä avain (%s)</string>
|
<string name="contact_show_key">Näytä avain (%s)</string>
|
||||||
<string name="key_colon">Avain:</string>
|
<string name="key_colon">Avain:</string>
|
||||||
<!--Android Account-->
|
<!--Android Account-->
|
||||||
|
|||||||
@@ -1384,7 +1384,7 @@
|
|||||||
<string name="key_unavailable">non disponible</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="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="title_view_cert">Voir les détails du certificat</string>
|
||||||
<string name="unknown_algorithm">inconnu</string>
|
<string name="unknown">inconnu</string>
|
||||||
<string name="can_sign_not">impossible de signer</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="error_no_encrypt_subkey">Aucune sous-clef de chiffrement n\'est proposée !</string>
|
||||||
<string name="contact_show_key">Montrer la clef (%s)</string>
|
<string name="contact_show_key">Montrer la clef (%s)</string>
|
||||||
|
|||||||
@@ -1009,7 +1009,7 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
|
|||||||
<string name="key_unavailable">non disponibile</string>
|
<string name="key_unavailable">non disponibile</string>
|
||||||
<string name="secret_cannot_multiple">Le vostre chiavi possono essere eliminate solo singolarmente!</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="title_view_cert">Visualizza Dettagli Certificati</string>
|
||||||
<string name="unknown_algorithm">sconosciuto</string>
|
<string name="unknown">sconosciuto</string>
|
||||||
<string name="can_sign_not">non può firmare</string>
|
<string name="can_sign_not">non può firmare</string>
|
||||||
<string name="error_no_encrypt_subkey">Nessuna sottochiave di codifica disponibile!</string>
|
<string name="error_no_encrypt_subkey">Nessuna sottochiave di codifica disponibile!</string>
|
||||||
<string name="contact_show_key">Mostra chiave (%s)</string>
|
<string name="contact_show_key">Mostra chiave (%s)</string>
|
||||||
|
|||||||
@@ -1360,7 +1360,7 @@
|
|||||||
<string name="key_unavailable">存在しない</string>
|
<string name="key_unavailable">存在しない</string>
|
||||||
<string name="secret_cannot_multiple">あなたが所有者の鍵は個別にしか削除できません!</string>
|
<string name="secret_cannot_multiple">あなたが所有者の鍵は個別にしか削除できません!</string>
|
||||||
<string name="title_view_cert">証明の詳細を見る</string>
|
<string name="title_view_cert">証明の詳細を見る</string>
|
||||||
<string name="unknown_algorithm">不明</string>
|
<string name="unknown">不明</string>
|
||||||
<string name="can_sign_not">署名不可</string>
|
<string name="can_sign_not">署名不可</string>
|
||||||
<string name="error_no_encrypt_subkey">暗号化の副鍵がありません!</string>
|
<string name="error_no_encrypt_subkey">暗号化の副鍵がありません!</string>
|
||||||
<string name="contact_show_key">鍵 (%s) を表示</string>
|
<string name="contact_show_key">鍵 (%s) を表示</string>
|
||||||
|
|||||||
@@ -1161,7 +1161,7 @@
|
|||||||
<string name="key_unavailable">niet beschikbaar</string>
|
<string name="key_unavailable">niet beschikbaar</string>
|
||||||
<string name="secret_cannot_multiple">Je eigen sleutels kunnen enkel individueel verwijderd worden!</string>
|
<string name="secret_cannot_multiple">Je eigen sleutels kunnen enkel individueel verwijderd worden!</string>
|
||||||
<string name="title_view_cert">Toon Certificaat Details</string>
|
<string name="title_view_cert">Toon Certificaat Details</string>
|
||||||
<string name="unknown_algorithm">onbekend</string>
|
<string name="unknown">onbekend</string>
|
||||||
<string name="can_sign_not">kan niet ondertekenen</string>
|
<string name="can_sign_not">kan niet ondertekenen</string>
|
||||||
<string name="error_no_encrypt_subkey">Geen codeer-subsleutel beschikbaar!</string>
|
<string name="error_no_encrypt_subkey">Geen codeer-subsleutel beschikbaar!</string>
|
||||||
<string name="contact_show_key">Toon sleutel (%s)</string>
|
<string name="contact_show_key">Toon sleutel (%s)</string>
|
||||||
|
|||||||
@@ -527,7 +527,7 @@ OSTRZEŻENIE: Jeżeli nie wiesz, czemu wyświetlił się ten komunikat, nie zezw
|
|||||||
<string name="key_unavailable">niedostepne</string>
|
<string name="key_unavailable">niedostepne</string>
|
||||||
<string name="secret_cannot_multiple">Twoje klucze mogą być usuwane tylko pojedynczo!</string>
|
<string name="secret_cannot_multiple">Twoje klucze mogą być usuwane tylko pojedynczo!</string>
|
||||||
<string name="title_view_cert">Zweryfikuj szczegóły certyfikatu</string>
|
<string name="title_view_cert">Zweryfikuj szczegóły certyfikatu</string>
|
||||||
<string name="unknown_algorithm">nieznany</string>
|
<string name="unknown">nieznany</string>
|
||||||
<string name="can_sign_not">nie może podpisać</string>
|
<string name="can_sign_not">nie może podpisać</string>
|
||||||
<string name="error_no_encrypt_subkey">Brak pod-klucza szyfrowania!</string>
|
<string name="error_no_encrypt_subkey">Brak pod-klucza szyfrowania!</string>
|
||||||
<string name="contact_show_key">Pokaż klucz (%s)</string>
|
<string name="contact_show_key">Pokaż klucz (%s)</string>
|
||||||
|
|||||||
@@ -1384,7 +1384,7 @@
|
|||||||
<string name="key_unavailable">indisponível</string>
|
<string name="key_unavailable">indisponível</string>
|
||||||
<string name="secret_cannot_multiple">Suas próprias chaves só podem ser excluídas individualmente!</string>
|
<string name="secret_cannot_multiple">Suas próprias chaves só podem ser excluídas individualmente!</string>
|
||||||
<string name="title_view_cert">Exibir Detalhes do Certificado</string>
|
<string name="title_view_cert">Exibir Detalhes do Certificado</string>
|
||||||
<string name="unknown_algorithm">desconhecido</string>
|
<string name="unknown">desconhecido</string>
|
||||||
<string name="can_sign_not">não pode assinar</string>
|
<string name="can_sign_not">não pode assinar</string>
|
||||||
<string name="error_no_encrypt_subkey">Nenhuma sub-chave de encriptação disponível!</string>
|
<string name="error_no_encrypt_subkey">Nenhuma sub-chave de encriptação disponível!</string>
|
||||||
<string name="contact_show_key">Exibir chave (%s)</string>
|
<string name="contact_show_key">Exibir chave (%s)</string>
|
||||||
|
|||||||
@@ -1433,7 +1433,7 @@
|
|||||||
<string name="key_unavailable">недоступно</string>
|
<string name="key_unavailable">недоступно</string>
|
||||||
<string name="secret_cannot_multiple">Ваши собственные ключи можно удалять только по одному!</string>
|
<string name="secret_cannot_multiple">Ваши собственные ключи можно удалять только по одному!</string>
|
||||||
<string name="title_view_cert">Просмотреть детали сертификации</string>
|
<string name="title_view_cert">Просмотреть детали сертификации</string>
|
||||||
<string name="unknown_algorithm">неизв.</string>
|
<string name="unknown">неизв.</string>
|
||||||
<string name="can_sign_not">не для подписания</string>
|
<string name="can_sign_not">не для подписания</string>
|
||||||
<string name="error_no_encrypt_subkey">Нет доп. ключа для шифрования!</string>
|
<string name="error_no_encrypt_subkey">Нет доп. ключа для шифрования!</string>
|
||||||
<string name="contact_show_key">Показать ключ (%s)</string>
|
<string name="contact_show_key">Показать ключ (%s)</string>
|
||||||
|
|||||||
@@ -751,7 +751,7 @@
|
|||||||
<string name="key_stripped">slečen</string>
|
<string name="key_stripped">slečen</string>
|
||||||
<string name="key_unavailable">ni na voljo</string>
|
<string name="key_unavailable">ni na voljo</string>
|
||||||
<string name="title_view_cert">Preglej podrobosti potrdila</string>
|
<string name="title_view_cert">Preglej podrobosti potrdila</string>
|
||||||
<string name="unknown_algorithm">neznan</string>
|
<string name="unknown">neznan</string>
|
||||||
<string name="can_sign_not">ne more podpisati</string>
|
<string name="can_sign_not">ne more podpisati</string>
|
||||||
<string name="error_no_encrypt_subkey">Ni nobenega podključa za šifriranje!</string>
|
<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="contact_show_key">Prikaži ključ (%s)</string>
|
||||||
|
|||||||
@@ -1389,7 +1389,7 @@
|
|||||||
<string name="key_unavailable">недоступан</string>
|
<string name="key_unavailable">недоступан</string>
|
||||||
<string name="secret_cannot_multiple">Сопствене кључеве можете брисати само појединачно!</string>
|
<string name="secret_cannot_multiple">Сопствене кључеве можете брисати само појединачно!</string>
|
||||||
<string name="title_view_cert">Прикажи детаље сертификата</string>
|
<string name="title_view_cert">Прикажи детаље сертификата</string>
|
||||||
<string name="unknown_algorithm">непознат</string>
|
<string name="unknown">непознат</string>
|
||||||
<string name="can_sign_not">не може да потпише</string>
|
<string name="can_sign_not">не може да потпише</string>
|
||||||
<string name="error_no_encrypt_subkey">Поткључ за шифровање није доступан!</string>
|
<string name="error_no_encrypt_subkey">Поткључ за шифровање није доступан!</string>
|
||||||
<string name="contact_show_key">Прикажи кључ (%s)</string>
|
<string name="contact_show_key">Прикажи кључ (%s)</string>
|
||||||
|
|||||||
@@ -958,7 +958,7 @@
|
|||||||
<string name="key_unavailable">otillgänglig</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="secret_cannot_multiple">Dina egna nycklar kan bara raderas var för sig!</string>
|
||||||
<string name="title_view_cert">Visa certifikatinformation</string>
|
<string name="title_view_cert">Visa certifikatinformation</string>
|
||||||
<string name="unknown_algorithm">okänd</string>
|
<string name="unknown">okänd</string>
|
||||||
<string name="can_sign_not">kan inte signera</string>
|
<string name="can_sign_not">kan inte signera</string>
|
||||||
<string name="error_no_encrypt_subkey">Ingen krypteringsundernyckel tillgänglig!</string>
|
<string name="error_no_encrypt_subkey">Ingen krypteringsundernyckel tillgänglig!</string>
|
||||||
<string name="contact_show_key">Visa nyckel (%s)</string>
|
<string name="contact_show_key">Visa nyckel (%s)</string>
|
||||||
|
|||||||
@@ -395,7 +395,7 @@
|
|||||||
<string name="key_unavailable">mevcut değil</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="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="title_view_cert">Sertifika Ayrıntılarını Görüntüle</string>
|
||||||
<string name="unknown_algorithm">bilinmeyen</string>
|
<string name="unknown">bilinmeyen</string>
|
||||||
<string name="can_sign_not">imzalanamadı</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="error_no_encrypt_subkey">Şifreleme için kullanılabilecek altanahtar mevcut değil!</string>
|
||||||
<string name="contact_show_key">Anahtarı göster (%s)</string>
|
<string name="contact_show_key">Anahtarı göster (%s)</string>
|
||||||
|
|||||||
@@ -483,7 +483,7 @@
|
|||||||
<string name="key_stripped">голий</string>
|
<string name="key_stripped">голий</string>
|
||||||
<string name="secret_cannot_multiple">Ваші власні ключі можна вилучити лише окремо!</string>
|
<string name="secret_cannot_multiple">Ваші власні ключі можна вилучити лише окремо!</string>
|
||||||
<string name="title_view_cert">Переглянути дані сертифікату</string>
|
<string name="title_view_cert">Переглянути дані сертифікату</string>
|
||||||
<string name="unknown_algorithm">невідомий</string>
|
<string name="unknown">невідомий</string>
|
||||||
<string name="can_sign_not">не можна підписати</string>
|
<string name="can_sign_not">не можна підписати</string>
|
||||||
<string name="error_no_encrypt_subkey">Жодний підключ шифрування недоступний!</string>
|
<string name="error_no_encrypt_subkey">Жодний підключ шифрування недоступний!</string>
|
||||||
<string name="contact_show_key">Показати ключ (%s)</string>
|
<string name="contact_show_key">Показати ключ (%s)</string>
|
||||||
|
|||||||
@@ -666,7 +666,7 @@
|
|||||||
<string name="key_unavailable">無法使用</string>
|
<string name="key_unavailable">無法使用</string>
|
||||||
<string name="secret_cannot_multiple">您的金鑰只能個別地刪除!</string>
|
<string name="secret_cannot_multiple">您的金鑰只能個別地刪除!</string>
|
||||||
<string name="title_view_cert">查看認證內容</string>
|
<string name="title_view_cert">查看認證內容</string>
|
||||||
<string name="unknown_algorithm">未知</string>
|
<string name="unknown">未知</string>
|
||||||
<string name="error_no_encrypt_subkey">沒有可供加密的子金鑰!</string>
|
<string name="error_no_encrypt_subkey">沒有可供加密的子金鑰!</string>
|
||||||
<string name="exchange_description">要發起金鑰交換,先在右邊選擇與會人數,然後點選〝開始交換〞。\n\n接下來會詢問你兩個問題,以確保會議成員與交換的指紋是正確的。</string>
|
<string name="exchange_description">要發起金鑰交換,先在右邊選擇與會人數,然後點選〝開始交換〞。\n\n接下來會詢問你兩個問題,以確保會議成員與交換的指紋是正確的。</string>
|
||||||
<string name="btn_start_exchange">開始交換</string>
|
<string name="btn_start_exchange">開始交換</string>
|
||||||
|
|||||||
@@ -1095,7 +1095,7 @@
|
|||||||
<string name="key_unavailable">不可用</string>
|
<string name="key_unavailable">不可用</string>
|
||||||
<string name="secret_cannot_multiple">只能逐个删除您的密钥!</string>
|
<string name="secret_cannot_multiple">只能逐个删除您的密钥!</string>
|
||||||
<string name="title_view_cert">查看认证详情</string>
|
<string name="title_view_cert">查看认证详情</string>
|
||||||
<string name="unknown_algorithm">未知</string>
|
<string name="unknown">未知</string>
|
||||||
<string name="can_sign_not">无法签署</string>
|
<string name="can_sign_not">无法签署</string>
|
||||||
<string name="exchange_description">在开始密钥交换之前,在右侧指定参与者的数量,然后点击“开始交换”按钮。\n\n您将被要求回答两个问题以确认由正确的参与者参与交换过程,并且他们的指纹是正确的。</string>
|
<string name="exchange_description">在开始密钥交换之前,在右侧指定参与者的数量,然后点击“开始交换”按钮。\n\n您将被要求回答两个问题以确认由正确的参与者参与交换过程,并且他们的指纹是正确的。</string>
|
||||||
<string name="btn_start_exchange">开始交换</string>
|
<string name="btn_start_exchange">开始交换</string>
|
||||||
|
|||||||
@@ -122,13 +122,14 @@
|
|||||||
<string name="menu_delete_key">"Delete key"</string>
|
<string name="menu_delete_key">"Delete key"</string>
|
||||||
<string name="menu_manage_keys">"Manage my keys"</string>
|
<string name="menu_manage_keys">"Manage my keys"</string>
|
||||||
<string name="menu_search">"Search"</string>
|
<string name="menu_search">"Search"</string>
|
||||||
|
<string name="menu_open">"Open"</string>
|
||||||
|
<string name="menu_read_clipboard">"Read from clipboard"</string>
|
||||||
<string name="menu_nfc_preferences">"NFC settings"</string>
|
<string name="menu_nfc_preferences">"NFC settings"</string>
|
||||||
<string name="menu_beam_preferences">"Beam settings"</string>
|
<string name="menu_beam_preferences">"Beam settings"</string>
|
||||||
<string name="menu_encrypt_to">"Encrypt to…"</string>
|
<string name="menu_encrypt_to">"Encrypt to…"</string>
|
||||||
<string name="menu_select_all">"Select all"</string>
|
<string name="menu_select_all">"Select all"</string>
|
||||||
<string name="menu_export_all_keys">"Export all keys"</string>
|
<string name="menu_export_all_keys">"Export all keys"</string>
|
||||||
<string name="menu_update_all_keys">"Update all keys"</string>
|
<string name="menu_update_all_keys">"Update all keys"</string>
|
||||||
<string name="menu_advanced">"Advanced"</string>
|
|
||||||
<string name="menu_certify_fingerprint">"Confirm with fingerprint"</string>
|
<string name="menu_certify_fingerprint">"Confirm with fingerprint"</string>
|
||||||
<string name="menu_certify_fingerprint_phrases">"Confirm with phrases"</string>
|
<string name="menu_certify_fingerprint_phrases">"Confirm with phrases"</string>
|
||||||
<string name="menu_share_log">"Share log"</string>
|
<string name="menu_share_log">"Share log"</string>
|
||||||
@@ -148,6 +149,7 @@
|
|||||||
<string name="label_passphrase_again">"Repeat Password"</string>
|
<string name="label_passphrase_again">"Repeat Password"</string>
|
||||||
<string name="label_show_passphrase">"Show Password"</string>
|
<string name="label_show_passphrase">"Show Password"</string>
|
||||||
<string name="label_algorithm">"Algorithm"</string>
|
<string name="label_algorithm">"Algorithm"</string>
|
||||||
|
<string name="label_algorithm_colon">"Algorithm:"</string>
|
||||||
<string name="label_ascii_armor">"File ASCII Armor"</string>
|
<string name="label_ascii_armor">"File ASCII Armor"</string>
|
||||||
<string name="label_file_ascii_armor">"Enable ASCII Armor"</string>
|
<string name="label_file_ascii_armor">"Enable ASCII Armor"</string>
|
||||||
<string name="label_write_version_header">"Let others know that you're using OpenKeychain"</string>
|
<string name="label_write_version_header">"Let others know that you're using OpenKeychain"</string>
|
||||||
@@ -165,9 +167,11 @@
|
|||||||
<string name="label_file_compression">"File compression"</string>
|
<string name="label_file_compression">"File compression"</string>
|
||||||
<string name="label_keyservers">"Manage OpenPGP keyservers"</string>
|
<string name="label_keyservers">"Manage OpenPGP keyservers"</string>
|
||||||
<string name="label_key_id">"Key ID"</string>
|
<string name="label_key_id">"Key ID"</string>
|
||||||
|
<string name="label_key_id_colon">"Key ID:"</string>
|
||||||
<string name="label_key_created">"Key created %s"</string>
|
<string name="label_key_created">"Key created %s"</string>
|
||||||
<string name="label_key_type">"Type"</string>
|
<string name="label_key_type">"Type"</string>
|
||||||
<string name="label_creation">"Creation"</string>
|
<string name="label_creation">"Creation"</string>
|
||||||
|
<string name="label_creation_colon">"Creation:"</string>
|
||||||
<string name="label_expiry">"Expiry"</string>
|
<string name="label_expiry">"Expiry"</string>
|
||||||
<string name="label_usage">"Usage"</string>
|
<string name="label_usage">"Usage"</string>
|
||||||
<string name="label_key_size">"Key Size"</string>
|
<string name="label_key_size">"Key Size"</string>
|
||||||
@@ -254,7 +258,6 @@
|
|||||||
<string name="orbot_start_dialog_cancel">"Cancel"</string>
|
<string name="orbot_start_dialog_cancel">"Cancel"</string>
|
||||||
<string name="orbot_start_dialog_ignore_tor">"Don't use Tor"</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="user_id_no_name"><![CDATA[<no name>]]></string>
|
||||||
<string name="none"><![CDATA[<none>]]></string>
|
<string name="none"><![CDATA[<none>]]></string>
|
||||||
|
|
||||||
@@ -388,8 +391,8 @@
|
|||||||
<string name="error_jelly_bean_needed">"You need Android 4.1 to use Android's NFC Beam feature!"</string>
|
<string name="error_jelly_bean_needed">"You need Android 4.1 to use Android's NFC Beam feature!"</string>
|
||||||
<string name="error_nfc_needed">"NFC must be enabled!"</string>
|
<string name="error_nfc_needed">"NFC must be enabled!"</string>
|
||||||
<string name="error_beam_needed">"Beam must be enabled!"</string>
|
<string name="error_beam_needed">"Beam must be enabled!"</string>
|
||||||
|
<string name="error_nothing">"Nothing to show yet."</string>
|
||||||
<string name="error_nothing_import">"No keys found!"</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_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>
|
<string name="error_generic_report_bug">"A generic error occurred, please create a new bug report for OpenKeychain."</string>
|
||||||
<string name="error_denied_storage_permission">"Can not read files from storage because access has been denied!"</string>
|
<string name="error_denied_storage_permission">"Can not read files from storage because access has been denied!"</string>
|
||||||
@@ -419,6 +422,7 @@
|
|||||||
<string name="progress_done">"Done."</string>
|
<string name="progress_done">"Done."</string>
|
||||||
<string name="progress_cancel">"Cancel"</string>
|
<string name="progress_cancel">"Cancel"</string>
|
||||||
<string name="progress_cancelling">"cancelling…"</string>
|
<string name="progress_cancelling">"cancelling…"</string>
|
||||||
|
<string name="progress_downloading">"downloading…"</string>
|
||||||
<string name="progress_saving">"saving…"</string>
|
<string name="progress_saving">"saving…"</string>
|
||||||
<string name="progress_importing">"importing…"</string>
|
<string name="progress_importing">"importing…"</string>
|
||||||
<string name="progress_benchmarking">"benchmarking…"</string>
|
<string name="progress_benchmarking">"benchmarking…"</string>
|
||||||
@@ -523,15 +527,17 @@
|
|||||||
<string name="import_tab_cloud">"Key Search"</string>
|
<string name="import_tab_cloud">"Key Search"</string>
|
||||||
<string name="import_tab_direct">"File/Clipboard"</string>
|
<string name="import_tab_direct">"File/Clipboard"</string>
|
||||||
<string name="import_tab_qr_code">"QR Code/NFC"</string>
|
<string name="import_tab_qr_code">"QR Code/NFC"</string>
|
||||||
<string name="import_import">"Import selected keys"</string>
|
<string name="import_found_keys">"Found %1$d keys"</string>
|
||||||
<string name="import_qr_code_wrong">"QR Code malformed! Please try again!"</string>
|
<string name="import_qr_code_wrong">"QR Code malformed! Please try again!"</string>
|
||||||
<string name="import_qr_code_fp">"Fingerprint is malformed or too short!"</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_too_short_fingerprint">"Fingerprint is too short!"</string>
|
||||||
<string name="import_qr_code_button">"Scan QR Code"</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>
|
<string name="import_qr_code_text">"Place your camera over the QR Code!"</string>
|
||||||
|
<string name="btn_import">"Import"</string>
|
||||||
<!-- Import from URL -->
|
<string name="btn_import_keys">"Import all keys"</string>
|
||||||
<string name="import_url_warn_no_search_parameter">"No search query defined. You can still manually search on this keyserver."</string>
|
<string name="btn_view_list">"View list"</string>
|
||||||
|
<string name="btn_refresh">"Refresh"</string>
|
||||||
|
<string name="btn_go_to_key">"Go to key"</string>
|
||||||
|
|
||||||
<!-- Generic result toast -->
|
<!-- Generic result toast -->
|
||||||
<string name="snackbar_details">"Details"</string>
|
<string name="snackbar_details">"Details"</string>
|
||||||
@@ -1439,6 +1445,7 @@
|
|||||||
<string name="msg_get_too_many_responses">"Key search query returned too many candidates. Please refine your query!"</string>
|
<string name="msg_get_too_many_responses">"Key search query returned too many candidates. Please refine your query!"</string>
|
||||||
<string name="msg_get_query_too_short">"Search query too short. Please refine your query!"</string>
|
<string name="msg_get_query_too_short">"Search query too short. Please refine your query!"</string>
|
||||||
<string name="msg_get_query_too_short_or_too_many_responses">"Either no keys or too many have been found. Please improve your query!"</string>
|
<string name="msg_get_query_too_short_or_too_many_responses">"Either no keys or too many have been found. Please improve your query!"</string>
|
||||||
|
<string name="msg_get_no_enabled_source">"Enable at least one source for downloading!"</string>
|
||||||
|
|
||||||
<string name="msg_download_query_failed">"An error occurred when searching for keys."</string>
|
<string name="msg_download_query_failed">"An error occurred when searching for keys."</string>
|
||||||
|
|
||||||
@@ -1509,7 +1516,7 @@
|
|||||||
<string name="key_unavailable">"unavailable"</string>
|
<string name="key_unavailable">"unavailable"</string>
|
||||||
<string name="secret_cannot_multiple">"Your own keys can only be deleted individually!"</string>
|
<string name="secret_cannot_multiple">"Your own keys can only be deleted individually!"</string>
|
||||||
<string name="title_view_cert">"View Certificate Details"</string>
|
<string name="title_view_cert">"View Certificate Details"</string>
|
||||||
<string name="unknown_algorithm">"unknown"</string>
|
<string name="unknown">"unknown"</string>
|
||||||
<string name="can_sign_not">"cannot sign"</string>
|
<string name="can_sign_not">"cannot sign"</string>
|
||||||
<string name="error_no_encrypt_subkey">"No encryption subkey available!"</string>
|
<string name="error_no_encrypt_subkey">"No encryption subkey available!"</string>
|
||||||
<string name="contact_show_key">"Show key (%s)"</string>
|
<string name="contact_show_key">"Show key (%s)"</string>
|
||||||
@@ -1569,7 +1576,6 @@
|
|||||||
<string name="security_token_status_partly">"Security Token matches, partly bound to key"</string>
|
<string name="security_token_status_partly">"Security Token matches, partly bound to key"</string>
|
||||||
<string name="security_token_create">"Hold Security Token against the back of your device."</string>
|
<string name="security_token_create">"Hold Security Token against the back of your device."</string>
|
||||||
<string name="security_token_reset_or_import">"This Security Token already contains a key. You can import the key using the cloud or reset the Security Token."</string>
|
<string name="security_token_reset_or_import">"This Security Token already contains a key. You can import the key using the cloud or reset the Security Token."</string>
|
||||||
<string name="btn_import">"Import"</string>
|
|
||||||
<string name="btn_reset">"Reset"</string>
|
<string name="btn_reset">"Reset"</string>
|
||||||
<string name="security_token_import_radio">"Import key"</string>
|
<string name="security_token_import_radio">"Import key"</string>
|
||||||
<string name="security_token_reset_radio">"Reset Security Token"</string>
|
<string name="security_token_reset_radio">"Reset Security Token"</string>
|
||||||
|
|||||||
@@ -11,6 +11,17 @@
|
|||||||
<item name="android:textSize">17sp</item>
|
<item name="android:textSize">17sp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="CardViewActionButton">
|
||||||
|
<item name="android:layout_width">wrap_content</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:layout_centerVertical">true</item>
|
||||||
|
<item name="android:background">?android:attr/selectableItemBackground</item>
|
||||||
|
<item name="android:minHeight">0dp</item>
|
||||||
|
<item name="android:minWidth">0dp</item>
|
||||||
|
<item name="android:padding">8dp</item>
|
||||||
|
<item name="android:textColor">@color/card_view_button</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="SectionHeader">
|
<style name="SectionHeader">
|
||||||
<item name="android:drawableBottom">?attr/sectionHeaderDrawable</item>
|
<item name="android:drawableBottom">?attr/sectionHeaderDrawable</item>
|
||||||
<item name="android:drawablePadding">4dp</item>
|
<item name="android:drawablePadding">4dp</item>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user