add support for yes/no/cancel question during background sync
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// stream for reading the data from the original file. If this is set to a non-null value, we know we need to sync
|
||||
/// </summary>
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -1255,5 +1255,5 @@
|
||||
<string name="switch_keyboard_for_totp_enabled">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.</string>
|
||||
<string name="switch_keyboard_inside_kp2a_enabled">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.</string>
|
||||
<string name="switch_keyboard_on_search_enabled">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.</string>
|
||||
|
||||
<string name="user_interaction_required">User interaction required. Please open the app.</string>
|
||||
</resources>
|
||||
@@ -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<bool> 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<DialogClickEventArgs>? YesHandler { get; set; }
|
||||
public EventHandler<DialogClickEventArgs>? NoHandler { get; set; }
|
||||
public EventHandler<DialogClickEventArgs>? 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<DialogClickEventArgs> yesHandlerWithShow = (sender, args) =>
|
||||
{
|
||||
onUserInputDialogClose();
|
||||
YesHandler.Invoke(sender, args);
|
||||
};
|
||||
string yesText = app.GetResourceString(YesString);
|
||||
builder.SetPositiveButton(yesText, yesHandlerWithShow);
|
||||
string noText = "";
|
||||
if (NoHandler != null)
|
||||
{
|
||||
EventHandler<DialogClickEventArgs> noHandlerWithShow = (sender, args) =>
|
||||
{
|
||||
onUserInputDialogClose();
|
||||
NoHandler.Invoke(sender, args);
|
||||
};
|
||||
|
||||
noText = app.GetResourceString(NoString);
|
||||
builder.SetNegativeButton(noText, noHandlerWithShow);
|
||||
}
|
||||
|
||||
string cancelText = "";
|
||||
if (CancelHandler != null)
|
||||
{
|
||||
EventHandler<DialogClickEventArgs> 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<DialogClickEventArgs> yesHandler,
|
||||
EventHandler<DialogClickEventArgs> noHandler,
|
||||
EventHandler<DialogClickEventArgs> 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<DialogClickEventArgs> yesHandlerWithShow = (sender, args) =>
|
||||
{
|
||||
OnUserInputDialogClose();
|
||||
yesHandler.Invoke(sender, args);
|
||||
};
|
||||
string yesText = GetResourceString(yesString);
|
||||
builder.SetPositiveButton(yesText, yesHandlerWithShow);
|
||||
string noText = "";
|
||||
if (noHandler != null)
|
||||
{
|
||||
EventHandler<DialogClickEventArgs> noHandlerWithShow = (sender, args) =>
|
||||
{
|
||||
OnUserInputDialogClose();
|
||||
noHandler.Invoke(sender, args);
|
||||
};
|
||||
|
||||
noText = GetResourceString(noString);
|
||||
builder.SetNegativeButton(noText, noHandlerWithShow);
|
||||
}
|
||||
string cancelText = "";
|
||||
if (cancelHandler != null)
|
||||
{
|
||||
EventHandler<DialogClickEventArgs> 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);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user