From b11d5e667ef36b3f592e69b5d00f6d0bfd382baa Mon Sep 17 00:00:00 2001 From: ianjazz246 Date: Sat, 20 May 2023 10:19:14 -0700 Subject: [PATCH] Hide progress dialogs when dialog requesting user input is showing --- src/keepass2android/app/App.cs | 135 +++++++++++++++++++++++++++------ 1 file changed, 111 insertions(+), 24 deletions(-) diff --git a/src/keepass2android/app/App.cs b/src/keepass2android/app/App.cs index 81b0aa7d..c5dd844e 100644 --- a/src/keepass2android/app/App.cs +++ b/src/keepass2android/app/App.cs @@ -111,7 +111,7 @@ namespace keepass2android public class Kp2aApp: IKp2aApp, ICacheSupervisor { - + public void Lock(bool allowQuickUnlock = true, bool lockWasTriggeredByTimeout = false) { @@ -467,8 +467,11 @@ namespace keepass2android } } - - private void AskForReload(Activity activity, Action actionOnResult) + private HashSet _activeProgressDialogs = new HashSet(); + // Whether the app is currently showing a dialog that requires user input, like a yesNoCancel dialog + private bool _isShowingUserInputDialog = false; + + private void AskForReload(Activity activity, Action actionOnResult) { AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.SetTitle(activity.GetString(Resource.String.AskReloadFile_title)); @@ -489,6 +492,8 @@ namespace keepass2android actionOnResult(true); actionOnResult = null; } + + OnUserInputDialogClose(); }); builder.SetNegativeButton(activity.GetString(Android.Resource.String.No), (dlgSender, dlgEvt) => @@ -499,8 +504,9 @@ namespace keepass2android actionOnResult(false); actionOnResult = null; } - }); + OnUserInputDialogClose(); + }); Dialog dialog = builder.Create(); @@ -516,9 +522,12 @@ namespace keepass2android actionOnResult(false); actionOnResult = null; } + + OnUserInputDialogClose(); })); - dialog.Show(); + OnUserInputDialogShow(); + dialog.Show(); } public void StoreOpenedFileAsRecent(IOConnectionInfo ioc, string keyfile, bool updateTimestamp, string displayName = "") @@ -581,35 +590,59 @@ namespace keepass2android EventHandler dismissHandler, Context ctx, string messageSuffix = "") { - Handler handler = new Handler(Looper.MainLooper); + Handler handler = new Handler(Looper.MainLooper); handler.Post(() => { AlertDialog.Builder builder = new AlertDialog.Builder(ctx); builder.SetTitle(GetResourceString(titleKey)); - builder.SetMessage(GetResourceString(messageKey)+(messageSuffix != "" ? " " + messageSuffix: "")); + builder.SetMessage(GetResourceString(messageKey) + (messageSuffix != "" ? " " + messageSuffix : "")); - string yesText = GetResourceString(yesString); - builder.SetPositiveButton(yesText, yesHandler); + // _____handlerWithShow are wrappers around given handlers to update _isSHowingYesNoCancelDialog + // and to show progress dialog after yesNoCancel dialog is closed + EventHandler yesHandlerWithShow = (sender, args) => + { + OnUserInputDialogClose(); + yesHandler.Invoke(sender, args); + }; + string yesText = GetResourceString(yesString); + builder.SetPositiveButton(yesText, yesHandlerWithShow); string noText = ""; if (noHandler != null) { - noText = GetResourceString(noString); - builder.SetNegativeButton(noText, noHandler); + EventHandler noHandlerWithShow = (sender, args) => + { + OnUserInputDialogClose(); + noHandler.Invoke(sender, args); + }; + + noText = GetResourceString(noString); + builder.SetNegativeButton(noText, noHandlerWithShow); } string cancelText = ""; if (cancelHandler != null) { - cancelText = ctx.GetString(Android.Resource.String.Cancel); - builder.SetNeutralButton(cancelText, - cancelHandler); - } + EventHandler cancelHandlerWithShow = (sender, args) => + { + OnUserInputDialogClose(); + cancelHandler.Invoke(sender, args); + }; - + cancelText = ctx.GetString(Android.Resource.String.Cancel); + builder.SetNeutralButton(cancelText, + cancelHandlerWithShow); + } AlertDialog dialog = builder.Create(); if (dismissHandler != null) - dialog.SetOnDismissListener(new Util.DismissListener(() => dismissHandler(dialog, EventArgs.Empty))); + { + dialog.SetOnDismissListener(new Util.DismissListener(() => { + OnUserInputDialogClose(); + dismissHandler(dialog, EventArgs.Empty); + })); + } + + OnUserInputDialogShow(); dialog.Show(); if (yesText.Length + noText.Length + cancelText.Length >= 20) @@ -626,12 +659,53 @@ namespace keepass2android } } - } ); } - public Handler UiThreadHandler + /// + /// Shows all non-dismissed progress dialogs. + /// If there are multiple progressDialogs active, they all will be showing. + /// There probably will never be multiple dialogs at the same time because only one ProgressTask can run at a time. + /// Even if multiple dialogs show at the same time, it shouldn't be too much of an issue + /// because they are just progress indicators. + /// + private void ShowAllActiveProgressDialogs() + { + foreach (RealProgressDialog progressDialog in _activeProgressDialogs) + { + progressDialog.Show(); + } + } + + private void HideAllActiveProgressDialogs() + { + foreach (RealProgressDialog progressDialog in _activeProgressDialogs) + { + progressDialog.Hide(); + } + } + + /// + /// Hide progress dialogs whenever a dialog that requires user interaction + /// appears so that the progress dialogs cannot cover the user-interaction dialog + /// + private void OnUserInputDialogShow() + { + _isShowingUserInputDialog = true; + HideAllActiveProgressDialogs(); + } + + /// + /// Show previously hidden progress dialogs after user interaction with dialog finished + /// + private void OnUserInputDialogClose() + { + _isShowingUserInputDialog = false; + ShowAllActiveProgressDialogs(); + } + + public Handler UiThreadHandler { get { return new Handler(); } } @@ -642,9 +716,11 @@ namespace keepass2android private class RealProgressDialog : IProgressDialog { private readonly ProgressDialog _pd; + private readonly Kp2aApp _app; - public RealProgressDialog(Context ctx) + public RealProgressDialog(Context ctx, Kp2aApp app) { + _app = app; _pd = new ProgressDialog(ctx); _pd.SetCancelable(false); } @@ -669,18 +745,29 @@ namespace keepass2android { Kp2aLog.LogUnexpectedError(e); } - - } + _app._activeProgressDialogs.Remove(this); + } public void Show() { - _pd.Show(); + _app._activeProgressDialogs.Add(this); + // Only show if asking dialog not also showing + if (!_app._isShowingUserInputDialog) + { + _pd.Show(); + } + } + + public void Hide() + { + _pd.Hide(); } } public IProgressDialog CreateProgressDialog(Context ctx) { - return new RealProgressDialog(ctx); + var newProgressDialog = new RealProgressDialog(ctx, this); + return newProgressDialog; } public IFileStorage GetFileStorage(IOConnectionInfo iocInfo)