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 BackgroundOperationRunner()
|
||||||
{
|
{
|
||||||
//private constructor
|
//private constructor
|
||||||
@@ -77,7 +79,6 @@ namespace keepass2android
|
|||||||
if (!_taskQueue.Any())
|
if (!_taskQueue.Any())
|
||||||
{
|
{
|
||||||
_thread = null;
|
_thread = null;
|
||||||
_currentlyRunningTask = null;
|
|
||||||
_statusLogger.EndLogging();
|
_statusLogger.EndLogging();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -86,7 +87,18 @@ namespace keepass2android
|
|||||||
_currentlyRunningTask = _taskQueue.Dequeue();
|
_currentlyRunningTask = _taskQueue.Dequeue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var originalFinishedHandler = _currentlyRunningTask.operationFinishedHandler;
|
||||||
|
_currentlyRunningTask.operationFinishedHandler = new ActionOnOperationFinished(app, (
|
||||||
|
(success, message, context) =>
|
||||||
|
{
|
||||||
|
_currentlyRunningTask = null;
|
||||||
|
}), originalFinishedHandler);
|
||||||
_currentlyRunningTask.Run();
|
_currentlyRunningTask.Run();
|
||||||
|
while (_currentlyRunningTask != null)
|
||||||
|
{
|
||||||
|
Thread.Sleep(100);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -248,13 +260,7 @@ namespace keepass2android
|
|||||||
get { return _activeActivity; }
|
get { return _activeActivity; }
|
||||||
private set
|
private set
|
||||||
{
|
{
|
||||||
if (_activeActivity != null && _activeActivity != _previouslyActiveContext)
|
_activeActivity = value;
|
||||||
{
|
|
||||||
_previouslyActiveContext = _activeActivity;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
_activeActivity = value;
|
|
||||||
|
|
||||||
if (_activeActivity != null)
|
if (_activeActivity != null)
|
||||||
{
|
{
|
||||||
@@ -264,18 +270,12 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Context PreviouslyActiveContext
|
private readonly Handler _handler;
|
||||||
{
|
|
||||||
get { return _previouslyActiveContext; }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly Handler _handler;
|
|
||||||
private readonly OperationWithFinishHandler _task;
|
private readonly OperationWithFinishHandler _task;
|
||||||
private IProgressDialog _progressDialog;
|
private IProgressDialog _progressDialog;
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
private Java.Lang.Thread _thread;
|
private Java.Lang.Thread _thread;
|
||||||
private Context _activeActivity, _previouslyActiveContext;
|
private Context _activeActivity;
|
||||||
private ProgressDialogStatusLogger _progressDialogStatusLogger;
|
private ProgressDialogStatusLogger _progressDialogStatusLogger;
|
||||||
|
|
||||||
public BlockingOperationRunner(IKp2aApp app, OperationWithFinishHandler task)
|
public BlockingOperationRunner(IKp2aApp app, OperationWithFinishHandler task)
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
_saveDb = null;
|
_saveDb = null;
|
||||||
}), _app.CurrentDb, false, remoteData);
|
}), _app.CurrentDb, false, remoteData);
|
||||||
|
_saveDb.SetStatusLogger(StatusLogger);
|
||||||
|
_saveDb.DoNotSetStatusLoggerMessage = true; //Keep "sync db" as main message
|
||||||
_saveDb.SyncInBackground = false;
|
_saveDb.SyncInBackground = false;
|
||||||
_saveDb.Run();
|
_saveDb.Run();
|
||||||
|
|
||||||
|
|||||||
@@ -38,9 +38,6 @@ namespace keepass2android
|
|||||||
_actionToPerform = actionToPerform;
|
_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()
|
public override void Run()
|
||||||
{
|
{
|
||||||
if (Message == null)
|
if (Message == null)
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ namespace keepass2android
|
|||||||
private readonly bool _dontSave;
|
private readonly bool _dontSave;
|
||||||
private bool requiresSubsequentSync = false; //if true, we need to sync the file after saving.
|
private bool requiresSubsequentSync = false; //if true, we need to sync the file after saving.
|
||||||
|
|
||||||
|
public bool DoNotSetStatusLoggerMessage = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <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
|
/// 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>
|
/// </summary>
|
||||||
@@ -104,9 +106,12 @@ namespace keepass2android
|
|||||||
if (ShowDatabaseIocInStatus)
|
if (ShowDatabaseIocInStatus)
|
||||||
message += " (" + _app.GetFileStorage(_db.Ioc).GetDisplayName(_db.Ioc) + ")";
|
message += " (" + _app.GetFileStorage(_db.Ioc).GetDisplayName(_db.Ioc) + ")";
|
||||||
|
|
||||||
StatusLogger.UpdateMessage(message);
|
if (!DoNotSetStatusLoggerMessage)
|
||||||
|
{
|
||||||
|
StatusLogger.UpdateMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
IOConnectionInfo ioc = _db.Ioc;
|
IOConnectionInfo ioc = _db.Ioc;
|
||||||
IFileStorage fileStorage = _app.GetFileStorage(ioc);
|
IFileStorage fileStorage = _app.GetFileStorage(ioc);
|
||||||
|
|
||||||
if (SyncInBackground && fileStorage is IOfflineSwitchable offlineSwitchable)
|
if (SyncInBackground && fileStorage is IOfflineSwitchable offlineSwitchable)
|
||||||
|
|||||||
@@ -1305,7 +1305,7 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
if (Success)
|
if (Success)
|
||||||
{
|
{
|
||||||
((GroupBaseActivity)ActiveContext)?.RefreshIfDirty();
|
(ActiveContext as GroupBaseActivity)?.RefreshIfDirty();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1325,7 +1325,7 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
if (Success)
|
if (Success)
|
||||||
{
|
{
|
||||||
((GroupBaseActivity)ActiveContext)?.RefreshIfDirty();
|
(ActiveContext as GroupBaseActivity)?.RefreshIfDirty();
|
||||||
}
|
}
|
||||||
else
|
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_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_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="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>
|
</resources>
|
||||||
@@ -17,6 +17,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.Design;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Security;
|
using System.Net.Security;
|
||||||
@@ -46,6 +47,7 @@ using keepass2android;
|
|||||||
using keepass2android.Utils;
|
using keepass2android.Utils;
|
||||||
using KeePassLib.Interfaces;
|
using KeePassLib.Interfaces;
|
||||||
using KeePassLib.Utility;
|
using KeePassLib.Utility;
|
||||||
|
using AlertDialog = AndroidX.AppCompat.App.AlertDialog;
|
||||||
using Message = keepass2android.Utils.Message;
|
using Message = keepass2android.Utils.Message;
|
||||||
#if !NoNet
|
#if !NoNet
|
||||||
#if !EXCLUDE_JAVAFILESTORAGE
|
#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
|
// Whether the app is currently showing a dialog that requires user input, like a yesNoCancel dialog
|
||||||
private bool _isShowingUserInputDialog = false;
|
private bool _isShowingUserInputDialog = false;
|
||||||
private IMessagePresenter? _messagePresenter;
|
private IMessagePresenter? _messagePresenter;
|
||||||
|
private YesNoCancelQuestion? _currentlyPendingYesNoCancelQuestion = null;
|
||||||
|
private Context _activeContext;
|
||||||
|
|
||||||
private void AskForReload(Activity activity, Action<bool> actionOnResult)
|
private void AskForReload(Activity activity, Action<bool> actionOnResult)
|
||||||
{
|
{
|
||||||
@@ -591,92 +595,142 @@ namespace keepass2android
|
|||||||
AskYesNoCancel(titleKey, messageKey, yesString, noString, yesHandler, noHandler, cancelHandler, null, messageSuffix);
|
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,
|
UiStringKey yesString, UiStringKey noString,
|
||||||
EventHandler<DialogClickEventArgs> yesHandler,
|
EventHandler<DialogClickEventArgs> yesHandler,
|
||||||
EventHandler<DialogClickEventArgs> noHandler,
|
EventHandler<DialogClickEventArgs> noHandler,
|
||||||
EventHandler<DialogClickEventArgs> cancelHandler,
|
EventHandler<DialogClickEventArgs> cancelHandler,
|
||||||
EventHandler dismissHandler,string messageSuffix = "")
|
EventHandler dismissHandler,string messageSuffix = "")
|
||||||
{
|
{
|
||||||
Handler handler = new Handler(Looper.MainLooper);
|
_currentlyPendingYesNoCancelQuestion = new YesNoCancelQuestion()
|
||||||
handler.Post(() =>
|
{
|
||||||
{
|
TitleKey = titleKey,
|
||||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(ActiveContext);
|
MessageKey = messageKey,
|
||||||
builder.SetTitle(GetResourceString(titleKey));
|
YesString = yesString,
|
||||||
|
NoString = noString,
|
||||||
|
YesHandler = yesHandler,
|
||||||
|
NoHandler = noHandler,
|
||||||
|
CancelHandler = cancelHandler,
|
||||||
|
DismissHandler = dismissHandler,
|
||||||
|
MessageSuffix = messageSuffix
|
||||||
|
};
|
||||||
|
|
||||||
builder.SetMessage(GetResourceString(messageKey) + (messageSuffix != "" ? " " + messageSuffix : ""));
|
_currentlyPendingYesNoCancelQuestion.TryShow(this, OnUserInputDialogClose, OnUserInputDialogShow);
|
||||||
|
|
||||||
// _____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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -718,7 +772,9 @@ namespace keepass2android
|
|||||||
private void OnUserInputDialogClose()
|
private void OnUserInputDialogClose()
|
||||||
{
|
{
|
||||||
_isShowingUserInputDialog = false;
|
_isShowingUserInputDialog = false;
|
||||||
ShowAllActiveProgressDialogs();
|
_currentlyPendingYesNoCancelQuestion = null;
|
||||||
|
|
||||||
|
ShowAllActiveProgressDialogs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Handler UiThreadHandler
|
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