Merge branch 'master' of github.com:open-keychain/open-keychain

This commit is contained in:
Vincent Breitmoser
2015-11-04 20:24:11 +01:00
22 changed files with 369 additions and 95 deletions

View File

@@ -9,10 +9,12 @@ sudo: false
# - ADB_INSTALL_TIMEOUT=8 # minutes (2 minutes by default) # - ADB_INSTALL_TIMEOUT=8 # minutes (2 minutes by default)
android: android:
components: components:
- build-tools-23.0.1
- build-tools-22.0.1 - build-tools-22.0.1
- build-tools-21.1.2 - build-tools-21.1.2
- build-tools-21.1.1 - build-tools-21.1.1
- build-tools-19.1.0 - build-tools-19.1.0
- android-23
- android-22 - android-22
- android-21 - android-21
- android-19 - android-19

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="24"
height="24"
viewBox="0 0 24 24"
id="svg2"
inkscape:version="0.48.5 r10040"
sodipodi:docname="ic_action_encrypt_paste.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2558"
inkscape:window-height="1419"
id="namedview6"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="0.10169504"
inkscape:cy="12"
inkscape:window-x="0"
inkscape:window-y="19"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<path
id="path3076"
d="m 17.363197,9.8980016 a 0.84079901,0.84079901 0 0 0 0.840798,-0.8408 c 0,-0.466643 -0.378359,-0.840799 -0.840798,-0.840799 a 0.84079901,0.84079901 0 0 0 -0.8408,0.840799 0.84079901,0.84079901 0 0 0 0.8408,0.8408 m 2.522396,-3.783596 a 0.84079901,0.84079901 0 0 1 0.8408,0.840799 V 11.159199 A 0.84079901,0.84079901 0 0 1 19.885593,12 H 14.840799 A 0.84079901,0.84079901 0 0 1 14,11.159199 V 6.9552046 c 0,-0.466644 0.37836,-0.840799 0.840799,-0.840799 h 0.4204 v -0.840799 a 2.1019975,2.1019975 0 0 1 2.101998,-2.101997 2.1019975,2.1019975 0 0 1 2.101997,2.101997 v 0.840799 h 0.420399 m -2.522396,-2.101998 a 1.2611986,1.2611986 0 0 0 -1.261198,1.261199 v 0.840799 h 2.522396 v -0.840799 a 1.2611986,1.2611986 0 0 0 -1.261198,-1.261199 z"
inkscape:connector-curvature="0"
style="fill:#000000" />
<path
id="path3053"
d="M 15.283513,19.502312 H 6.4607096 V 9.4191068 H 7.7211105 V 11.309707 H 14.023111 V 9.4191068 h 1.260402 M 10.87211,8.1587061 a 0.63020031,0.63020031 0 0 1 0.630201,0.6302004 0.63020031,0.63020031 0 0 1 -0.630201,0.6302003 0.63020031,0.63020031 0 0 1 -0.6302,-0.6302003 0.63020031,0.63020031 0 0 1 0.6302,-0.6302004 m 4.411405,0 h -2.63424 C 12.384592,7.427674 11.691372,6.8983055 10.87211,6.8983055 c -0.819259,0 -1.5124797,0.5293685 -1.7771628,1.2604006 H 6.4607096 A 1.2604005,1.2604005 0 0 0 5.200309,9.4191068 V 19.502312 a 1.2604005,1.2604005 0 0 0 1.2604006,1.260399 h 8.8228034 a 1.2604005,1.2604005 0 0 0 1.2604,-1.260399 V 9.4191068 a 1.2604005,1.2604005 0 0 0 -1.2604,-1.2604007 z"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -12,7 +12,7 @@
height="24" height="24"
viewBox="0 0 24 24" viewBox="0 0 24 24"
id="svg2" id="svg2"
inkscape:version="0.48.3.1 r9886" inkscape:version="0.48.5 r10040"
sodipodi:docname="ic_action_encrypt_save.svg"> sodipodi:docname="ic_action_encrypt_save.svg">
<metadata <metadata
id="metadata10"> id="metadata10">
@@ -41,8 +41,8 @@
id="namedview6" id="namedview6"
showgrid="false" showgrid="false"
inkscape:zoom="9.8333333" inkscape:zoom="9.8333333"
inkscape:cx="12.20339" inkscape:cx="-4.7288134"
inkscape:cy="12" inkscape:cy="11.949153"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="19" inkscape:window-y="19"
inkscape:window-maximized="1" inkscape:window-maximized="1"
@@ -50,10 +50,11 @@
<path <path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
d="M 12.355932,11.847458 H 4.7288135 V 8.7966102 H 12.355932 M 10.067797,19.474576 a 2.2881356,2.2881356 0 0 1 -2.288136,-2.288135 2.2881356,2.2881356 0 0 1 2.288136,-2.288136 2.2881356,2.2881356 0 0 1 2.288135,2.288136 2.2881356,2.2881356 0 0 1 -2.288135,2.288135 M 13.881356,7.2711864 H 4.7288135 c -0.8466101,0 -1.5254237,0.6864407 -1.5254237,1.5254238 V 19.474576 A 1.5254237,1.5254237 0 0 0 4.7288135,21 H 15.40678 a 1.5254237,1.5254237 0 0 0 1.525424,-1.525424 V 10.322034 L 13.881356,7.2711864 z" d="M 12.355932,11.847458 H 4.7288135 V 8.7966102 H 12.355932 M 10.067797,19.474576 a 2.2881356,2.2881356 0 0 1 -2.288136,-2.288135 2.2881356,2.2881356 0 0 1 2.288136,-2.288136 2.2881356,2.2881356 0 0 1 2.288135,2.288136 2.2881356,2.2881356 0 0 1 -2.288135,2.288135 M 13.881356,7.2711864 H 4.7288135 c -0.8466101,0 -1.5254237,0.6864407 -1.5254237,1.5254238 V 19.474576 A 1.5254237,1.5254237 0 0 0 4.7288135,21 H 15.40678 a 1.5254237,1.5254237 0 0 0 1.525424,-1.525424 V 10.322034 L 13.881356,7.2711864 z"
id="path4-7" /> id="path4-7"
style="fill:#000000;fill-opacity:1" />
<path <path
id="path3076-1" id="path3076-1"
d="m 17.363197,9.8980016 a 0.84079901,0.84079901 0 0 0 0.840798,-0.8408 c 0,-0.466643 -0.378359,-0.840799 -0.840798,-0.840799 a 0.84079901,0.84079901 0 0 0 -0.8408,0.840799 0.84079901,0.84079901 0 0 0 0.8408,0.8408 m 2.522396,-3.783596 a 0.84079901,0.84079901 0 0 1 0.8408,0.840799 V 11.159199 A 0.84079901,0.84079901 0 0 1 19.885593,12 H 14.840799 A 0.84079901,0.84079901 0 0 1 14,11.159199 V 6.9552046 c 0,-0.466644 0.37836,-0.840799 0.840799,-0.840799 h 0.4204 v -0.840799 a 2.1019975,2.1019975 0 0 1 2.101998,-2.101997 2.1019975,2.1019975 0 0 1 2.101997,2.101997 v 0.840799 h 0.420399 m -2.522396,-2.101998 a 1.2611986,1.2611986 0 0 0 -1.261198,1.261199 v 0.840799 h 2.522396 v -0.840799 a 1.2611986,1.2611986 0 0 0 -1.261198,-1.261199 z" d="m 17.363197,9.8980016 a 0.84079901,0.84079901 0 0 0 0.840798,-0.8408 c 0,-0.466643 -0.378359,-0.840799 -0.840798,-0.840799 a 0.84079901,0.84079901 0 0 0 -0.8408,0.840799 0.84079901,0.84079901 0 0 0 0.8408,0.8408 m 2.522396,-3.783596 a 0.84079901,0.84079901 0 0 1 0.8408,0.840799 V 11.159199 A 0.84079901,0.84079901 0 0 1 19.885593,12 H 14.840799 A 0.84079901,0.84079901 0 0 1 14,11.159199 V 6.9552046 c 0,-0.466644 0.37836,-0.840799 0.840799,-0.840799 h 0.4204 v -0.840799 a 2.1019975,2.1019975 0 0 1 2.101998,-2.101997 2.1019975,2.1019975 0 0 1 2.101997,2.101997 v 0.840799 h 0.420399 m -2.522396,-2.101998 a 1.2611986,1.2611986 0 0 0 -1.261198,1.261199 v 0.840799 h 2.522396 v -0.840799 a 1.2611986,1.2611986 0 0 0 -1.261198,-1.261199 z"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="fill:#000000" /> style="fill:#000000;fill-opacity:1" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M19,20H5V4H7V7H17V4H19M12,2A1,1 0 0,1 13,3A1,1 0 0,1 12,4A1,1 0 0,1 11,3A1,1 0 0,1 12,2M19,2H14.82C14.4,0.84 13.3,0 12,0C10.7,0 9.6,0.84 9.18,2H5A2,2 0 0,0 3,4V20A2,2 0 0,0 5,22H19A2,2 0 0,0 21,20V4A2,2 0 0,0 19,2Z" /></svg>

After

Width:  |  Height:  |  Size: 509 B

View File

@@ -22,7 +22,7 @@ SRC_DIR=./drawables/
#inkscape -w 512 -h 512 -e "$PLAY_DIR/$NAME.png" $NAME.svg #inkscape -w 512 -h 512 -e "$PLAY_DIR/$NAME.png" $NAME.svg
for NAME in "ic_cloud_search" "ic_action_encrypt_file" "ic_action_encrypt_text" "ic_action_verified_cutout" "ic_action_encrypt_copy" "ic_action_encrypt_save" "ic_action_encrypt_share" "status_lock_closed" "status_lock_error" "status_lock_open" "status_signature_expired_cutout" "status_signature_invalid_cutout" "status_signature_revoked_cutout" "status_signature_unknown_cutout" "status_signature_unverified_cutout" "status_signature_verified_cutout" "key_flag_authenticate" "key_flag_certify" "key_flag_encrypt" "key_flag_sign" "yubi_icon" "ic_stat_notify" "status_signature_verified_inner" "link" "octo_link" for NAME in "ic_cloud_search" "ic_action_encrypt_file" "ic_action_encrypt_text" "ic_action_verified_cutout" "ic_action_encrypt_copy" "ic_action_encrypt_paste" "ic_action_encrypt_save" "ic_action_encrypt_share" "status_lock_closed" "status_lock_error" "status_lock_open" "status_signature_expired_cutout" "status_signature_invalid_cutout" "status_signature_revoked_cutout" "status_signature_unknown_cutout" "status_signature_unverified_cutout" "status_signature_verified_cutout" "key_flag_authenticate" "key_flag_certify" "key_flag_encrypt" "key_flag_sign" "yubi_icon" "ic_stat_notify" "status_signature_verified_inner" "link" "octo_link"
do do
echo $NAME echo $NAME
inkscape -w 24 -h 24 -e "$MDPI_DIR/${NAME}_24dp.png" "$SRC_DIR/$NAME.svg" inkscape -w 24 -h 24 -e "$MDPI_DIR/${NAME}_24dp.png" "$SRC_DIR/$NAME.svg"

View File

@@ -53,19 +53,32 @@
android:name="${applicationId}.WRITE_TEMPORARY_STORAGE" android:name="${applicationId}.WRITE_TEMPORARY_STORAGE"
android:protectionLevel="signature" /> android:protectionLevel="signature" />
<uses-permission android:name="android.permission.INTERNET" /> <!-- CAMERA permission requested by ZXing library -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.NFC" /> <!-- contact group -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" /> <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" /> <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_PROFILE" /> <uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.WRITE_PROFILE" /> <uses-permission android:name="android.permission.WRITE_PROFILE" />
<!-- storage group -->
<!--
No need on >= Android 4.4 for WRITE_EXTERNAL_STORAGE, because we use Storage Access Framework,
but better not use maxSdkVersion as it causes problems: https://code.google.com/p/android/issues/detail?id=63895
-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- READ_EXTERNAL_STORAGE is now dangerous on Android >= 6 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- other group (for free) -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<!-- android:allowBackup="false": Don't allow backup over adb backup or other apps! --> <!-- android:allowBackup="false": Don't allow backup over adb backup or other apps! -->
<application <application
android:name=".KeychainApplication" android:name=".KeychainApplication"
@@ -97,12 +110,12 @@
android:value=".ui.MainActivity" /> android:value=".ui.MainActivity" />
<!-- Connect with YubiKeys. This Activity will automatically show/import/create YubiKeys --> <!-- Connect with YubiKeys. This Activity will automatically show/import/create YubiKeys -->
<intent-filter android:label="@string/app_name"> <intent-filter android:label="@string/app_name">
<action android:name="android.nfc.action.NDEF_DISCOVERED"/> <action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT" />
<data <data
android:scheme="https"
android:host="my.yubico.com" android:host="my.yubico.com"
android:pathPrefix="/neo"/> android:pathPrefix="/neo"
android:scheme="https" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
@@ -114,9 +127,7 @@
android:name=".ui.linked.LinkedIdWizard" android:name=".ui.linked.LinkedIdWizard"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_linked_create" android:label="@string/title_linked_create"
android:parentActivityName=".ui.ViewKeyActivity" android:parentActivityName=".ui.ViewKeyActivity"></activity>
>
</activity>
<activity <activity
android:name=".ui.QrCodeViewActivity" android:name=".ui.QrCodeViewActivity"
android:label="@string/share_qr_code_dialog_title" /> android:label="@string/share_qr_code_dialog_title" />
@@ -210,6 +221,12 @@
<data android:mimeType="text/*" /> <data android:mimeType="text/*" />
<data android:mimeType="message/*" /> <data android:mimeType="message/*" />
</intent-filter> </intent-filter>
<!-- Android 6 Floating Action Mode -->
<intent-filter>
<action android:name="android.intent.action.PROCESS_TEXT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity> </activity>
<activity <activity
android:name=".ui.DisplayTextActivity" android:name=".ui.DisplayTextActivity"
@@ -466,7 +483,7 @@
android:name=".ui.ImportKeysProxyActivity" android:name=".ui.ImportKeysProxyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@android:style/Theme.NoDisplay" android:theme="@style/Theme.Keychain.Transparent"
android:windowSoftInputMode="stateHidden"> android:windowSoftInputMode="stateHidden">
<!-- VIEW with fingerprint scheme: <!-- VIEW with fingerprint scheme:
@@ -499,8 +516,7 @@
<data android:mimeType="application/pgp-keys" /> <data android:mimeType="application/pgp-keys" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity android:name=".ui.QrCodeCaptureActivity" />
android:name=".ui.QrCodeCaptureActivity"/>
<activity <activity
android:name=".ui.ImportKeysActivity" android:name=".ui.ImportKeysActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
@@ -692,19 +708,19 @@
android:label="@string/title_log_display" /> android:label="@string/title_log_display" />
<activity <activity
android:name=".ui.ConsolidateDialogActivity" android:name=".ui.ConsolidateDialogActivity"
android:theme="@android:style/Theme.NoDisplay" /> android:theme="@style/Theme.Keychain.Transparent" />
<activity <activity
android:name=".ui.PassphraseDialogActivity" android:name=".ui.PassphraseDialogActivity"
android:theme="@android:style/Theme.NoDisplay" /> android:theme="@style/Theme.Keychain.Transparent" />
<activity <activity
android:name=".ui.RetryUploadDialogActivity" android:name=".ui.RetryUploadDialogActivity"
android:theme="@android:style/Theme.NoDisplay" /> android:theme="@style/Theme.Keychain.Transparent" />
<activity <activity
android:name=".ui.DeleteKeyDialogActivity" android:name=".ui.DeleteKeyDialogActivity"
android:theme="@android:style/Theme.NoDisplay" /> android:theme="@style/Theme.Keychain.Transparent" />
<activity <activity
android:name=".ui.OrbotRequiredDialogActivity" android:name=".ui.OrbotRequiredDialogActivity"
android:theme="@android:style/Theme.NoDisplay" /> android:theme="@style/Theme.Keychain.Transparent" />
<!-- <!--
NOTE: singleTop is set to get NFC foreground dispatch to work. NOTE: singleTop is set to get NFC foreground dispatch to work.
Then, all NFC intents will be broadcasted to onNewIntent() of this activity! Then, all NFC intents will be broadcasted to onNewIntent() of this activity!
@@ -714,10 +730,10 @@
--> -->
<activity <activity
android:name=".ui.NfcOperationActivity" android:name=".ui.NfcOperationActivity"
android:theme="@style/Theme.Keychain.Light.Dialog"
android:allowTaskReparenting="true" android:allowTaskReparenting="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:taskAffinity=":Nfc" /> android:taskAffinity=":Nfc"
android:theme="@style/Theme.Keychain.Light.Dialog" />
<activity <activity
android:name=".ui.HelpActivity" android:name=".ui.HelpActivity"
@@ -742,7 +758,7 @@
android:name=".provider.KeychainProvider" android:name=".provider.KeychainProvider"
android:authorities="${applicationId}.provider" android:authorities="${applicationId}.provider"
android:exported="false" android:exported="false"
android:label="@string/keyserver_sync_settings_title"/> android:label="@string/keyserver_sync_settings_title" />
<!-- Internal classes of the remote APIs (not exported) --> <!-- Internal classes of the remote APIs (not exported) -->
<activity <activity

View File

@@ -540,7 +540,9 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
// adding required information to mResultType // adding required information to mResultType
// special case,no keys requested for import // special case,no keys requested for import
if (mBadKeys == 0 && mNewKeys == 0 && mUpdatedKeys == 0) { if (mBadKeys == 0 && mNewKeys == 0 && mUpdatedKeys == 0
&& (mResultType & ImportKeyResult.RESULT_CANCELLED)
!= ImportKeyResult.RESULT_CANCELLED) {
mResultType = ImportKeyResult.RESULT_FAIL_NOTHING; mResultType = ImportKeyResult.RESULT_FAIL_NOTHING;
} else { } else {
if (mNewKeys > 0) { if (mNewKeys > 0) {

View File

@@ -59,11 +59,12 @@ public class KeyserverSyncAdapterService extends Service {
// time since last update after which a key should be updated again, in s // time since last update after which a key should be updated again, in s
public static final long KEY_UPDATE_LIMIT = public static final long KEY_UPDATE_LIMIT =
Constants.DEBUG_KEYSERVER_SYNC ? 1 : TimeUnit.DAYS.toSeconds(7); Constants.DEBUG_KEYSERVER_SYNC ? 1 : TimeUnit.DAYS.toSeconds(7);
// time by which a sync is postponed in case of a // time by which a sync is postponed in case screen is on
public static final long SYNC_POSTPONE_TIME = public static final long SYNC_POSTPONE_TIME =
Constants.DEBUG_KEYSERVER_SYNC ? 30 * 1000 : TimeUnit.MINUTES.toMillis(5); Constants.DEBUG_KEYSERVER_SYNC ? 30 * 1000 : TimeUnit.MINUTES.toMillis(5);
// Time taken by Orbot before a new circuit is created // Time taken by Orbot before a new circuit is created
public static final int ORBOT_CIRCUIT_TIMEOUT = (int) TimeUnit.MINUTES.toMillis(10); public static final int ORBOT_CIRCUIT_TIMEOUT_SECONDS =
Constants.DEBUG_KEYSERVER_SYNC ? 2 : (int) TimeUnit.MINUTES.toSeconds(10);
private static final String ACTION_IGNORE_TOR = "ignore_tor"; private static final String ACTION_IGNORE_TOR = "ignore_tor";
@@ -77,10 +78,14 @@ public class KeyserverSyncAdapterService extends Service {
@Override @Override
public int onStartCommand(final Intent intent, int flags, final int startId) { public int onStartCommand(final Intent intent, int flags, final int startId) {
if (intent == null || intent.getAction() == null) {
// introduced due to https://github.com/open-keychain/open-keychain/issues/1573
return START_NOT_STICKY; // we can't act on this Intent and don't want it redelivered
}
switch (intent.getAction()) { switch (intent.getAction()) {
case ACTION_CANCEL: { case ACTION_CANCEL: {
mCancelled.set(true); mCancelled.set(true);
break; return START_NOT_STICKY;
} }
// the reason for the separation betweyeen SYNC_NOW and UPDATE_ALL is so that starting // the reason for the separation betweyeen SYNC_NOW and UPDATE_ALL is so that starting
// the sync directly from the notification is possible while the screen is on with // the sync directly from the notification is possible while the screen is on with
@@ -92,44 +97,47 @@ public class KeyserverSyncAdapterService extends Service {
Constants.PROVIDER_AUTHORITY, Constants.PROVIDER_AUTHORITY,
new Bundle() new Bundle()
); );
break; return START_NOT_STICKY;
} }
case ACTION_UPDATE_ALL: { case ACTION_UPDATE_ALL: {
// does not check for screen on/off // does not check for screen on/off
asyncKeyUpdate(this, new CryptoInputParcel()); asyncKeyUpdate(this, new CryptoInputParcel(), startId);
break; // we depend on handleUpdateResult to call stopSelf when it is no longer necessary
// for the intent to be redelivered
return START_REDELIVER_INTENT;
} }
case ACTION_IGNORE_TOR: { case ACTION_IGNORE_TOR: {
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT);
asyncKeyUpdate(this, new CryptoInputParcel(ParcelableProxy.getForNoProxy())); asyncKeyUpdate(this, new CryptoInputParcel(ParcelableProxy.getForNoProxy()),
break; startId);
// we depend on handleUpdateResult to call stopSelf when it is no longer necessary
// for the intent to be redelivered
return START_REDELIVER_INTENT;
} }
case ACTION_START_ORBOT: { case ACTION_START_ORBOT: {
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); NotificationManager manager = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT);
Intent startOrbot = new Intent(this, OrbotRequiredDialogActivity.class); Intent startOrbot = new Intent(this, OrbotRequiredDialogActivity.class);
startOrbot.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startOrbot.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startOrbot.putExtra(OrbotRequiredDialogActivity.EXTRA_START_ORBOT, true); startOrbot.putExtra(OrbotRequiredDialogActivity.EXTRA_START_ORBOT, true);
Messenger messenger = new Messenger( Messenger messenger = new Messenger(
new Handler() { new Handler() {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
switch (msg.what) { switch (msg.what) {
case OrbotRequiredDialogActivity.MESSAGE_ORBOT_STARTED: { case OrbotRequiredDialogActivity.MESSAGE_ORBOT_STARTED: {
asyncKeyUpdate(KeyserverSyncAdapterService.this, startServiceWithUpdateAll();
new CryptoInputParcel());
break;
}
case OrbotRequiredDialogActivity.MESSAGE_ORBOT_IGNORE: {
asyncKeyUpdate(KeyserverSyncAdapterService.this,
new CryptoInputParcel(
ParcelableProxy.getForNoProxy()));
break; break;
} }
case OrbotRequiredDialogActivity.MESSAGE_ORBOT_IGNORE:
case OrbotRequiredDialogActivity.MESSAGE_DIALOG_CANCEL: { case OrbotRequiredDialogActivity.MESSAGE_DIALOG_CANCEL: {
// just stop service // not possible since we proceed to Orbot's Activity
stopSelf(); // directly, by starting OrbotRequiredDialogActivity with
// EXTRA_START_ORBOT set to true
break; break;
} }
} }
@@ -138,13 +146,17 @@ public class KeyserverSyncAdapterService extends Service {
); );
startOrbot.putExtra(OrbotRequiredDialogActivity.EXTRA_MESSENGER, messenger); startOrbot.putExtra(OrbotRequiredDialogActivity.EXTRA_MESSENGER, messenger);
startActivity(startOrbot); startActivity(startOrbot);
break; // since we return START_NOT_STICKY, we also postpone the sync as a backup in case
// the service is killed before OrbotRequiredDialogActivity can get back to us
postponeSync();
// if use START_REDELIVER_INTENT, we might annoy the user by repeatedly starting the
// Orbot Activity when our service is killed and restarted
return START_NOT_STICKY;
} }
case ACTION_DISMISS_NOTIFICATION: { case ACTION_DISMISS_NOTIFICATION: {
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT);
stopSelf(startId); return START_NOT_STICKY;
break;
} }
} }
return START_NOT_STICKY; return START_NOT_STICKY;
@@ -167,10 +179,7 @@ public class KeyserverSyncAdapterService extends Service {
boolean isScreenOn = pm.isScreenOn(); boolean isScreenOn = pm.isScreenOn();
if (!isScreenOn) { if (!isScreenOn) {
Intent serviceIntent = new Intent(KeyserverSyncAdapterService.this, startServiceWithUpdateAll();
KeyserverSyncAdapterService.class);
serviceIntent.setAction(ACTION_UPDATE_ALL);
startService(serviceIntent);
} else { } else {
postponeSync(); postponeSync();
} }
@@ -188,16 +197,24 @@ public class KeyserverSyncAdapterService extends Service {
return new KeyserverSyncAdapter().getSyncAdapterBinder(); return new KeyserverSyncAdapter().getSyncAdapterBinder();
} }
private void handleUpdateResult(ImportKeyResult result) { /**
* Since we're returning START_REDELIVER_INTENT in onStartCommand, we need to remember to call
* stopSelf(int) to prevent the Intent from being redelivered if our work is already done
*
* @param result result of keyserver sync
* @param startId startId provided to the onStartCommand call which resulted in this sync
*/
private void handleUpdateResult(ImportKeyResult result, final int startId) {
if (result.isPending()) { if (result.isPending()) {
Log.d(Constants.TAG, "Orbot required for sync but not running, attempting to start");
// result is pending due to Orbot not being started // result is pending due to Orbot not being started
// try to start it silently, if disabled show notifications // try to start it silently, if disabled show notifications
new OrbotHelper.SilentStartManager() { new OrbotHelper.SilentStartManager() {
@Override @Override
protected void onOrbotStarted() { protected void onOrbotStarted() {
// retry the update // retry the update
asyncKeyUpdate(KeyserverSyncAdapterService.this, startServiceWithUpdateAll();
new CryptoInputParcel()); stopSelf(startId); // startServiceWithUpdateAll will deliver a new Intent
} }
@Override @Override
@@ -207,16 +224,24 @@ public class KeyserverSyncAdapterService extends Service {
(NotificationManager) getSystemService(NOTIFICATION_SERVICE); (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT, manager.notify(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT,
getOrbotNoification(KeyserverSyncAdapterService.this)); getOrbotNoification(KeyserverSyncAdapterService.this));
// further action on user interaction with notification, intent should not be
// redelivered, therefore:
stopSelf(startId);
} }
}.startOrbotAndListen(this, false); }.startOrbotAndListen(this, false);
// if we're killed before we get a response from Orbot, we need the intent to be
// redelivered, so no stopSelf(int) here
} else if (isUpdateCancelled()) { } else if (isUpdateCancelled()) {
Log.d(Constants.TAG, "Keyserver sync cancelled, postponing by" + SYNC_POSTPONE_TIME Log.d(Constants.TAG, "Keyserver sync cancelled, postponing by" + SYNC_POSTPONE_TIME
+ "ms"); + "ms");
postponeSync(); postponeSync();
// postponeSync creates a new intent, so we don't need this to be redelivered
stopSelf(startId);
} else { } else {
Log.d(Constants.TAG, "Keyserver sync completed: Updated: " + result.mUpdatedKeys Log.d(Constants.TAG, "Keyserver sync completed: Updated: " + result.mUpdatedKeys
+ " Failed: " + result.mBadKeys); + " Failed: " + result.mBadKeys);
stopSelf(); // key sync completed successfully, we can stop
stopSelf(startId);
} }
} }
@@ -234,12 +259,12 @@ public class KeyserverSyncAdapterService extends Service {
} }
private void asyncKeyUpdate(final Context context, private void asyncKeyUpdate(final Context context,
final CryptoInputParcel cryptoInputParcel) { final CryptoInputParcel cryptoInputParcel, final int startId) {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
ImportKeyResult result = updateKeysFromKeyserver(context, cryptoInputParcel); ImportKeyResult result = updateKeysFromKeyserver(context, cryptoInputParcel);
handleUpdateResult(result); handleUpdateResult(result, startId);
} }
}).start(); }).start();
} }
@@ -278,7 +303,6 @@ public class KeyserverSyncAdapterService extends Service {
); );
} }
/** /**
* will perform a staggered update of user's keys using delays to ensure new Tor circuits, as * will perform a staggered update of user's keys using delays to ensure new Tor circuits, as
* performed by parcimonie. Relevant issue and method at: * performed by parcimonie. Relevant issue and method at:
@@ -290,17 +314,31 @@ public class KeyserverSyncAdapterService extends Service {
CryptoInputParcel cryptoInputParcel) { CryptoInputParcel cryptoInputParcel) {
Log.d(Constants.TAG, "Starting staggered update"); Log.d(Constants.TAG, "Starting staggered update");
// final int WEEK_IN_SECONDS = (int) TimeUnit.DAYS.toSeconds(7); // final int WEEK_IN_SECONDS = (int) TimeUnit.DAYS.toSeconds(7);
// we are limiting our randomness to ORBOT_CIRCUIT_TIMEOUT_SECONDS for now
final int WEEK_IN_SECONDS = 0; final int WEEK_IN_SECONDS = 0;
ImportOperation.KeyImportAccumulator accumulator ImportOperation.KeyImportAccumulator accumulator
= new ImportOperation.KeyImportAccumulator(keyList.size(), null); = new ImportOperation.KeyImportAccumulator(keyList.size(), null);
// so that the first key can be updated without waiting. This is so that there isn't a
// large gap between a "Start Orbot" notification and the next key update
boolean first = true;
for (ParcelableKeyRing keyRing : keyList) { for (ParcelableKeyRing keyRing : keyList) {
int waitTime; int waitTime;
int staggeredTime = new Random().nextInt(1 + 2 * (WEEK_IN_SECONDS / keyList.size())); int staggeredTime = new Random().nextInt(1 + 2 * (WEEK_IN_SECONDS / keyList.size()));
if (staggeredTime >= ORBOT_CIRCUIT_TIMEOUT) { if (staggeredTime >= ORBOT_CIRCUIT_TIMEOUT_SECONDS) {
waitTime = staggeredTime; waitTime = staggeredTime;
} else { } else {
waitTime = ORBOT_CIRCUIT_TIMEOUT + new Random().nextInt(ORBOT_CIRCUIT_TIMEOUT); waitTime = ORBOT_CIRCUIT_TIMEOUT_SECONDS
+ new Random().nextInt(1 + ORBOT_CIRCUIT_TIMEOUT_SECONDS);
} }
if (first) {
waitTime = 0;
first = false;
}
Log.d(Constants.TAG, "Updating key with fingerprint " + keyRing.mExpectedFingerprint + Log.d(Constants.TAG, "Updating key with fingerprint " + keyRing.mExpectedFingerprint +
" with a wait time of " + waitTime + "s"); " with a wait time of " + waitTime + "s");
try { try {
@@ -362,13 +400,15 @@ public class KeyserverSyncAdapterService extends Service {
); );
ArrayList<Long> ignoreMasterKeyIds = new ArrayList<>(); ArrayList<Long> ignoreMasterKeyIds = new ArrayList<>();
while (updatedKeysCursor.moveToNext()) { while (updatedKeysCursor != null && updatedKeysCursor.moveToNext()) {
long masterKeyId = updatedKeysCursor.getLong(INDEX_UPDATED_KEYS_MASTER_KEY_ID); long masterKeyId = updatedKeysCursor.getLong(INDEX_UPDATED_KEYS_MASTER_KEY_ID);
Log.d(Constants.TAG, "Keyserver sync: Ignoring {" + masterKeyId + "} last updated at {" Log.d(Constants.TAG, "Keyserver sync: Ignoring {" + masterKeyId + "} last updated at {"
+ updatedKeysCursor.getLong(INDEX_LAST_UPDATED) + "}s"); + updatedKeysCursor.getLong(INDEX_LAST_UPDATED) + "}s");
ignoreMasterKeyIds.add(masterKeyId); ignoreMasterKeyIds.add(masterKeyId);
} }
updatedKeysCursor.close(); if (updatedKeysCursor != null) {
updatedKeysCursor.close();
}
// 2. Make a list of public keys which should be updated // 2. Make a list of public keys which should be updated
final int INDEX_MASTER_KEY_ID = 0; final int INDEX_MASTER_KEY_ID = 0;
@@ -413,7 +453,7 @@ public class KeyserverSyncAdapterService extends Service {
/** /**
* will cancel an update already in progress. We send an Intent to cancel it instead of simply * will cancel an update already in progress. We send an Intent to cancel it instead of simply
* modifying a static variable sync the service is running in a process that is different from * modifying a static variable since the service is running in a process that is different from
* the default application process where the UI code runs. * the default application process where the UI code runs.
* *
* @param context used to send an Intent to the service requesting cancellation. * @param context used to send an Intent to the service requesting cancellation.
@@ -491,6 +531,12 @@ public class KeyserverSyncAdapterService extends Service {
} }
} }
private void startServiceWithUpdateAll() {
Intent serviceIntent = new Intent(this, KeyserverSyncAdapterService.class);
serviceIntent.setAction(ACTION_UPDATE_ALL);
this.startService(serviceIntent);
}
// from de.azapps.mirakel.helper.Helpers from https://github.com/MirakelX/mirakel-android // from de.azapps.mirakel.helper.Helpers from https://github.com/MirakelX/mirakel-android
private Bitmap getBitmap(int resId, Context context) { private Bitmap getBitmap(int resId, Context context) {
int mLargeIconWidth = (int) context.getResources().getDimension( int mLargeIconWidth = (int) context.getResources().getDimension(

View File

@@ -60,6 +60,9 @@ public class EncryptTextActivity extends EncryptActivity {
extras = new Bundle(); extras = new Bundle();
} }
String textData = extras.getString(EXTRA_TEXT);
boolean returnProcessText = false;
// When sending to OpenKeychain Encrypt via share menu // When sending to OpenKeychain Encrypt via share menu
if (Intent.ACTION_SEND.equals(action) && type != null) { if (Intent.ACTION_SEND.equals(action) && type != null) {
Log.logDebugBundle(extras, "extras"); Log.logDebugBundle(extras, "extras");
@@ -95,12 +98,33 @@ public class EncryptTextActivity extends EncryptActivity {
} }
// handle like normal text encryption, override action and extras to later // handle like normal text encryption, override action and extras to later
// executeServiceMethod ACTION_ENCRYPT_TEXT in main actions // executeServiceMethod ACTION_ENCRYPT_TEXT in main actions
extras.putString(EXTRA_TEXT, sharedText); textData = sharedText;
} }
} }
String textData = extras.getString(EXTRA_TEXT); // Android 6, PROCESS_TEXT Intent
if (Intent.ACTION_PROCESS_TEXT.equals(action) && type != null) {
String sharedText = null;
if (extras.containsKey(Intent.EXTRA_PROCESS_TEXT)) {
sharedText = extras.getString(Intent.EXTRA_PROCESS_TEXT);
returnProcessText = true;
} else if (extras.containsKey(Intent.EXTRA_PROCESS_TEXT_READONLY)) {
sharedText = extras.getString(Intent.EXTRA_PROCESS_TEXT_READONLY);
}
if (sharedText != null) {
if (sharedText.length() > Constants.TEXT_LENGTH_LIMIT) {
sharedText = sharedText.substring(0, Constants.TEXT_LENGTH_LIMIT);
Notify.create(this, R.string.snack_shared_text_too_long, Style.WARN).show();
}
// handle like normal text encryption, override action and extras to later
// executeServiceMethod ACTION_ENCRYPT_TEXT in main actions
textData = sharedText;
}
}
if (textData == null) { if (textData == null) {
textData = ""; textData = "";
} }
@@ -108,7 +132,7 @@ public class EncryptTextActivity extends EncryptActivity {
if (savedInstanceState == null) { if (savedInstanceState == null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
EncryptTextFragment encryptFragment = EncryptTextFragment.newInstance(textData); EncryptTextFragment encryptFragment = EncryptTextFragment.newInstance(textData, returnProcessText);
transaction.replace(R.id.encrypt_text_container, encryptFragment); transaction.replace(R.id.encrypt_text_container, encryptFragment);
transaction.commit(); transaction.commit();
} }

View File

@@ -56,8 +56,10 @@ public class EncryptTextFragment
public static final String ARG_TEXT = "text"; public static final String ARG_TEXT = "text";
public static final String ARG_USE_COMPRESSION = "use_compression"; public static final String ARG_USE_COMPRESSION = "use_compression";
public static final String ARG_RETURN_PROCESS_TEXT = "return_process_text";
private boolean mShareAfterEncrypt; private boolean mShareAfterEncrypt;
private boolean mReturnProcessTextAfterEncrypt;
private boolean mUseCompression; private boolean mUseCompression;
private boolean mHiddenRecipients = false; private boolean mHiddenRecipients = false;
@@ -66,11 +68,12 @@ public class EncryptTextFragment
/** /**
* Creates new instance of this fragment * Creates new instance of this fragment
*/ */
public static EncryptTextFragment newInstance(String text) { public static EncryptTextFragment newInstance(String text, boolean returnProcessTextAfterEncrypt) {
EncryptTextFragment frag = new EncryptTextFragment(); EncryptTextFragment frag = new EncryptTextFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putString(ARG_TEXT, text); args.putString(ARG_TEXT, text);
args.putBoolean(ARG_RETURN_PROCESS_TEXT, returnProcessTextAfterEncrypt);
frag.setArguments(args); frag.setArguments(args);
return frag; return frag;
@@ -128,6 +131,7 @@ public class EncryptTextFragment
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (savedInstanceState == null) { if (savedInstanceState == null) {
mMessage = getArguments().getString(ARG_TEXT); mMessage = getArguments().getString(ARG_TEXT);
mReturnProcessTextAfterEncrypt = getArguments().getBoolean(ARG_RETURN_PROCESS_TEXT, false);
} }
Preferences prefs = Preferences.getPreferences(getActivity()); Preferences prefs = Preferences.getPreferences(getActivity());
@@ -151,6 +155,12 @@ public class EncryptTextFragment
inflater.inflate(R.menu.encrypt_text_fragment, menu); inflater.inflate(R.menu.encrypt_text_fragment, menu);
menu.findItem(R.id.check_enable_compression).setChecked(mUseCompression); menu.findItem(R.id.check_enable_compression).setChecked(mUseCompression);
if (mReturnProcessTextAfterEncrypt) {
menu.findItem(R.id.encrypt_paste).setVisible(true);
menu.findItem(R.id.encrypt_copy).setVisible(false);
menu.findItem(R.id.encrypt_share).setVisible(false);
}
} }
@Override @Override
@@ -177,6 +187,11 @@ public class EncryptTextFragment
cryptoOperation(new CryptoInputParcel(new Date())); cryptoOperation(new CryptoInputParcel(new Date()));
break; break;
} }
case R.id.encrypt_paste: {
hideKeyboard();
cryptoOperation(new CryptoInputParcel(new Date()));
break;
}
default: { default: {
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@@ -328,6 +343,11 @@ public class EncryptTextFragment
// Share encrypted message/file // Share encrypted message/file
startActivity(Intent.createChooser(createSendIntent(result.getResultBytes()), startActivity(Intent.createChooser(createSendIntent(result.getResultBytes()),
getString(R.string.title_share_message))); getString(R.string.title_share_message)));
} else if (mReturnProcessTextAfterEncrypt) {
Intent resultIntent = new Intent();
resultIntent.putExtra(Intent.EXTRA_PROCESS_TEXT, new String(result.getResultBytes()));
getActivity().setResult(Activity.RESULT_OK, resultIntent);
getActivity().finish();
} else { } else {
// Copy to clipboard // Copy to clipboard
copyToClipboard(result); copyToClipboard(result);

View File

@@ -1,23 +1,31 @@
/* /*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* *
* 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;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentActivity;
import android.support.v4.content.ContextCompat;
import android.view.KeyEvent; import android.view.KeyEvent;
import com.journeyapps.barcodescanner.CaptureManager; import com.journeyapps.barcodescanner.CaptureManager;
@@ -29,6 +37,8 @@ public class QrCodeCaptureActivity extends FragmentActivity {
private CaptureManager capture; private CaptureManager capture;
private CompoundBarcodeView barcodeScannerView; private CompoundBarcodeView barcodeScannerView;
public static final int MY_PERMISSIONS_REQUEST_CAMERA = 42;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -38,33 +48,87 @@ public class QrCodeCaptureActivity extends FragmentActivity {
barcodeScannerView = (CompoundBarcodeView) findViewById(R.id.zxing_barcode_scanner); barcodeScannerView = (CompoundBarcodeView) findViewById(R.id.zxing_barcode_scanner);
barcodeScannerView.setStatusText(getString(R.string.import_qr_code_text)); barcodeScannerView.setStatusText(getString(R.string.import_qr_code_text));
if (savedInstanceState != null) {
init(barcodeScannerView, getIntent(), savedInstanceState);
}
// check Android 6 permission
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED) {
init(barcodeScannerView, getIntent(), null);
} else {
// // Should we show an explanation?
// if (ActivityCompat.shouldShowRequestPermissionRationale(this,
// Manifest.permission.CAMERA)) {
//
// // Show an explanation to the user *asynchronously* -- don't block
// // this thread waiting for the user's response! After the user
// // sees the explanation, try again to request the permission.
//
// } else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
MY_PERMISSIONS_REQUEST_CAMERA);
// }
}
}
private void init(CompoundBarcodeView barcodeScannerView, Intent intent, Bundle savedInstanceState) {
capture = new CaptureManager(this, barcodeScannerView); capture = new CaptureManager(this, barcodeScannerView);
capture.initializeFromIntent(getIntent(), savedInstanceState); capture.initializeFromIntent(intent, savedInstanceState);
capture.decode(); capture.decode();
} }
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
@NonNull int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_CAMERA: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted
init(barcodeScannerView, getIntent(), null);
} else {
setResult(Activity.RESULT_CANCELED);
finish();
}
}
}
}
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
capture.onResume(); if (capture != null) {
capture.onResume();
}
} }
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
capture.onPause(); if (capture != null) {
capture.onPause();
}
} }
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
capture.onDestroy(); if (capture != null) {
capture.onDestroy();
}
} }
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
capture.onSaveInstanceState(outState); if (capture != null) {
capture.onSaveInstanceState(outState);
}
} }
@Override @Override

View File

@@ -38,6 +38,7 @@ import android.os.Handler;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.support.design.widget.AppBarLayout; import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CollapsingToolbarLayout; import android.support.design.widget.CollapsingToolbarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
@@ -869,7 +870,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
mActionEncryptFile.setVisibility(View.INVISIBLE); mActionEncryptFile.setVisibility(View.INVISIBLE);
mActionEncryptText.setVisibility(View.INVISIBLE); mActionEncryptText.setVisibility(View.INVISIBLE);
mActionNfc.setVisibility(View.INVISIBLE); mActionNfc.setVisibility(View.INVISIBLE);
mFab.setVisibility(View.GONE); hideFab();
mQrCodeLayout.setVisibility(View.GONE); mQrCodeLayout.setVisibility(View.GONE);
} else if (mIsExpired) { } else if (mIsExpired) {
if (mIsSecret) { if (mIsSecret) {
@@ -885,7 +886,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
mActionEncryptFile.setVisibility(View.INVISIBLE); mActionEncryptFile.setVisibility(View.INVISIBLE);
mActionEncryptText.setVisibility(View.INVISIBLE); mActionEncryptText.setVisibility(View.INVISIBLE);
mActionNfc.setVisibility(View.INVISIBLE); mActionNfc.setVisibility(View.INVISIBLE);
mFab.setVisibility(View.GONE); hideFab();
mQrCodeLayout.setVisibility(View.GONE); mQrCodeLayout.setVisibility(View.GONE);
} else if (mIsSecret) { } else if (mIsSecret) {
mStatusText.setText(R.string.view_key_my_key); mStatusText.setText(R.string.view_key_my_key);
@@ -927,7 +928,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
} else { } else {
mActionNfc.setVisibility(View.GONE); mActionNfc.setVisibility(View.GONE);
} }
mFab.setVisibility(View.VISIBLE); showFab();
// noinspection deprecation (no getDrawable with theme at current minApi level 15!) // noinspection deprecation (no getDrawable with theme at current minApi level 15!)
mFab.setImageDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp)); mFab.setImageDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp));
} else { } else {
@@ -944,7 +945,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
color = getResources().getColor(R.color.key_flag_green); color = getResources().getColor(R.color.key_flag_green);
photoTask.execute(mMasterKeyId); photoTask.execute(mMasterKeyId);
mFab.setVisibility(View.GONE); hideFab();
} else { } else {
mStatusText.setText(R.string.view_key_unverified); mStatusText.setText(R.string.view_key_unverified);
mStatusImage.setVisibility(View.VISIBLE); mStatusImage.setVisibility(View.VISIBLE);
@@ -952,7 +953,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
State.UNVERIFIED, R.color.icons, true); State.UNVERIFIED, R.color.icons, true);
color = getResources().getColor(R.color.key_flag_orange); color = getResources().getColor(R.color.key_flag_orange);
mFab.setVisibility(View.VISIBLE); showFab();
} }
} }
@@ -982,6 +983,28 @@ public class ViewKeyActivity extends BaseNfcActivity implements
} }
} }
/**
* Helper to show Fab, from http://stackoverflow.com/a/31047038
*/
private void showFab() {
CoordinatorLayout.LayoutParams p = (CoordinatorLayout.LayoutParams) mFab.getLayoutParams();
p.setBehavior(new FloatingActionButton.Behavior());
p.setAnchorId(R.id.app_bar_layout);
mFab.setLayoutParams(p);
mFab.setVisibility(View.VISIBLE);
}
/**
* Helper to hide Fab, from http://stackoverflow.com/a/31047038
*/
private void hideFab() {
CoordinatorLayout.LayoutParams p = (CoordinatorLayout.LayoutParams) mFab.getLayoutParams();
p.setBehavior(null); //should disable default animations
p.setAnchorId(View.NO_ID); //should let you set visibility
mFab.setLayoutParams(p);
mFab.setVisibility(View.GONE);
}
@Override @Override
public void onLoaderReset(Loader<Cursor> loader) { public void onLoaderReset(Loader<Cursor> loader) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 0 B

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 986 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -2,6 +2,14 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/encrypt_paste"
android:title="@string/btn_paste_encrypted_signed"
android:icon="@drawable/ic_action_encrypt_paste_24dp"
android:orderInCategory="1"
android:visible="false"
app:showAsAction="ifRoom" />
<item <item
android:id="@+id/encrypt_copy" android:id="@+id/encrypt_copy"
android:title="@string/btn_copy_encrypted_signed" android:title="@string/btn_copy_encrypted_signed"

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- see http://stackoverflow.com/questions/32169303/activity-did-not-call-finish-api-23 -->
<style name="Theme.Keychain.Transparent" parent="@android:style/Theme.Translucent.NoTitleBar" />
</resources>

View File

@@ -88,6 +88,7 @@
<string name="btn_match_phrases">"Phrases match"</string> <string name="btn_match_phrases">"Phrases match"</string>
<string name="btn_share_encrypted_signed">"Encrypt/sign and share text"</string> <string name="btn_share_encrypted_signed">"Encrypt/sign and share text"</string>
<string name="btn_copy_encrypted_signed">"Encrypt/sign and copy text"</string> <string name="btn_copy_encrypted_signed">"Encrypt/sign and copy text"</string>
<string name="btn_paste_encrypted_signed">"Encrypt/sign and paste text"</string>
<string name="btn_view_cert_key">"View certification key"</string> <string name="btn_view_cert_key">"View certification key"</string>
<string name="btn_create_key">"Create key"</string> <string name="btn_create_key">"Create key"</string>
<string name="btn_add_files">"Add file(s)"</string> <string name="btn_add_files">"Add file(s)"</string>

View File

@@ -85,11 +85,9 @@
<item name="alertDialogTheme">@style/Theme.Keychain.Dark.Dialog.Alert</item> <item name="alertDialogTheme">@style/Theme.Keychain.Dark.Dialog.Alert</item>
</style> </style>
<style name="Theme.Keychain.Light" parent="Base.Theme.Keychain.Light"> <style name="Theme.Keychain.Light" parent="Base.Theme.Keychain.Light"></style>
</style>
<style name="Theme.Keychain.Dark" parent="Base.Theme.Keychain.Dark"> <style name="Theme.Keychain.Dark" parent="Base.Theme.Keychain.Dark"></style>
</style>
<!-- http://android-developers.blogspot.de/2014/10/appcompat-v21-material-design-for-pre.html --> <!-- http://android-developers.blogspot.de/2014/10/appcompat-v21-material-design-for-pre.html -->
<style name="Widget.Keychain.SearchView" parent="Widget.AppCompat.SearchView"> <style name="Widget.Keychain.SearchView" parent="Widget.AppCompat.SearchView">
@@ -134,4 +132,6 @@
<item name="windowActionBar">false</item> <item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item> <item name="windowNoTitle">true</item>
</style> </style>
<style name="Theme.Keychain.Transparent" parent="@android:style/Theme.NoDisplay" />
</resources> </resources>