diff --git a/src/Kp2aBusinessLogic/BlockingOperationRunner.cs b/src/Kp2aBusinessLogic/BlockingOperationRunner.cs index 377eec45..eff116c6 100644 --- a/src/Kp2aBusinessLogic/BlockingOperationRunner.cs +++ b/src/Kp2aBusinessLogic/BlockingOperationRunner.cs @@ -45,6 +45,8 @@ namespace keepass2android } } + public ProgressUiAsStatusLoggerAdapter StatusLogger => _statusLogger; + private BackgroundOperationRunner() { //private constructor @@ -77,7 +79,6 @@ namespace keepass2android if (!_taskQueue.Any()) { _thread = null; - _currentlyRunningTask = null; _statusLogger.EndLogging(); break; } @@ -86,7 +87,18 @@ namespace keepass2android _currentlyRunningTask = _taskQueue.Dequeue(); } } + + var originalFinishedHandler = _currentlyRunningTask.operationFinishedHandler; + _currentlyRunningTask.operationFinishedHandler = new ActionOnOperationFinished(app, ( + (success, message, context) => + { + _currentlyRunningTask = null; + }), originalFinishedHandler); _currentlyRunningTask.Run(); + while (_currentlyRunningTask != null) + { + Thread.Sleep(100); + } } }); @@ -248,13 +260,7 @@ namespace keepass2android get { return _activeActivity; } private set { - if (_activeActivity != null && _activeActivity != _previouslyActiveContext) - { - _previouslyActiveContext = _activeActivity; - - - } - _activeActivity = value; + _activeActivity = value; if (_activeActivity != null) { @@ -264,18 +270,12 @@ namespace keepass2android } } - public Context PreviouslyActiveContext - { - get { return _previouslyActiveContext; } - - } - - private readonly Handler _handler; + private readonly Handler _handler; private readonly OperationWithFinishHandler _task; private IProgressDialog _progressDialog; private readonly IKp2aApp _app; private Java.Lang.Thread _thread; - private Context _activeActivity, _previouslyActiveContext; + private Context _activeActivity; private ProgressDialogStatusLogger _progressDialogStatusLogger; public BlockingOperationRunner(IKp2aApp app, OperationWithFinishHandler task) diff --git a/src/Kp2aBusinessLogic/database/SynchronizeCachedDatabase.cs b/src/Kp2aBusinessLogic/database/SynchronizeCachedDatabase.cs index 1d5707fd..552cc021 100644 --- a/src/Kp2aBusinessLogic/database/SynchronizeCachedDatabase.cs +++ b/src/Kp2aBusinessLogic/database/SynchronizeCachedDatabase.cs @@ -82,6 +82,8 @@ namespace keepass2android } _saveDb = null; }), _app.CurrentDb, false, remoteData); + _saveDb.SetStatusLogger(StatusLogger); + _saveDb.DoNotSetStatusLoggerMessage = true; //Keep "sync db" as main message _saveDb.SyncInBackground = false; _saveDb.Run(); diff --git a/src/Kp2aBusinessLogic/database/edit/ActionOnOperationFinished.cs b/src/Kp2aBusinessLogic/database/edit/ActionOnOperationFinished.cs index 373d1bb8..c17c36de 100644 --- a/src/Kp2aBusinessLogic/database/edit/ActionOnOperationFinished.cs +++ b/src/Kp2aBusinessLogic/database/edit/ActionOnOperationFinished.cs @@ -38,9 +38,6 @@ namespace keepass2android _actionToPerform = actionToPerform; } - //if set to true, the previously active active will be passed to ActionToPerformOnFinish instead null if no activity is on foreground - public bool AllowInactiveActivity { get; set; } - public override void Run() { if (Message == null) diff --git a/src/Kp2aBusinessLogic/database/edit/SaveDB.cs b/src/Kp2aBusinessLogic/database/edit/SaveDB.cs index b8f10d19..41710977 100644 --- a/src/Kp2aBusinessLogic/database/edit/SaveDB.cs +++ b/src/Kp2aBusinessLogic/database/edit/SaveDB.cs @@ -41,6 +41,8 @@ namespace keepass2android private readonly bool _dontSave; private bool requiresSubsequentSync = false; //if true, we need to sync the file after saving. + public bool DoNotSetStatusLoggerMessage = false; + /// /// stream for reading the data from the original file. If this is set to a non-null value, we know we need to sync /// @@ -104,9 +106,12 @@ namespace keepass2android if (ShowDatabaseIocInStatus) message += " (" + _app.GetFileStorage(_db.Ioc).GetDisplayName(_db.Ioc) + ")"; - StatusLogger.UpdateMessage(message); - - IOConnectionInfo ioc = _db.Ioc; + if (!DoNotSetStatusLoggerMessage) + { + StatusLogger.UpdateMessage(message); + } + + IOConnectionInfo ioc = _db.Ioc; IFileStorage fileStorage = _app.GetFileStorage(ioc); if (SyncInBackground && fileStorage is IOfflineSwitchable offlineSwitchable) diff --git a/src/keepass2android-app/GroupBaseActivity.cs b/src/keepass2android-app/GroupBaseActivity.cs index 365df0d0..036a6b29 100644 --- a/src/keepass2android-app/GroupBaseActivity.cs +++ b/src/keepass2android-app/GroupBaseActivity.cs @@ -1305,7 +1305,7 @@ namespace keepass2android { if (Success) { - ((GroupBaseActivity)ActiveContext)?.RefreshIfDirty(); + (ActiveContext as GroupBaseActivity)?.RefreshIfDirty(); } else { @@ -1325,7 +1325,7 @@ namespace keepass2android { if (Success) { - ((GroupBaseActivity)ActiveContext)?.RefreshIfDirty(); + (ActiveContext as GroupBaseActivity)?.RefreshIfDirty(); } else { diff --git a/src/keepass2android-app/Resources/values/strings.xml b/src/keepass2android-app/Resources/values/strings.xml index e012dd2a..a43e9497 100644 --- a/src/keepass2android-app/Resources/values/strings.xml +++ b/src/keepass2android-app/Resources/values/strings.xml @@ -1255,5 +1255,5 @@ Note: You have enabled App - Password access - Autofill-Service - Autofill for TOTP entries. This can cause this window to show when you open an entry with a TOTP. Note: You have enabled App - Security - Use built-in keyboard inside Keepass2Android. This can cause this window to show when you open the app or edit an entry. Note: You have enabled App - Security - Password access - Keyboard switching - Switch keyboard. This can cause this window to show when you search for an entry from the browser. - + User interaction required. Please open the app. \ No newline at end of file diff --git a/src/keepass2android-app/app/App.cs b/src/keepass2android-app/app/App.cs index 7dc66fd7..8db785a4 100644 --- a/src/keepass2android-app/app/App.cs +++ b/src/keepass2android-app/app/App.cs @@ -17,6 +17,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file using System; using System.Collections.Generic; +using System.ComponentModel.Design; using System.IO; using System.Linq; using System.Net.Security; @@ -46,6 +47,7 @@ using keepass2android; using keepass2android.Utils; using KeePassLib.Interfaces; using KeePassLib.Utility; +using AlertDialog = AndroidX.AppCompat.App.AlertDialog; using Message = keepass2android.Utils.Message; #if !NoNet #if !EXCLUDE_JAVAFILESTORAGE @@ -480,6 +482,8 @@ namespace keepass2android // Whether the app is currently showing a dialog that requires user input, like a yesNoCancel dialog private bool _isShowingUserInputDialog = false; private IMessagePresenter? _messagePresenter; + private YesNoCancelQuestion? _currentlyPendingYesNoCancelQuestion = null; + private Context _activeContext; private void AskForReload(Activity activity, Action actionOnResult) { @@ -591,92 +595,142 @@ namespace keepass2android AskYesNoCancel(titleKey, messageKey, yesString, noString, yesHandler, noHandler, cancelHandler, null, messageSuffix); } - public void AskYesNoCancel(UiStringKey titleKey, UiStringKey messageKey, + class YesNoCancelQuestion + { + private AlertDialog? _dialog; + public UiStringKey TitleKey { get; set; } + public UiStringKey MessageKey { get; set; } + public UiStringKey YesString { get; set; } + public UiStringKey NoString { get; set; } + public EventHandler? YesHandler { get; set; } + public EventHandler? NoHandler { get; set; } + public EventHandler? CancelHandler { get; set; } + public EventHandler DismissHandler { get; set; } + public string MessageSuffix { get; set; } + + public bool TryShow(IKp2aApp app, Action onUserInputDialogClose, Action onUserInputDialogShow) + { + if (app.ActiveContext is Activity activity) + { + Handler handler = new Handler(Looper.MainLooper); + handler.Post(() => + { + if (_dialog is { IsShowing: true }) + { + _dialog.Dismiss(); + _dialog = null; + } + + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity); + builder.SetTitle(app.GetResourceString(TitleKey)); + + builder.SetMessage(app.GetResourceString(MessageKey) + + (MessageSuffix != "" ? " " + MessageSuffix : "")); + + // _____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 = app.GetResourceString(YesString); + builder.SetPositiveButton(yesText, yesHandlerWithShow); + string noText = ""; + if (NoHandler != null) + { + EventHandler noHandlerWithShow = (sender, args) => + { + onUserInputDialogClose(); + NoHandler.Invoke(sender, args); + }; + + noText = app.GetResourceString(NoString); + builder.SetNegativeButton(noText, noHandlerWithShow); + } + + string cancelText = ""; + if (CancelHandler != null) + { + EventHandler cancelHandlerWithShow = (sender, args) => + { + onUserInputDialogClose(); + CancelHandler.Invoke(sender, args); + }; + + cancelText = App.Context.GetString(Android.Resource.String.Cancel); + builder.SetNeutralButton(cancelText, + cancelHandlerWithShow); + } + + _dialog = builder.Create(); + if (DismissHandler != null) + { + _dialog.SetOnDismissListener(new Util.DismissListener(() => + { + onUserInputDialogClose(); + DismissHandler(_dialog, EventArgs.Empty); + })); + } + + onUserInputDialogShow(); + try + { + _dialog.Show(); + } + catch (Exception e) + { + Kp2aLog.LogUnexpectedError(e); + } + + + if (yesText.Length + noText.Length + cancelText.Length >= 20) + { + try + { + Button button = _dialog.GetButton((int)DialogButtonType.Positive); + LinearLayout linearLayout = (LinearLayout)button.Parent; + linearLayout.Orientation = Orientation.Vertical; + } + catch (Exception e) + { + Kp2aLog.LogUnexpectedError(e); + } + + } + }); + return true; + } + else + { + BackgroundOperationRunner.Instance.StatusLogger?.UpdateSubMessage(App.Context.GetString(Resource.String.user_interaction_required)); + return false; + } + } + } + + public void AskYesNoCancel(UiStringKey titleKey, UiStringKey messageKey, UiStringKey yesString, UiStringKey noString, EventHandler yesHandler, EventHandler noHandler, EventHandler cancelHandler, EventHandler dismissHandler,string messageSuffix = "") { - Handler handler = new Handler(Looper.MainLooper); - handler.Post(() => - { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(ActiveContext); - builder.SetTitle(GetResourceString(titleKey)); - - builder.SetMessage(GetResourceString(messageKey) + (messageSuffix != "" ? " " + messageSuffix : "")); - - // _____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) - { - EventHandler noHandlerWithShow = (sender, args) => - { - OnUserInputDialogClose(); - noHandler.Invoke(sender, args); - }; - - noText = GetResourceString(noString); - builder.SetNegativeButton(noText, noHandlerWithShow); - } - string cancelText = ""; - if (cancelHandler != null) - { - EventHandler cancelHandlerWithShow = (sender, args) => - { - OnUserInputDialogClose(); - cancelHandler.Invoke(sender, args); - }; - - cancelText = App.Context.GetString(Android.Resource.String.Cancel); - builder.SetNeutralButton(cancelText, - cancelHandlerWithShow); - } - - var dialog = builder.Create(); - if (dismissHandler != null) - { - dialog.SetOnDismissListener(new Util.DismissListener(() => { - OnUserInputDialogClose(); - dismissHandler(dialog, EventArgs.Empty); - })); - } - - OnUserInputDialogShow(); - try - { - dialog.Show(); - } - catch (Exception e) - { - Kp2aLog.LogUnexpectedError(e); - } - - - if (yesText.Length + noText.Length + cancelText.Length >= 20) - { - try - { - Button button = dialog.GetButton((int)DialogButtonType.Positive); - LinearLayout linearLayout = (LinearLayout)button.Parent; - linearLayout.Orientation = Orientation.Vertical; - } - catch (Exception e) - { - Kp2aLog.LogUnexpectedError(e); - } - - } - } - ); + _currentlyPendingYesNoCancelQuestion = new YesNoCancelQuestion() + { + TitleKey = titleKey, + MessageKey = messageKey, + YesString = yesString, + NoString = noString, + YesHandler = yesHandler, + NoHandler = noHandler, + CancelHandler = cancelHandler, + DismissHandler = dismissHandler, + MessageSuffix = messageSuffix + }; + + _currentlyPendingYesNoCancelQuestion.TryShow(this, OnUserInputDialogClose, OnUserInputDialogShow); + } /// @@ -718,7 +772,9 @@ namespace keepass2android private void OnUserInputDialogClose() { _isShowingUserInputDialog = false; - ShowAllActiveProgressDialogs(); + _currentlyPendingYesNoCancelQuestion = null; + + ShowAllActiveProgressDialogs(); } public Handler UiThreadHandler @@ -1369,7 +1425,15 @@ namespace keepass2android } - public Context ActiveContext { get; set; } + public Context ActiveContext + { + get => _activeContext; + set + { + _activeContext = value; + _currentlyPendingYesNoCancelQuestion?.TryShow(this, OnUserInputDialogClose, OnUserInputDialogClose); + } + } }