diff --git a/src/Kp2aBusinessLogic/BlockingOperationRunner.cs b/src/Kp2aBusinessLogic/BlockingOperationRunner.cs
index fbe0d0d7..b35ff2b1 100644
--- a/src/Kp2aBusinessLogic/BlockingOperationRunner.cs
+++ b/src/Kp2aBusinessLogic/BlockingOperationRunner.cs
@@ -32,46 +32,59 @@ namespace keepass2android
//for handling Activity recreation situations, we need access to the currently active task. It must hold that there is no more than one active task.
private static BlockingOperationRunner _currentTask = null;
- public static void SetNewActiveActivity(Activity activeActivity)
+ public static void SetNewActiveContext(Context activeContext)
{
if (_currentTask != null)
{
- _currentTask.ActiveActivity = activeActivity;
+ _currentTask.ActiveContext = activeContext;
}
}
- public static void RemoveActiveActivity(Activity activity)
+ public static void RemoveActiveContext(Context context)
{
- if ((_currentTask != null) && (_currentTask._activeActivity == activity))
- _currentTask.ActiveActivity = null;
+ if ((_currentTask != null) && (_currentTask._activeContext == context))
+ _currentTask.ActiveContext = null;
}
- public Context ActiveActivity
+ public Context ActiveContext
{
- get { return _activeActivity; }
+ get { return _activeContext; }
private set
{
- _activeActivity = value;
+ _activeContext = value;
- if (_activeActivity != null)
+ if (_activeContext is Activity)
{
SetupProgressDialog(_app);
_progressDialog.Show();
}
- }
+ else if (_activeContext is Service and IProgressUiProvider progressUiProvider)
+ {
+ _statusLogger?.SetNewProgressUi(progressUiProvider.ProgressUi);
+ }
+
+ }
}
+ public static bool HasActiveTask
+ {
+ get
+ {
+ return _currentTask != null;
+ }
+ }
+
private readonly Handler _handler;
private readonly OperationWithFinishHandler _task;
private IProgressDialog _progressDialog;
private readonly IKp2aApp _app;
private Java.Lang.Thread _thread;
- private Context _activeActivity;
- private ProgressDialogStatusLogger _progressDialogStatusLogger;
+ private Context _activeContext;
+ private ProgressUiAsStatusLoggerAdapter _statusLogger;
public BlockingOperationRunner(IKp2aApp app, OperationWithFinishHandler task)
{
- _activeActivity = app.ActiveContext;
+ _activeContext = app.ActiveContext;
_task = task;
_handler = app.UiThreadHandler;
_app = app;
@@ -84,7 +97,7 @@ namespace keepass2android
// Set code to run when this is finished
_task.operationFinishedHandler = new AfterTask(app, task.operationFinishedHandler, _handler, this);
- _task.SetStatusLogger(_progressDialogStatusLogger);
+ _task.SetStatusLogger(_statusLogger);
}
@@ -94,12 +107,12 @@ namespace keepass2android
string currentMessage = "Initializing...";
string currentSubmessage = "";
- if (_progressDialogStatusLogger != null)
+ if (_statusLogger != null)
{
- currentMessage = _progressDialogStatusLogger.Message;
- currentSubmessage = _progressDialogStatusLogger.SubMessage;
+ currentMessage = _statusLogger.LastMessage;
+ currentSubmessage = _statusLogger.LastSubMessage;
}
-
+
if (_progressDialog != null)
{
var pd = _progressDialog;
@@ -110,11 +123,21 @@ namespace keepass2android
}
// Show process dialog
- _progressDialog = app.CreateProgressDialog(_activeActivity);
- _progressDialog.SetTitle(_app.GetResourceString(UiStringKey.progress_title));
- _progressDialogStatusLogger = new ProgressDialogStatusLogger(_app, _handler, _progressDialog);
- _progressDialogStatusLogger.UpdateMessage(currentMessage);
- _progressDialogStatusLogger.UpdateSubMessage(currentSubmessage);
+ _progressDialog = app.CreateProgressDialog(_activeContext);
+
+ var progressUi = new ProgressDialogUi(_app, app.UiThreadHandler, _progressDialog);
+ if (_statusLogger == null)
+ {
+ _statusLogger = new ProgressUiAsStatusLoggerAdapter(progressUi, app);
+ }
+ else
+ {
+ _statusLogger.SetNewProgressUi(progressUi);
+ }
+
+ _statusLogger.StartLogging("", false);
+ _statusLogger.UpdateMessage(currentMessage);
+ _statusLogger.UpdateSubMessage(currentSubmessage);
}
public void Run(bool allowOverwriteCurrentTask = false)
diff --git a/src/Kp2aBusinessLogic/IKp2aApp.cs b/src/Kp2aBusinessLogic/IKp2aApp.cs
index 984ab2de..2226f6e7 100644
--- a/src/Kp2aBusinessLogic/IKp2aApp.cs
+++ b/src/Kp2aBusinessLogic/IKp2aApp.cs
@@ -143,5 +143,10 @@ namespace keepass2android
ReaderWriterLockSlim DatabasesBackgroundModificationLock { get; }
bool CancelBackgroundOperations();
+
+ ///
+ /// Registers an action that should be executed when the context instance with the given id has been resumed.
+ ///
+ void RegisterPendingActionForContextInstance(int contextInstanceId, ActionOnOperationFinished actionToPerformWhenContextIsResumed);
}
}
\ No newline at end of file
diff --git a/src/Kp2aBusinessLogic/ProgressDialogStatusLogger.cs b/src/Kp2aBusinessLogic/ProgressDialogStatusLogger.cs
index 280a1795..5279a758 100644
--- a/src/Kp2aBusinessLogic/ProgressDialogStatusLogger.cs
+++ b/src/Kp2aBusinessLogic/ProgressDialogStatusLogger.cs
@@ -25,6 +25,8 @@ namespace keepass2android
public interface IKp2aStatusLogger : IStatusLogger
{
void UpdateMessage(UiStringKey stringKey);
+ string LastMessage { get; }
+ string LastSubMessage { get; }
}
public interface IProgressUi
@@ -62,14 +64,16 @@ namespace keepass2android
return true;
}
+ private string _lastMessage;
+ private string _lastSubMessage;
public void UpdateMessage(string message)
{
-
+ _lastMessage = message;
}
public void UpdateSubMessage(string submessage)
{
-
+ _lastSubMessage = submessage;
}
public bool ContinueWork()
@@ -81,133 +85,88 @@ namespace keepass2android
{
}
- }
-
- public abstract class Kp2aAppStatusLogger : IKp2aStatusLogger
- {
- protected IKp2aApp _app;
-
- public Kp2aAppStatusLogger(IKp2aApp app)
- {
- _app = app;
- }
-
- #region IStatusLogger implementation
-
- public void StartLogging(string strOperation, bool bWriteOperationToLog)
- {
-
- }
-
- public void EndLogging()
- {
-
- }
-
- public bool SetProgress(uint uPercent)
- {
- return true;
- }
-
- public bool SetText(string strNewText, LogStatusType lsType)
- {
- if (strNewText.StartsWith("KP2AKEY_"))
- {
- UiStringKey key;
- if (Enum.TryParse(strNewText.Substring("KP2AKEY_".Length), true, out key))
- {
- UpdateMessage(_app.GetResourceString(key), lsType);
- return true;
- }
- }
- UpdateMessage(strNewText, lsType);
-
- return true;
- }
-
- public abstract void UpdateMessage(string message);
- public abstract void UpdateSubMessage(string submessage);
-
- private void UpdateMessage(string message, LogStatusType lsType)
- {
- if (lsType == LogStatusType.AdditionalInfo)
- {
- UpdateSubMessage(message);
- }
- else
- {
- UpdateMessage(message);
- }
- }
-
- public bool ContinueWork()
- {
- return true;
- }
-
- #endregion
-
- public void UpdateMessage(UiStringKey stringKey)
- {
- if (_app != null)
- UpdateMessage(_app.GetResourceString(stringKey));
- }
+ public string LastMessage { get { return _lastMessage; } }
+ public string LastSubMessage { get { return _lastSubMessage; } }
}
///
/// StatusLogger implementation which shows the progress in a progress dialog
///
- public class ProgressDialogStatusLogger: Kp2aAppStatusLogger
+ public class ProgressDialogUi: IProgressUi
{
private readonly IProgressDialog _progressDialog;
private readonly Handler _handler;
private string _message = "";
private string _submessage;
+ private readonly IKp2aApp _app;
- public String SubMessage => _submessage;
- public String Message => _message;
+ public String LastSubMessage => _submessage;
+ public String LastMessage => _message;
- public ProgressDialogStatusLogger(IKp2aApp app, Handler handler, IProgressDialog pd)
- : base(app){
+ public ProgressDialogUi(IKp2aApp app, Handler handler, IProgressDialog pd)
+ {
+ _app = app;
_progressDialog = pd;
_handler = handler;
}
-
-
- public override void UpdateMessage (String message)
- {
- Kp2aLog.Log("status message: " + message);
+
+ public void UpdateSubMessage(String submessage)
+ {
+ Kp2aLog.Log("status submessage: " + submessage);
+ _submessage = submessage;
+ if (_app != null && _progressDialog != null && _handler != null)
+ {
+ _handler.Post(() =>
+ {
+ if (!String.IsNullOrEmpty(submessage))
+ {
+ _progressDialog.SetMessage(_message + " (" + submessage + ")");
+ }
+ else
+ {
+ _progressDialog.SetMessage(_message);
+ }
+ }
+ );
+ }
+ }
+
+ public void Show()
+ {
+ _handler.Post(() =>
+ {
+
+ _progressDialog?.Show();
+
+ });
+ }
+
+ public void Hide()
+ {
+ _handler.Post(() =>
+ {
+
+ _progressDialog?.Dismiss();
+
+ });
+ }
+
+ public void UpdateMessage(string message)
+ {
+ Kp2aLog.Log("status message: " + message);
_message = message;
- if ( _app!= null && _progressDialog != null && _handler != null ) {
- _handler.Post(() => {_progressDialog.SetMessage(message); } );
- }
- }
+ if (_app != null && _progressDialog != null && _handler != null)
+ {
+ _handler.Post(() => { _progressDialog.SetMessage(message); });
+ }
+ }
- public override void UpdateSubMessage(String submessage)
- {
- Kp2aLog.Log("status submessage: " + submessage);
- _submessage = submessage;
- if (_app != null && _progressDialog != null && _handler != null)
- {
- _handler.Post(() =>
- {
- if (!String.IsNullOrEmpty(submessage))
- {
- _progressDialog.SetMessage(_message + " (" + submessage + ")");
- }
- else
- {
- _progressDialog.SetMessage(_message);
- }
- }
- );
- }
- }
+ }
- }
+
}
diff --git a/src/Kp2aBusinessLogic/ProgressUiAsStatusLoggerAdapter.cs b/src/Kp2aBusinessLogic/ProgressUiAsStatusLoggerAdapter.cs
index de444374..0b58f6e2 100644
--- a/src/Kp2aBusinessLogic/ProgressUiAsStatusLoggerAdapter.cs
+++ b/src/Kp2aBusinessLogic/ProgressUiAsStatusLoggerAdapter.cs
@@ -87,4 +87,7 @@ public class ProgressUiAsStatusLoggerAdapter : IKp2aStatusLogger
if (_app != null)
UpdateMessage(_app.GetResourceString(stringKey));
}
+
+ public string LastMessage { get { return _lastMessage; } }
+ public string LastSubMessage { get { return _lastSubMessage; } }
}
\ No newline at end of file
diff --git a/src/Kp2aBusinessLogic/database/edit/ActionOnOperationFinished.cs b/src/Kp2aBusinessLogic/database/edit/ActionOnOperationFinished.cs
index c17c36de..fd407d35 100644
--- a/src/Kp2aBusinessLogic/database/edit/ActionOnOperationFinished.cs
+++ b/src/Kp2aBusinessLogic/database/edit/ActionOnOperationFinished.cs
@@ -19,6 +19,7 @@ using System;
using Android.App;
using Android.Content;
using Android.OS;
+using keepass2android;
namespace keepass2android
{
@@ -53,3 +54,30 @@ namespace keepass2android
}
}
+ public class ActionInContextInstanceOnOperationFinished : ActionOnOperationFinished
+ {
+ private readonly int _contextInstanceId;
+ private IKp2aApp _app;
+
+ public ActionInContextInstanceOnOperationFinished(int contextInstanceId, IKp2aApp app, ActionToPerformOnFinsh actionToPerform) : base(app, actionToPerform)
+ {
+ _contextInstanceId = contextInstanceId;
+ _app = app;
+ }
+ public ActionInContextInstanceOnOperationFinished(int contextInstanceId, IKp2aApp app, ActionToPerformOnFinsh actionToPerform, OnOperationFinishedHandler operationFinishedHandler) : base(app, actionToPerform, operationFinishedHandler)
+ {
+ _contextInstanceId = contextInstanceId;
+ _app = app;
+ }
+
+ public override void Run()
+ {
+ if ((ActiveContext as IContextInstanceIdProvider)?.ContextInstanceId != _contextInstanceId)
+ {
+ _app.RegisterPendingActionForContextInstance(_contextInstanceId, this);
+ }
+ else base.Run();
+ }
+
+ }
+
diff --git a/src/Kp2aBusinessLogic/database/edit/IContextInstanceIdProvider.cs b/src/Kp2aBusinessLogic/database/edit/IContextInstanceIdProvider.cs
new file mode 100644
index 00000000..ac1d8631
--- /dev/null
+++ b/src/Kp2aBusinessLogic/database/edit/IContextInstanceIdProvider.cs
@@ -0,0 +1,13 @@
+namespace keepass2android;
+
+// A context instance can be the instance of an Activity. Even if the activity is recreated (due to a configuration change, for example), the instance id must remain the same
+// but it must be different for other activities/services or if the activity is finished and then starts again.
+// We want to be able to perform actions on a context instance, even though that instance might not live at the time when we want to perform the action.
+// In that case, we want to be able to register the action such that it is performed when the activity is recreated.
+public interface IContextInstanceIdProvider
+{
+
+ int ContextInstanceId { get; }
+
+
+}
\ No newline at end of file
diff --git a/src/keepass2android-app/EntryEditActivity.cs b/src/keepass2android-app/EntryEditActivity.cs
index b2d6b769..e293e0bd 100644
--- a/src/keepass2android-app/EntryEditActivity.cs
+++ b/src/keepass2android-app/EntryEditActivity.cs
@@ -551,9 +551,9 @@ namespace keepass2android
//make sure we can close the EntryEditActivity activity even if the app went to background till we get to the OnOperationFinishedHandler Action
- ActionOnOperationFinished afterAddEntry = new ActionOnOperationFinished(App.Kp2a, (success, message, activity) =>
+ ActionOnOperationFinished afterAddEntry = new ActionInContextInstanceOnOperationFinished(ContextInstanceId, App.Kp2a, (success, message, context) =>
{
- if (success && activity is EntryEditActivity entryEditActivity)
+ if (success && context is EntryEditActivity entryEditActivity)
AppTask.AfterAddNewEntry(entryEditActivity, newEntry);
},closeOrShowError);
diff --git a/src/keepass2android-app/LifecycleAwareActivity.cs b/src/keepass2android-app/LifecycleAwareActivity.cs
index 68b75b57..97931c9c 100644
--- a/src/keepass2android-app/LifecycleAwareActivity.cs
+++ b/src/keepass2android-app/LifecycleAwareActivity.cs
@@ -25,7 +25,7 @@ using Android.Runtime;
namespace keepass2android
{
- public abstract class LifecycleAwareActivity : AndroidX.AppCompat.App.AppCompatActivity
+ public abstract class LifecycleAwareActivity : AndroidX.AppCompat.App.AppCompatActivity, IContextInstanceIdProvider
{
protected override void AttachBaseContext(Context baseContext)
{
@@ -84,12 +84,11 @@ namespace keepass2android
return baseRes;
}
- public Action? OnResumeListener { get; set; }
protected override void OnResume()
{
base.OnResume();
- OnResumeListener?.Invoke();
+ App.Kp2a.PerformPendingActions(_instanceId);
Kp2aLog.Log(ClassName + ".OnResume " + ID);
if (App.Kp2a.CurrentDb == null)
@@ -105,28 +104,40 @@ namespace keepass2android
protected override void OnStart()
{
App.Kp2a.ActiveContext = this;
- BlockingOperationRunner.SetNewActiveActivity(this);
+ BlockingOperationRunner.SetNewActiveContext(this);
BackgroundOperationRunner.Instance.SetNewActiveContext( App.Kp2a);
base.OnStart();
Kp2aLog.Log(ClassName + ".OnStart" + " " + ID);
}
+ const string ID_KEY = "kp2a_context_instance_id";
+ const int InvalidId = -1;
+
+ private int _instanceId;
+
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
+
+ _instanceId = bundle?.GetInt(ID_KEY, InvalidId) ?? InvalidId;
+ if (_instanceId == InvalidId)
+ {
+ _instanceId = _nextContextInstanceId++;
+ }
OnCreateListener?.Invoke(bundle);
- Kp2aLog.Log(ClassName + ".OnCreate" + " " + ID);
+ Kp2aLog.Log(ClassName + ".OnCreate" + " " + ID + " (instance=" + _instanceId +")");
Kp2aLog.Log(ClassName + ":apptask=" + Intent.GetStringExtra("KP2A_APP_TASK_TYPE") + " " + ID);
}
+
protected override void OnDestroy()
{
base.OnDestroy();
- Kp2aLog.Log(ClassName + ".OnDestroy" + IsFinishing.ToString() + " " + ID);
+ Kp2aLog.Log(ClassName + ".OnDestroy " + IsFinishing.ToString() + " " + ID);
}
protected override void OnPause()
@@ -139,14 +150,19 @@ namespace keepass2android
{
base.OnStop();
Kp2aLog.Log(ClassName + ".OnStop" + " " + ID);
- BlockingOperationRunner.RemoveActiveActivity(this);
+ BlockingOperationRunner.RemoveActiveContext(this);
}
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
+ outState.PutInt(ID_KEY, _instanceId);
OnSaveInstanceStateListener?.Invoke(outState);
}
+
+ static int _nextContextInstanceId = 0;
+
+ public int ContextInstanceId => _instanceId;
}
}
diff --git a/src/keepass2android-app/app/App.cs b/src/keepass2android-app/app/App.cs
index 07bc17bd..ebfb4bbf 100644
--- a/src/keepass2android-app/app/App.cs
+++ b/src/keepass2android-app/app/App.cs
@@ -522,7 +522,7 @@ namespace keepass2android
private bool _isShowingUserInputDialog = false;
private IMessagePresenter? _messagePresenter;
private YesNoCancelQuestion? _currentlyPendingYesNoCancelQuestion = null;
- private Context _activeContext;
+ private Context? _activeContext;
private void AskForReload(Activity activity, Action actionOnResult)
{
@@ -882,8 +882,11 @@ namespace keepass2android
public IProgressDialog CreateProgressDialog(Context ctx)
{
- return new RealProgressDialog(ctx, this);
- }
+ var pd = new RealProgressDialog(ctx, this);
+ pd.SetTitle(GetResourceString(UiStringKey.progress_title));
+ return pd;
+
+ }
public IFileStorage GetFileStorage(IOConnectionInfo iocInfo)
{
@@ -1480,7 +1483,7 @@ namespace keepass2android
public Context ActiveContext
{
- get => _activeContext;
+ get => _activeContext ?? Application.Context;
set
{
_activeContext = value;
@@ -1513,6 +1516,47 @@ namespace keepass2android
return true;
+ }
+
+ private readonly Dictionary> _pendingActionsForContextInstances = new();
+ private readonly object _pendingActionsForContextInstancesLock = new();
+ public void RegisterPendingActionForContextInstance(int contextInstanceId,
+ ActionOnOperationFinished actionToPerformWhenContextIsResumed)
+ {
+ lock (_pendingActionsForContextInstancesLock)
+ {
+
+ if (!_pendingActionsForContextInstances.TryGetValue(contextInstanceId, out var actions))
+ {
+ actions = new List();
+ _pendingActionsForContextInstances[contextInstanceId] = actions;
+ }
+
+ actions.Add(actionToPerformWhenContextIsResumed);
+ }
+ }
+
+ public void PerformPendingActions(int instanceId)
+ {
+ lock (_pendingActionsForContextInstancesLock)
+ {
+ if (_pendingActionsForContextInstances.TryGetValue(instanceId, out var actions))
+ {
+ foreach (var action in actions)
+ {
+ try
+ {
+ action.Run();
+ }
+ catch (Exception e)
+ {
+ Kp2aLog.LogUnexpectedError(e);
+ }
+ }
+
+ _pendingActionsForContextInstances.Remove(instanceId);
+ }
+ }
}
}