From 7da783228499ea9d5b0a98201e8ba5b17e30bb10 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 31 Aug 2014 17:32:13 +0200 Subject: [PATCH 1/5] Add cancelable mechanism and support in key import Closes #323 --- .../keychain/helper/ExportHelper.java | 9 +- .../keychain/pgp/PgpImportExport.java | 23 ++++- .../service/KeychainIntentService.java | 21 +++- .../service/KeychainIntentServiceHandler.java | 8 +- .../service/OperationResultParcel.java | 10 +- .../keychain/service/OperationResults.java | 35 ++++--- .../keychain/ui/ImportKeysActivity.java | 5 +- .../ui/dialog/ProgressDialogFragment.java | 96 ++++++++----------- OpenKeychain/src/main/res/values/strings.xml | 2 + 9 files changed, 119 insertions(+), 90 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java index bcd57b290..f065dbc6d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java @@ -121,14 +121,7 @@ public class ExportHelper { // Message is received after exporting is done in KeychainIntentService KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler(mActivity, mActivity.getString(R.string.progress_exporting), - ProgressDialog.STYLE_HORIZONTAL, - true, - new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialogInterface) { - mActivity.stopService(intent); - } - }) { + ProgressDialog.STYLE_HORIZONTAL) { public void handleMessage(Message message) { // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index 0bc3ac0ab..669a7d37e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -46,6 +46,7 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; public class PgpImportExport { @@ -125,11 +126,20 @@ public class PgpImportExport { } /** Imports keys from given data. If keyIds is given only those are imported */ + public ImportKeyResult importKeyRings(List entries, + AtomicBoolean cancelled) { + return importKeyRings(entries.iterator(), entries.size(), cancelled); + } + public ImportKeyResult importKeyRings(List entries) { - return importKeyRings(entries.iterator(), entries.size()); + return importKeyRings(entries.iterator(), entries.size(), null); } public ImportKeyResult importKeyRings(Iterator entries, int num) { + return importKeyRings(entries, num, null); + } + public ImportKeyResult importKeyRings(Iterator entries, int num, + AtomicBoolean cancelled) { updateProgress(R.string.progress_importing, 0, 100); // If there aren't even any keys, do nothing here. @@ -143,6 +153,12 @@ public class PgpImportExport { int position = 0; double progSteps = 100.0 / num; for (ParcelableKeyRing entry : new IterableIterator(entries)) { + // Has this action been cancelled? If so, don't proceed any further + if (cancelled != null && cancelled.get()) { + Log.d(Constants.TAG, "CANCELLED!"); + break; + } + try { UncachedKeyRing key = UncachedKeyRing.decodeFromData(entry.getBytes()); @@ -208,9 +224,12 @@ public class PgpImportExport { } } if (log.containsWarnings()) { - resultType |= ImportKeyResult.RESULT_WITH_WARNINGS; + resultType |= ImportKeyResult.RESULT_WARNINGS; } } + if (cancelled != null && cancelled.get()) { + resultType |= ImportKeyResult.RESULT_CANCELLED; + } return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys, secret); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 021e6bc07..bc570385a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -72,6 +72,7 @@ import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; /** * This Service contains all important long lasting operations for APG. It receives Intents with @@ -110,6 +111,8 @@ public class KeychainIntentService extends IntentService public static final String ACTION_CONSOLIDATE = Constants.INTENT_PREFIX + "CONSOLIDATE"; + public static final String ACTION_CANCEL = Constants.INTENT_PREFIX + "CANCEL"; + /* keys for data bundle */ // encrypt, decrypt, import export @@ -196,6 +199,8 @@ public class KeychainIntentService extends IntentService Messenger mMessenger; private boolean mIsCanceled; + // this attribute can possibly merged with the one above? not sure... + private AtomicBoolean mActionCanceled = new AtomicBoolean(false); public KeychainIntentService() { super("KeychainIntentService"); @@ -214,6 +219,10 @@ public class KeychainIntentService extends IntentService */ @Override protected void onHandleIntent(Intent intent) { + + // We have not been cancelled! (yet) + mActionCanceled.set(false); + Bundle extras = intent.getExtras(); if (extras == null) { Log.e(Constants.TAG, "Extras bundle is null!"); @@ -500,7 +509,7 @@ public class KeychainIntentService extends IntentService ProviderHelper providerHelper = new ProviderHelper(this); PgpImportExport pgpImportExport = new PgpImportExport(this, providerHelper, this); - ImportKeyResult result = pgpImportExport.importKeyRings(entries); + ImportKeyResult result = pgpImportExport.importKeyRings(entries, mActionCanceled); if (result.mSecret > 0) { providerHelper.consolidateDatabaseStep1(this); @@ -612,7 +621,6 @@ public class KeychainIntentService extends IntentService ArrayList keyRings = new ArrayList(entries.size()); for (ImportKeysListEntry entry : entries) { - Keyserver server; if (entry.getOrigin() == null) { server = new HkpKeyserver(keyServer); @@ -874,6 +882,15 @@ public class KeychainIntentService extends IntentService } } + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (ACTION_CANCEL.equals(intent.getAction())) { + mActionCanceled.set(true); + return START_STICKY; + } + return super.onStartCommand(intent, flags, startId); + } + private String getOriginalFilename(Bundle data) throws PgpGeneralException, FileNotFoundException { int target = data.getInt(TARGET); switch (target) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java index 0cdbe708e..1772dc6ae 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java @@ -60,18 +60,16 @@ public class KeychainIntentServiceHandler extends Handler { public KeychainIntentServiceHandler(Activity activity, String progressDialogMessage, int progressDialogStyle) { - this(activity, progressDialogMessage, progressDialogStyle, false, null); + this(activity, progressDialogMessage, progressDialogStyle, false); } public KeychainIntentServiceHandler(Activity activity, String progressDialogMessage, - int progressDialogStyle, boolean cancelable, - OnCancelListener onCancelListener) { + int progressDialogStyle, boolean cancelable) { this.mActivity = activity; this.mProgressDialogFragment = ProgressDialogFragment.newInstance( progressDialogMessage, progressDialogStyle, - cancelable, - onCancelListener); + cancelable); } public void showProgressDialog(FragmentActivity activity) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index fe699224b..151c1761e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -55,13 +55,17 @@ public class OperationResultParcel implements Parcelable { public static final String EXTRA_RESULT = "operation_result"; - /** Holds the overall result, the number specifying varying degrees of success. The first bit - * is 0 on overall success, 1 on overall failure. All other bits may be used for more specific - * conditions. */ + /** Holds the overall result, the number specifying varying degrees of success: + * - The first bit is 0 on overall success, 1 on overall failure + * - The second bit indicates if the action was cancelled - may still be an error or success! + * - The third bit should be set if the operation succeeded with warnings + * All other bits may be used for more specific conditions. */ final int mResult; public static final int RESULT_OK = 0; public static final int RESULT_ERROR = 1; + public static final int RESULT_CANCELLED = 2; + public static final int RESULT_WARNINGS = 4; /// A list of log entries tied to the operation result. final OperationLog mLog; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java index f3d0b9e9b..822c069cc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java @@ -26,6 +26,7 @@ import android.view.View; import com.github.johnpersano.supertoasts.SuperCardToast; import com.github.johnpersano.supertoasts.SuperToast; +import com.github.johnpersano.supertoasts.SuperToast.Duration; import com.github.johnpersano.supertoasts.util.OnClickWrapper; import com.github.johnpersano.supertoasts.util.Style; @@ -44,16 +45,14 @@ public abstract class OperationResults { public final int mNewKeys, mUpdatedKeys, mBadKeys, mSecret; // At least one new key - public static final int RESULT_OK_NEWKEYS = 2; + public static final int RESULT_OK_NEWKEYS = 8; // At least one updated key - public static final int RESULT_OK_UPDATED = 4; + public static final int RESULT_OK_UPDATED = 16; // At least one key failed (might still be an overall success) - public static final int RESULT_WITH_ERRORS = 8; - // There are warnings in the log - public static final int RESULT_WITH_WARNINGS = 16; + public static final int RESULT_WITH_ERRORS = 32; // No keys to import... - public static final int RESULT_FAIL_NOTHING = 32 + 1; + public static final int RESULT_FAIL_NOTHING = 64 + 1; public boolean isOkBoth() { return (mResult & (RESULT_OK_NEWKEYS | RESULT_OK_UPDATED)) @@ -119,15 +118,20 @@ public abstract class OperationResults { if ((resultType & OperationResultParcel.RESULT_ERROR) == 0) { String withWarnings; + duration = Duration.EXTRA_LONG; + color = Style.GREEN; + withWarnings = ""; + // Any warnings? - if ((resultType & ImportKeyResult.RESULT_WITH_WARNINGS) > 0) { + if ((resultType & ImportKeyResult.RESULT_WARNINGS) > 0) { duration = 0; color = Style.ORANGE; - withWarnings = activity.getResources().getString(R.string.import_with_warnings); - } else { - duration = SuperToast.Duration.LONG; - color = Style.GREEN; - withWarnings = ""; + withWarnings += activity.getString(R.string.import_with_warnings); + } + if ((resultType & ImportKeyResult.RESULT_CANCELLED) > 0) { + duration = 0; + color = Style.ORANGE; + withWarnings += activity.getString(R.string.import_with_cancelled); } // New and updated keys @@ -152,13 +156,14 @@ public abstract class OperationResults { duration = 0; color = Style.RED; if (isFailNothing()) { - str = activity.getString(R.string.import_error_nothing); + str = activity.getString((resultType & ImportKeyResult.RESULT_CANCELLED) > 0 + ? R.string.import_error_nothing_cancelled + : R.string.import_error_nothing); } else { str = activity.getString(R.string.import_error); } } - // TODO: externalize into Notify class? boolean button = getLog() != null && !getLog().isEmpty(); SuperCardToast toast = new SuperCardToast(activity, button ? SuperToast.Type.BUTTON : SuperToast.Type.STANDARD, @@ -243,7 +248,7 @@ public abstract class OperationResults { } // Some old key was updated - public static final int UPDATED = 2; + public static final int UPDATED = 4; // Public key was saved public static final int SAVED_PUBLIC = 8; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 96fa11363..7ad7d1007 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.ui; import android.annotation.TargetApi; import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.DialogInterface.OnCancelListener; import android.content.Intent; import android.net.Uri; import android.nfc.NdefMessage; @@ -447,7 +449,8 @@ public class ImportKeysActivity extends ActionBarActivity { KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( this, getString(R.string.progress_importing), - ProgressDialog.STYLE_HORIZONTAL) { + ProgressDialog.STYLE_HORIZONTAL, + true) { public void handleMessage(Message message) { // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java index 0324dd3b9..49a8ac49f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java @@ -21,32 +21,26 @@ import android.app.Activity; import android.app.Dialog; import android.app.ProgressDialog; import android.content.DialogInterface; -import android.content.DialogInterface.OnCancelListener; import android.content.DialogInterface.OnKeyListener; +import android.content.Intent; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.view.ContextThemeWrapper; import android.view.KeyEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.KeychainIntentService; public class ProgressDialogFragment extends DialogFragment { private static final String ARG_MESSAGE = "message"; private static final String ARG_STYLE = "style"; private static final String ARG_CANCELABLE = "cancelable"; - private OnCancelListener mOnCancelListener; - - /** - * Creates new instance of this fragment - * - * @param message - * @param style - * @param cancelable - * @return - */ - public static ProgressDialogFragment newInstance(String message, int style, boolean cancelable, - OnCancelListener onCancelListener) { + /** Creates new instance of this fragment */ + public static ProgressDialogFragment newInstance(String message, int style, boolean cancelable) { ProgressDialogFragment frag = new ProgressDialogFragment(); Bundle args = new Bundle(); args.putString(ARG_MESSAGE, message); @@ -54,28 +48,16 @@ public class ProgressDialogFragment extends DialogFragment { args.putBoolean(ARG_CANCELABLE, cancelable); frag.setArguments(args); - frag.mOnCancelListener = onCancelListener; return frag; } - /** - * Updates progress of dialog - * - * @param messageId - * @param progress - * @param max - */ + /** Updates progress of dialog */ public void setProgress(int messageId, int progress, int max) { setProgress(getString(messageId), progress, max); } - /** - * Updates progress of dialog - * - * @param progress - * @param max - */ + /** Updates progress of dialog */ public void setProgress(int progress, int max) { ProgressDialog dialog = (ProgressDialog) getDialog(); @@ -83,13 +65,7 @@ public class ProgressDialogFragment extends DialogFragment { dialog.setMax(max); } - /** - * Updates progress of dialog - * - * @param message - * @param progress - * @param max - */ + /** Updates progress of dialog */ public void setProgress(String message, int progress, int max) { ProgressDialog dialog = (ProgressDialog) getDialog(); @@ -98,15 +74,6 @@ public class ProgressDialogFragment extends DialogFragment { dialog.setMax(max); } - @Override - public void onCancel(DialogInterface dialog) { - super.onCancel(dialog); - - if (this.mOnCancelListener != null) { - this.mOnCancelListener.onCancel(dialog); - } - } - /** * Creates dialog */ @@ -121,41 +88,62 @@ public class ProgressDialogFragment extends DialogFragment { ProgressDialog dialog = new ProgressDialog(context); dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + // We never use the builtin cancel method dialog.setCancelable(false); dialog.setCanceledOnTouchOutside(false); String message = getArguments().getString(ARG_MESSAGE); int style = getArguments().getInt(ARG_STYLE); - boolean cancelable = getArguments().getBoolean(ARG_CANCELABLE); + final boolean cancelable = getArguments().getBoolean(ARG_CANCELABLE); dialog.setMessage(message); dialog.setProgressStyle(style); + // If this is supposed to be cancelable, add our (custom) cancel mechanic if (cancelable) { + // Just show the button, take care of the onClickListener afterwards (in onStart) dialog.setButton(DialogInterface.BUTTON_NEGATIVE, - activity.getString(R.string.progress_cancel), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.cancel(); - } - }); + activity.getString(R.string.progress_cancel), (DialogInterface.OnClickListener) null); } - // Disable the back button + // Disable the back button regardless OnKeyListener keyListener = new OnKeyListener() { - @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { + if (cancelable) { + ((ProgressDialog) dialog).getButton( + DialogInterface.BUTTON_NEGATIVE).performClick(); + } + // return true, indicating we handled this return true; } return false; } - }; dialog.setOnKeyListener(keyListener); return dialog; } + + @Override + public void onStart() { + super.onStart(); + + // Override the default behavior so the dialog is NOT dismissed on click + Button negative = ((ProgressDialog) getDialog()).getButton(DialogInterface.BUTTON_NEGATIVE); + negative.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + // send a cancel message. note that this message will be handled by + // KeychainIntentService.onStartCommand, which runs in this thread, + // not the service one, and will not queue up a command. + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_CANCEL); + getActivity().startService(intent); + } + }); + + } + } diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 8eb452df6..270feb099 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -381,8 +381,10 @@ View Log Nothing to import. + Import cancelled. Error importing keys! , with warnings + , until cancelled Decrypt File with OpenKeychain From d483a8b73e01ab80d8f9098b46df797a7298a09e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 31 Aug 2014 19:01:28 +0200 Subject: [PATCH 2/5] get rid of some last bootstrapbutton references --- OpenKeychain/src/main/res/layout/decrypt_activity.xml | 3 +-- OpenKeychain/src/main/res/layout/decrypt_message_fragment.xml | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/OpenKeychain/src/main/res/layout/decrypt_activity.xml b/OpenKeychain/src/main/res/layout/decrypt_activity.xml index 94d82fe1d..bb0e463b3 100644 --- a/OpenKeychain/src/main/res/layout/decrypt_activity.xml +++ b/OpenKeychain/src/main/res/layout/decrypt_activity.xml @@ -1,6 +1,5 @@ @@ -9,4 +8,4 @@ - \ No newline at end of file + diff --git a/OpenKeychain/src/main/res/layout/decrypt_message_fragment.xml b/OpenKeychain/src/main/res/layout/decrypt_message_fragment.xml index 4ac60620d..3b4eba3ef 100644 --- a/OpenKeychain/src/main/res/layout/decrypt_message_fragment.xml +++ b/OpenKeychain/src/main/res/layout/decrypt_message_fragment.xml @@ -1,6 +1,5 @@ From e46bc240793f03f3cd09684a0852e33af96b2e89 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 31 Aug 2014 19:20:08 +0200 Subject: [PATCH 3/5] add cancel support to edit key action --- .../keychain/pgp/PgpKeyOperation.java | 35 +++++++++++++++++++ .../service/KeychainIntentService.java | 32 +++++++++++++---- .../service/OperationResultParcel.java | 25 ++++++------- .../keychain/ui/CreateKeyFinalFragment.java | 5 ++- .../keychain/ui/EditKeyFragment.java | 10 +++--- .../keychain/ui/LogDisplayFragment.java | 1 + .../src/main/res/values-de/strings.xml | 2 +- .../src/main/res/values-es/strings.xml | 2 +- .../src/main/res/values-fr/strings.xml | 2 +- .../src/main/res/values-it/strings.xml | 2 +- .../src/main/res/values-ja/strings.xml | 2 +- .../src/main/res/values-uk/strings.xml | 2 +- OpenKeychain/src/main/res/values/strings.xml | 5 ++- 13 files changed, 92 insertions(+), 33 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 967a7caa9..6b1433cca 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -73,6 +73,7 @@ import java.util.Arrays; import java.util.Date; import java.util.Iterator; import java.util.Stack; +import java.util.concurrent.atomic.AtomicBoolean; /** * This class is the single place where ALL operations that actually modify a PGP public or secret @@ -85,6 +86,7 @@ import java.util.Stack; */ public class PgpKeyOperation { private Stack mProgress; + private AtomicBoolean mCancelled; // most preferred is first private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{ @@ -134,6 +136,15 @@ public class PgpKeyOperation { } } + public PgpKeyOperation(Progressable progress, AtomicBoolean cancelled) { + this(progress); + mCancelled = cancelled; + } + + private boolean checkCancelled() { + return mCancelled != null && mCancelled.get(); + } + private void subProgressPush(int from, int to) { if (mProgress == null) { return; @@ -450,6 +461,12 @@ public class PgpKeyOperation { try { + // Check if we were cancelled + if (checkCancelled()) { + log.add(LogLevel.CANCELLED, LogType.MSG_OPERATION_CANCELLED, indent); + return new EditKeyResult(EditKeyResult.RESULT_CANCELLED, log, null); + } + { // work on master secret key PGPPublicKey modifiedPublicKey = masterPublicKey; @@ -640,6 +657,12 @@ public class PgpKeyOperation { } + // Check if we were cancelled - again + if (checkCancelled()) { + log.add(LogLevel.CANCELLED, LogType.MSG_OPERATION_CANCELLED, indent); + return new EditKeyResult(EditKeyResult.RESULT_CANCELLED, log, null); + } + // 4a. For each subkey change, generate new subkey binding certificate subProgressPush(50, 60); for (int i = 0; i < saveParcel.mChangeSubKeys.size(); i++) { @@ -750,6 +773,12 @@ public class PgpKeyOperation { subProgressPush(70, 90); for (int i = 0; i < saveParcel.mAddSubKeys.size(); i++) { + // Check if we were cancelled - again. This operation is expensive so we do it each loop. + if (checkCancelled()) { + log.add(LogLevel.CANCELLED, LogType.MSG_OPERATION_CANCELLED, indent); + return new EditKeyResult(EditKeyResult.RESULT_CANCELLED, log, null); + } + progress(R.string.progress_modify_subkeyadd, (i-1) * (100 / saveParcel.mAddSubKeys.size())); SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(i); log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent, @@ -806,6 +835,12 @@ public class PgpKeyOperation { } subProgressPop(); + // Check if we were cancelled - again. This operation is expensive so we do it each loop. + if (checkCancelled()) { + log.add(LogLevel.CANCELLED, LogType.MSG_OPERATION_CANCELLED, indent); + return new EditKeyResult(EditKeyResult.RESULT_CANCELLED, log, null); + } + // 6. If requested, change passphrase if (saveParcel.mNewPassphrase != null) { progress(R.string.progress_modify_passphrase, 90); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index bc570385a..5a147d646 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -54,6 +54,9 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; +import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.OperationResults.ConsolidateResult; import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult; import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult; @@ -93,7 +96,7 @@ public class KeychainIntentService extends IntentService public static final String ACTION_DECRYPT_METADATA = Constants.INTENT_PREFIX + "DECRYPT_METADATA"; - public static final String ACTION_SAVE_KEYRING = Constants.INTENT_PREFIX + "SAVE_KEYRING"; + public static final String ACTION_EDIT_KEYRING = Constants.INTENT_PREFIX + "EDIT_KEYRING"; public static final String ACTION_DELETE_FILE_SECURELY = Constants.INTENT_PREFIX + "DELETE_FILE_SECURELY"; @@ -144,8 +147,8 @@ public class KeychainIntentService extends IntentService public static final String DECRYPT_PASSPHRASE = "passphrase"; // save keyring - public static final String SAVE_KEYRING_PARCEL = "save_parcel"; - public static final String SAVE_KEYRING_PASSPHRASE = "passphrase"; + public static final String EDIT_KEYRING_PARCEL = "save_parcel"; + public static final String EDIT_KEYRING_PASSPHRASE = "passphrase"; // delete file securely public static final String DELETE_FILE = "deleteFile"; @@ -409,21 +412,22 @@ public class KeychainIntentService extends IntentService } catch (Exception e) { sendErrorToHandler(e); } - } else if (ACTION_SAVE_KEYRING.equals(action)) { + } else if (ACTION_EDIT_KEYRING.equals(action)) { try { /* Input */ - SaveKeyringParcel saveParcel = data.getParcelable(SAVE_KEYRING_PARCEL); + SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL); if (saveParcel == null) { Log.e(Constants.TAG, "bug: missing save_keyring_parcel in data!"); return; } /* Operation */ - PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 10, 60, 100)); + PgpKeyOperation keyOperations = + new PgpKeyOperation(new ProgressScaler(this, 10, 60, 100), mActionCanceled); EditKeyResult modifyResult; if (saveParcel.mMasterKeyId != null) { - String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE); + String passphrase = data.getString(EDIT_KEYRING_PASSPHRASE); CanonicalizedSecretKeyRing secRing = new ProviderHelper(this).getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId); @@ -445,6 +449,20 @@ public class KeychainIntentService extends IntentService UncachedKeyRing ring = modifyResult.getRing(); + // Check if the action was cancelled + if (mActionCanceled.get()) { + OperationLog log = modifyResult.getLog(); + // If it wasn't added before, add log entry + if (!modifyResult.cancelled()) { + log.add(LogLevel.CANCELLED, LogType.MSG_OPERATION_CANCELLED, 0); + } + // If so, just stop without saving + SaveKeyringResult saveResult = new SaveKeyringResult( + SaveKeyringResult.RESULT_CANCELLED, log, null); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult); + return; + } + // Save the keyring. The ProviderHelper is initialized with the previous log SaveKeyringResult saveResult = new ProviderHelper(this, modifyResult.getLog()) .saveSecretKeyRing(ring, new ProgressScaler(this, 60, 95, 100)); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 151c1761e..4e89d0243 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -86,7 +86,11 @@ public class OperationResultParcel implements Parcelable { } public boolean success() { - return (mResult & 1) == 0; + return (mResult & RESULT_ERROR) == 0; + } + + public boolean cancelled() { + return (mResult & RESULT_CANCELLED) == RESULT_CANCELLED; } public OperationLog getLog() { @@ -151,30 +155,25 @@ public class OperationResultParcel implements Parcelable { public SuperCardToast createNotify(final Activity activity) { - int resultType = getResult(); - String str; - int duration, color; + int color; // Not an overall failure - if ((resultType & OperationResultParcel.RESULT_ERROR) == 0) { - + if (cancelled()) { + color = Style.RED; + str = "operation cancelled!"; + } else if (success()) { if (getLog().containsWarnings()) { color = Style.ORANGE; } else { color = Style.GREEN; } - str = "operation succeeded!"; // str = activity.getString(R.string.import_error); - } else { - color = Style.RED; - str = "operation failed"; // str = activity.getString(R.string.import_error); - } boolean button = getLog() != null && !getLog().isEmpty(); @@ -227,7 +226,8 @@ public class OperationResultParcel implements Parcelable { */ public static enum LogType { - INTERNAL_ERROR (R.string.internal_error), + MSG_INTERNAL_ERROR (R.string.msg_internal_error), + MSG_OPERATION_CANCELLED (R.string.msg_cancelled), // import public MSG_IP(R.string.msg_ip), @@ -444,6 +444,7 @@ public class OperationResultParcel implements Parcelable { ERROR, // should occur once at the end of a failed operation START, // should occur once at the start of each independent operation OK, // should occur once at the end of a successful operation + CANCELLED, // should occur once at the end of a cancelled operation } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java index 69f4af04b..fbfa5ee08 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -31,7 +31,6 @@ import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.TextView; -import org.spongycastle.bcpg.PublicKeyAlgorithmTags; import org.spongycastle.bcpg.sig.KeyFlags; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -126,7 +125,7 @@ public class CreateKeyFinalFragment extends Fragment { private void createKey() { Intent intent = new Intent(getActivity(), KeychainIntentService.class); - intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING); + intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING); KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( getActivity(), @@ -178,7 +177,7 @@ public class CreateKeyFinalFragment extends Fragment { parcel.mNewPassphrase = mPassphrase; // get selected key entries - data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, parcel); + data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, parcel); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java index 28692f638..2137af65f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -507,7 +507,8 @@ public class EditKeyFragment extends LoaderFragment implements KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( getActivity(), getString(R.string.progress_saving), - ProgressDialog.STYLE_HORIZONTAL) { + ProgressDialog.STYLE_HORIZONTAL, + true) { public void handleMessage(Message message) { // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); @@ -543,12 +544,12 @@ public class EditKeyFragment extends LoaderFragment implements // Send all information needed to service to import key in other thread Intent intent = new Intent(getActivity(), KeychainIntentService.class); - intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING); + intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING); // fill values for this action Bundle data = new Bundle(); - data.putString(KeychainIntentService.SAVE_KEYRING_PASSPHRASE, passphrase); - data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, mSaveKeyringParcel); + data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); + data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); // Create a new Messenger for the communication back @@ -560,5 +561,6 @@ public class EditKeyFragment extends LoaderFragment implements // start service with intent getActivity().startService(intent); + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index e524c3870..1202de1b7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -192,6 +192,7 @@ public class LogDisplayFragment extends ListFragment implements OnTouchListener case ERROR: ih.mImg.setBackgroundColor(Color.RED); break; case START: ih.mImg.setBackgroundColor(Color.GREEN); break; case OK: ih.mImg.setBackgroundColor(Color.GREEN); break; + case CANCELLED: ih.mImg.setBackgroundColor(Color.RED); break; } return convertView; diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml index 6c5e12266..19b3c38e4 100644 --- a/OpenKeychain/src/main/res/values-de/strings.xml +++ b/OpenKeychain/src/main/res/values-de/strings.xml @@ -443,7 +443,7 @@ - Interner Fehler! + Interner Fehler! Beglaubiger Zertifikatdetails Identität diff --git a/OpenKeychain/src/main/res/values-es/strings.xml b/OpenKeychain/src/main/res/values-es/strings.xml index bd72a4fed..1bd73f389 100644 --- a/OpenKeychain/src/main/res/values-es/strings.xml +++ b/OpenKeychain/src/main/res/values-es/strings.xml @@ -662,7 +662,7 @@ Limpiar caché Contraseña - ¡Error interno! + ¡Error interno! Certificador Detalles del certificado Identidad diff --git a/OpenKeychain/src/main/res/values-fr/strings.xml b/OpenKeychain/src/main/res/values-fr/strings.xml index 3aa0f1f5d..1b0a355d3 100644 --- a/OpenKeychain/src/main/res/values-fr/strings.xml +++ b/OpenKeychain/src/main/res/values-fr/strings.xml @@ -662,7 +662,7 @@ Effacer le cache Mot de passe - Erreur interne ! + Erreur interne ! Certificateur Détails du certificat identité diff --git a/OpenKeychain/src/main/res/values-it/strings.xml b/OpenKeychain/src/main/res/values-it/strings.xml index b8707ab70..d117b44d8 100644 --- a/OpenKeychain/src/main/res/values-it/strings.xml +++ b/OpenKeychain/src/main/res/values-it/strings.xml @@ -662,7 +662,7 @@ Pulisci Cache Password - Errore interno! + Errore interno! Certificatore Dettagli Certificato Identit diff --git a/OpenKeychain/src/main/res/values-ja/strings.xml b/OpenKeychain/src/main/res/values-ja/strings.xml index 41426cb9b..5859a85ff 100644 --- a/OpenKeychain/src/main/res/values-ja/strings.xml +++ b/OpenKeychain/src/main/res/values-ja/strings.xml @@ -637,7 +637,7 @@ キャッシュクリア パスワード - 内部エラー! + 内部エラー! 検証者 証明の詳細 ユーザID diff --git a/OpenKeychain/src/main/res/values-uk/strings.xml b/OpenKeychain/src/main/res/values-uk/strings.xml index b94749ee5..48c180103 100644 --- a/OpenKeychain/src/main/res/values-uk/strings.xml +++ b/OpenKeychain/src/main/res/values-uk/strings.xml @@ -601,7 +601,7 @@ Очистити кеш Пароль - Внутрішня помилка! + Внутрішня помилка! Ким підписаний Дані сертифікату Сутність diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 270feb099..056350832 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -262,6 +262,7 @@ Done. Cancel + cancelling… saving… importing… exporting… @@ -519,6 +520,9 @@ + Internal error! + Operation cancelled. + Applying insert batch operation. Tried to import secret keyring as public. This is a bug, please file a report! @@ -744,7 +748,6 @@ Password - Internal error! Certifier Certificate Details Identity From d17b478a9ea13d3fd11cc7112f60cd9de43ec8db Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 31 Aug 2014 19:30:28 +0200 Subject: [PATCH 4/5] don't make our service sticky --- .../keychain/service/KeychainIntentService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 5a147d646..f865c58e2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -904,7 +904,7 @@ public class KeychainIntentService extends IntentService public int onStartCommand(Intent intent, int flags, int startId) { if (ACTION_CANCEL.equals(intent.getAction())) { mActionCanceled.set(true); - return START_STICKY; + return START_NOT_STICKY; } return super.onStartCommand(intent, flags, startId); } From e9a2f256b9324cc898061d2a2ebde18446ce1dd1 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 31 Aug 2014 20:05:19 +0200 Subject: [PATCH 5/5] add cancel prevention mechanism, improve cancellation for key import --- .../keychain/pgp/PgpImportExport.java | 28 ++++++------ .../service/KeychainIntentService.java | 9 +++- .../service/KeychainIntentServiceHandler.java | 5 ++- .../keychain/ui/CreateKeyFinalFragment.java | 1 - .../ui/dialog/ProgressDialogFragment.java | 45 +++++++++++++++++-- 5 files changed, 67 insertions(+), 21 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index 669a7d37e..f5b2280c9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -33,6 +33,8 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult; import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; @@ -57,6 +59,7 @@ public class PgpImportExport { private Context mContext; private Progressable mProgressable; + private AtomicBoolean mCancelled; private KeychainServiceListener mKeychainServiceListener; @@ -73,6 +76,14 @@ public class PgpImportExport { this.mProviderHelper = providerHelper; } + public PgpImportExport(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) { + super(); + mContext = context; + mProgressable = progressable; + mProviderHelper = providerHelper; + mCancelled = cancelled; + } + public PgpImportExport(Context context, Progressable progressable, KeychainServiceListener keychainListener) { super(); @@ -126,20 +137,11 @@ public class PgpImportExport { } /** Imports keys from given data. If keyIds is given only those are imported */ - public ImportKeyResult importKeyRings(List entries, - AtomicBoolean cancelled) { - return importKeyRings(entries.iterator(), entries.size(), cancelled); - } - public ImportKeyResult importKeyRings(List entries) { - return importKeyRings(entries.iterator(), entries.size(), null); + return importKeyRings(entries.iterator(), entries.size()); } public ImportKeyResult importKeyRings(Iterator entries, int num) { - return importKeyRings(entries, num, null); - } - public ImportKeyResult importKeyRings(Iterator entries, int num, - AtomicBoolean cancelled) { updateProgress(R.string.progress_importing, 0, 100); // If there aren't even any keys, do nothing here. @@ -154,8 +156,7 @@ public class PgpImportExport { double progSteps = 100.0 / num; for (ParcelableKeyRing entry : new IterableIterator(entries)) { // Has this action been cancelled? If so, don't proceed any further - if (cancelled != null && cancelled.get()) { - Log.d(Constants.TAG, "CANCELLED!"); + if (mCancelled != null && mCancelled.get()) { break; } @@ -227,7 +228,8 @@ public class PgpImportExport { resultType |= ImportKeyResult.RESULT_WARNINGS; } } - if (cancelled != null && cancelled.get()) { + if (mCancelled != null && mCancelled.get()) { + log.add(LogLevel.CANCELLED, LogType.MSG_OPERATION_CANCELLED, 0); resultType |= ImportKeyResult.RESULT_CANCELLED; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index f865c58e2..2073d6a6c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -526,12 +526,17 @@ public class KeychainIntentService extends IntentService } ProviderHelper providerHelper = new ProviderHelper(this); - PgpImportExport pgpImportExport = new PgpImportExport(this, providerHelper, this); - ImportKeyResult result = pgpImportExport.importKeyRings(entries, mActionCanceled); + PgpImportExport pgpImportExport = new PgpImportExport( + this, providerHelper, this, mActionCanceled); + ImportKeyResult result = pgpImportExport.importKeyRings(entries); + // we do this even on failure or cancellation! if (result.mSecret > 0) { + // cannot cancel from here on out! + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_PREVENT_CANCEL); providerHelper.consolidateDatabaseStep1(this); } + // make sure new data is synced into contacts ContactSyncAdapterService.requestSync(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java index 1772dc6ae..940777458 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java @@ -18,7 +18,6 @@ package org.sufficientlysecure.keychain.service; import android.app.Activity; -import android.content.DialogInterface.OnCancelListener; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -37,6 +36,7 @@ public class KeychainIntentServiceHandler extends Handler { public static final int MESSAGE_OKAY = 1; public static final int MESSAGE_EXCEPTION = 2; public static final int MESSAGE_UPDATE_PROGRESS = 3; + public static final int MESSAGE_PREVENT_CANCEL = 4; // possible data keys for messages public static final String DATA_ERROR = "error"; @@ -124,6 +124,9 @@ public class KeychainIntentServiceHandler extends Handler { break; + case MESSAGE_PREVENT_CANCEL: + mProgressDialogFragment.setPreventCancel(true); + default: Log.e(Constants.TAG, "unknown handler message!"); break; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java index fbfa5ee08..ec4f2e337 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -44,7 +44,6 @@ import org.sufficientlysecure.keychain.service.OperationResults; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.Notify; public class CreateKeyFinalFragment extends Fragment { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java index 49a8ac49f..d09be2d51 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java @@ -23,6 +23,7 @@ import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnKeyListener; import android.content.Intent; +import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.view.ContextThemeWrapper; @@ -39,6 +40,8 @@ public class ProgressDialogFragment extends DialogFragment { private static final String ARG_STYLE = "style"; private static final String ARG_CANCELABLE = "cancelable"; + boolean mCanCancel = false, mPreventCancel = false, mIsCancelled = false; + /** Creates new instance of this fragment */ public static ProgressDialogFragment newInstance(String message, int style, boolean cancelable) { ProgressDialogFragment frag = new ProgressDialogFragment(); @@ -59,6 +62,10 @@ public class ProgressDialogFragment extends DialogFragment { /** Updates progress of dialog */ public void setProgress(int progress, int max) { + if (mIsCancelled) { + return; + } + ProgressDialog dialog = (ProgressDialog) getDialog(); dialog.setProgress(progress); @@ -67,6 +74,10 @@ public class ProgressDialogFragment extends DialogFragment { /** Updates progress of dialog */ public void setProgress(String message, int progress, int max) { + if (mIsCancelled) { + return; + } + ProgressDialog dialog = (ProgressDialog) getDialog(); dialog.setMessage(message); @@ -94,13 +105,13 @@ public class ProgressDialogFragment extends DialogFragment { String message = getArguments().getString(ARG_MESSAGE); int style = getArguments().getInt(ARG_STYLE); - final boolean cancelable = getArguments().getBoolean(ARG_CANCELABLE); + mCanCancel = getArguments().getBoolean(ARG_CANCELABLE); dialog.setMessage(message); dialog.setProgressStyle(style); // If this is supposed to be cancelable, add our (custom) cancel mechanic - if (cancelable) { + if (mCanCancel) { // Just show the button, take care of the onClickListener afterwards (in onStart) dialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getString(R.string.progress_cancel), (DialogInterface.OnClickListener) null); @@ -111,7 +122,7 @@ public class ProgressDialogFragment extends DialogFragment { @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { - if (cancelable) { + if (mCanCancel) { ((ProgressDialog) dialog).getButton( DialogInterface.BUTTON_NEGATIVE).performClick(); } @@ -126,15 +137,41 @@ public class ProgressDialogFragment extends DialogFragment { return dialog; } + public void setPreventCancel(boolean preventCancel) { + // Don't care if we can't cancel anymore either way! + if (mIsCancelled || ! mCanCancel) { + return; + } + + mPreventCancel = preventCancel; + final Button negative = ((ProgressDialog) getDialog()).getButton(DialogInterface.BUTTON_NEGATIVE); + negative.setVisibility(preventCancel ? View.GONE : View.VISIBLE); + } + @Override public void onStart() { super.onStart(); // Override the default behavior so the dialog is NOT dismissed on click - Button negative = ((ProgressDialog) getDialog()).getButton(DialogInterface.BUTTON_NEGATIVE); + final Button negative = ((ProgressDialog) getDialog()).getButton(DialogInterface.BUTTON_NEGATIVE); negative.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { + // nvm if we are already cancelled, or weren't able to begin with + if (mIsCancelled || ! mCanCancel) { + return; + } + + // Remember this, and don't allow another click + mIsCancelled = true; + negative.setClickable(false); + negative.setTextColor(Color.GRAY); + + // Set the progress bar accordingly + ProgressDialog dialog = (ProgressDialog) getDialog(); + dialog.setIndeterminate(true); + dialog.setMessage(getString(R.string.progress_cancelling)); + // send a cancel message. note that this message will be handled by // KeychainIntentService.onStartCommand, which runs in this thread, // not the service one, and will not queue up a command.