fix issues with background operations implementation, added more logging

This commit is contained in:
Philipp Crocoll
2025-06-03 14:44:01 +02:00
parent 6351e1f3d0
commit a4a3112dc6
14 changed files with 326 additions and 214 deletions

View File

@@ -21,6 +21,7 @@ using System.IO;
using Android;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Preferences;
using KeePassLib.Serialization;
@@ -34,9 +35,17 @@ namespace keepass2android
public static void Log(string message)
{
if (message != null)
Android.Util.Log.Debug("KP2A", message);
if (LogToFile)
if (message != null)
{
message += Thread.CurrentThread.ManagedThreadId != 0
? " (Thread ID: " + Thread.CurrentThread.ManagedThreadId + ")"
: "";
if (Looper.MainLooper == Looper.MyLooper())
message += " (Main Looper)";
Android.Util.Log.Debug("KP2A", message);
}
if (LogToFile)
{
lock (_fileLocker)
{

View File

@@ -208,7 +208,7 @@ namespace KeePassLib.Serialization
if (!string.IsNullOrEmpty(strHash) && (m_pbHashOfHeader != null) &&
!m_bRepairMode)
{
Debug.Assert(m_uFileVersion < FileVersion32_4);
// Debug.Assert(m_uFileVersion < FileVersion32_4);
byte[] pbHash = Convert.FromBase64String(strHash);
if (!MemUtil.ArraysEqual(pbHash, m_pbHashOfHeader))

View File

@@ -488,7 +488,7 @@ namespace KeePassLib.Serialization
ProtectedBinary pb = new ProtectedBinary(bProt, pbData,
1, pbData.Length - 1);
Debug.Assert(m_pbsBinaries.Find(pb) < 0); // No deduplication?
//Debug.Assert(m_pbsBinaries.Find(pb) < 0); // No deduplication?
m_pbsBinaries.Add(pb);
if (bProt) MemUtil.ZeroByteArray(pbData);

View File

@@ -1,6 +1,7 @@
using Android.App;
using Android.Content;
using Android.OS;
using System.Threading.Tasks;
using Thread = Java.Lang.Thread;
namespace keepass2android;
@@ -59,10 +60,11 @@ public class OperationRunner
public void Run(IKp2aApp app, OperationWithFinishHandler operation, bool runBlocking = false)
{
Kp2aLog.Log("OPR: Run: " + operation.GetType().Name + ", runBlocking: " + runBlocking);
lock (Instance._taskQueueLock)
{
_taskQueue.Enqueue(new OperationWithMetadata(){ Operation = operation, RunBlocking = runBlocking});
SetNewActiveContext(app);
operation.SetStatusLogger(_statusLogger);
// Start thread to run the task (unless it's already running)
if (_thread == null)
@@ -79,6 +81,7 @@ public class OperationRunner
{
_thread = null;
_statusLogger.EndLogging();
Kp2aLog.Log("OPR: task queue empty. Stopping operation runner thread.");
break;
}
else
@@ -89,21 +92,38 @@ public class OperationRunner
if (_currentlyRunningTask.Value.RunBlocking)
{
app.UiThreadHandler.Post(() => TrySetupProgressDialog());
Kp2aLog.Log("OPR: Run. Posting to set up progress dialog for blocking task: " + _currentlyRunningTask?.Operation?.GetType()?.Name ?? "null");
app.UiThreadHandler.Post(
() =>
{
Kp2aLog.Log("OPR: Run. Starting Setting up progress dialog for blocking task: " + _currentlyRunningTask?.Operation?.GetType()?.Name ?? "null");
TrySetupProgressDialog();
Kp2aLog.Log("OPR: Run. Finished Setting up progress dialog for blocking task: " + _currentlyRunningTask?.Operation?.GetType()?.Name ?? "null");
});
}
var originalFinishedHandler = _currentlyRunningTask.Value.Operation.operationFinishedHandler;
_currentlyRunningTask.Value.Operation.operationFinishedHandler = new ActionOnOperationFinished(app, (
(success, message, context) =>
{
if (_currentlyRunningTask.Value.RunBlocking)
if (_currentlyRunningTask?.RunBlocking == true)
{
_progressDialog?.Dismiss();
Kp2aLog.Log("OPR: Run. Blocking task finished: " + _currentlyRunningTask?.Operation?.GetType()?.Name ?? "null");
_app.UiThreadHandler.Post(() =>
{
Kp2aLog.Log("OPR: Starting Dismissing progress dialog");
_progressDialog?.Dismiss();
Kp2aLog.Log("OPR: Finished Dismissing progress dialog");
}
);
}
Kp2aLog.Log("OPR: Run. Finished handler called for task: " + _currentlyRunningTask?.Operation?.GetType()?.Name ?? "null");
_currentlyRunningTask = null;
}), originalFinishedHandler);
Kp2aLog.Log("OPR: starting to run " + _currentlyRunningTask?.Operation?.GetType()?.Name ?? "null");
_currentlyRunningTask.Value.Operation.Run();
while (_currentlyRunningTask != null)
{
try
@@ -115,12 +135,15 @@ public class OperationRunner
Kp2aLog.Log("Thread interrupted.");
}
}
Kp2aLog.Log("OPR: waiting for next task in queue...");
}
});
_thread.Start();
}
else Kp2aLog.Log("OPR: thread already running, only enqueued " + operation.GetType().Name );
}
}
@@ -128,6 +151,7 @@ public class OperationRunner
private bool TrySetupProgressDialog()
{
Kp2aLog.Log("OPR: TrySetupProgressDialog");
string currentMessage = "Initializing...";
string currentSubmessage = "";
@@ -142,7 +166,9 @@ public class OperationRunner
var pd = _progressDialog;
_app.UiThreadHandler.Post(() =>
{
Kp2aLog.Log("OPR: Starting TrySetupProgressDialog: Dismissing existing progress dialog");
pd.Dismiss();
Kp2aLog.Log("OPR: Finished TrySetupProgressDialog: Dismissing existing progress dialog");
});
}
@@ -150,7 +176,7 @@ public class OperationRunner
_progressDialog = _app.CreateProgressDialog(_app.ActiveContext);
if (_progressDialog == null)
{
Kp2aLog.Log("OperationRunner.TrySetupProgressDialog: _progressDialog is null");
Kp2aLog.Log("OPR: OperationRunner.TrySetupProgressDialog: _progressDialog is null");
return false;
}
@@ -164,9 +190,9 @@ public class OperationRunner
return true;
}
public void SetNewActiveContext(IKp2aApp app)
{
Kp2aLog.Log("OPR: SetNewActiveContext: " + app.ActiveContext?.GetType().Name);
_app = app;
Context? context = app.ActiveContext;
bool isAppContext = context == null || (context.ApplicationContext == context);
@@ -179,9 +205,15 @@ public class OperationRunner
return;
}
if (_currentlyRunningTask?.RunBlocking == true)
if (_currentlyRunningTask?.RunBlocking == true && (context is Activity { IsFinishing: false, IsDestroyed:false}))
{
app.UiThreadHandler.Post(() => TrySetupProgressDialog());
Kp2aLog.Log("OPR: SetNewActiveContext: running blocking task, setting up progress dialog");
app.UiThreadHandler.Post(() =>
{
Kp2aLog.Log("OPR: Starting posted TrySetupProgressDialog");
TrySetupProgressDialog();
Kp2aLog.Log("OPR: Finished posted TrySetupProgressDialog");
});
}
else
{

View File

@@ -121,6 +121,7 @@ namespace keepass2android
{
_handler.Post(() =>
{
Kp2aLog.Log("OPR: Starting posted SetMessage");
if (!String.IsNullOrEmpty(submessage))
{
_progressDialog.SetMessage(_message + " (" + submessage + ")");
@@ -129,6 +130,7 @@ namespace keepass2android
{
_progressDialog.SetMessage(_message);
}
Kp2aLog.Log("OPR: Finished posted SetMessage");
}
);
}
@@ -138,9 +140,10 @@ namespace keepass2android
{
_handler.Post(() =>
{
Kp2aLog.Log("OPR: Starting posted Show");
_progressDialog?.Show();
Kp2aLog.Log("OPR: Finished posted Show");
});
}
@@ -148,9 +151,10 @@ namespace keepass2android
{
_handler.Post(() =>
{
Kp2aLog.Log("OPR: Starting posted Dismiss");
_progressDialog?.Dismiss();
Kp2aLog.Log("OPR: Finished posted Dismiss");
});
}
@@ -160,7 +164,12 @@ namespace keepass2android
_message = message;
if (_app != null && _progressDialog != null && _handler != null)
{
_handler.Post(() => { _progressDialog.SetMessage(message); });
_handler.Post(() =>
{
Kp2aLog.Log("OPR: Starting posted SetMessage");
_progressDialog.SetMessage(message);
Kp2aLog.Log("OPR: Finishing posted SetMessage");
});
}
}

View File

@@ -66,12 +66,12 @@ namespace keepass2android
var baseVersionHash = cachingFileStorage.GetBaseVersionHash(ioc);
Kp2aLog.Log("Checking for file change. baseVersionHash = " + baseVersionHash);
if (baseVersionHash != hash ||
false //TODO remove
true //TODO remove
)
{
//remote file is modified
if (cachingFileStorage.HasLocalChanges(ioc)
|| false //TODO remove
|| true //TODO remove
)
{
//conflict! need to merge

View File

@@ -45,10 +45,17 @@ namespace keepass2android
Message = "";
if (Handler != null)
{
Handler.Post(() => {_actionToPerform(Success, Message, ActiveContext);});
Handler.Post(() =>
{
Kp2aLog.Log("OPR: Starting posted actionToPerform ");
_actionToPerform(Success, Message, ActiveContext);
Kp2aLog.Log("OPR: Finished posted actionToPerform ");
});
}
else
_actionToPerform(Success, Message, ActiveContext);
else
{
_actionToPerform(Success, Message, ActiveContext);
}
base.Run();
}
}
@@ -80,7 +87,7 @@ namespace keepass2android
{
_app.RegisterPendingActionForContextInstance(_contextInstanceId, this);
}
else base.Run();
else _app.UiThreadHandler.Post(() => base.Run());
}
}

View File

@@ -97,9 +97,16 @@ namespace keepass2android
if (NextOnOperationFinishedHandler == null) return;
// Pass on result on call finish
NextOnOperationFinishedHandler.SetResult(Success, Message, ImportantMessage, Exception);
if ( Handler != null ) {
Handler.Post(NextOnOperationFinishedHandler.Run);
var handler = Handler ?? NextOnOperationFinishedHandler.Handler ?? null;
if (handler != null ) {
handler.Post(() =>
{
Kp2aLog.Log("OPR: Starting posted NextOnOperationFinishedHandler");
NextOnOperationFinishedHandler.Run();
Kp2aLog.Log("OPR: Finished posted NextOnOperationFinishedHandler");
});
} else {
NextOnOperationFinishedHandler.Run();
}

View File

@@ -104,8 +104,6 @@ namespace keepass2android
protected override void OnStart()
{
App.Kp2a.ActiveContext = this;
OperationRunner.Instance.SetNewActiveContext( App.Kp2a);
base.OnStart();
Kp2aLog.Log(ClassName + ".OnStart" + " " + ID);
}

View File

@@ -56,7 +56,7 @@ namespace keepass2android
OperationWithFinishHandler task;
OnOperationFinishedHandler onOperationFinishedHandler = new ActionInContextInstanceOnOperationFinished(_activity.ContextInstanceId, App.Kp2a, (success, message, context) =>
{
new Handler(Looper.MainLooper).Post(() =>
App.Kp2a.UiThreadHandler.Post(() =>
{
if (!String.IsNullOrEmpty(message))
App.Kp2a.ShowMessage(context, message, success ? MessageSeverity.Info : MessageSeverity.Error);

View File

@@ -651,97 +651,108 @@ namespace keepass2android
{
if (app.ActiveContext is Activity activity)
{
Handler handler = new Handler(Looper.MainLooper);
handler.Post(() =>
Kp2aLog.Log("OPR: Will show YesNoCancel dialog because active context is an Activity.");
if (_dialog is { IsShowing: true })
{
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();
_dialog.Dismiss();
}
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
}
_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);
}));
}
else
{
_dialog.SetCancelable(false);
}
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);
}
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
{
Kp2aLog.Log("OPR: Cannot show YesNoCancel dialog because active context is not an Activity.");
OperationRunner.Instance.StatusLogger?.UpdateSubMessage(App.Context.GetString(Resource.String.user_interaction_required));
return false;
}
@@ -759,6 +770,7 @@ namespace keepass2android
{
throw new Java.Lang.InterruptedException();
}
Kp2aLog.Log("OPR: AskYesNoCancel called with titleKey " + titleKey + ", messageKey " + messageKey);
_currentlyPendingYesNoCancelQuestion = new YesNoCancelQuestion()
{
@@ -772,8 +784,13 @@ namespace keepass2android
DismissHandler = dismissHandler,
MessageSuffix = messageSuffix
};
_currentlyPendingYesNoCancelQuestion.TryShow(this, OnUserInputDialogClose, OnUserInputDialogShow);
UiThreadHandler.Post( () =>
{
Kp2aLog.Log("OPR: Starting posted AskYesNoCancel/TryShow ");
_currentlyPendingYesNoCancelQuestion.TryShow(this, OnUserInputDialogClose, OnUserInputDialogShow);
Kp2aLog.Log("OPR: Finished posted AskYesNoCancel/TryShow ");
});
}
@@ -785,18 +802,22 @@ namespace keepass2android
/// because they are just progress indicators.
/// </summary>
private void ShowAllActiveProgressDialogs()
{
{
Kp2aLog.Log("OPR: ShowAllActiveProgressDialogs");
foreach (RealProgressDialog progressDialog in _activeProgressDialogs)
{
progressDialog.Show();
Kp2aLog.Log("OPR: ShowAllActiveProgressDialogs: Showing " + progressDialog.GetType().Name);
progressDialog.Show();
}
}
private void HideAllActiveProgressDialogs()
{
foreach (RealProgressDialog progressDialog in _activeProgressDialogs)
Kp2aLog.Log("OPR: HideAllActiveProgressDialogs");
foreach (RealProgressDialog progressDialog in _activeProgressDialogs)
{
progressDialog.Hide();
Kp2aLog.Log("OPR: HideAllActiveProgressDialogs: Hiding " + progressDialog.GetType().Name);
progressDialog.Hide();
}
}
@@ -816,6 +837,7 @@ namespace keepass2android
private void OnUserInputDialogClose()
{
_isShowingUserInputDialog = false;
Kp2aLog.Log("OPR: OnUserInputDialogClose called, _currentlyPendingYesNoCancelQuestion is " + (_currentlyPendingYesNoCancelQuestion != null ? "not null" : "null") + " Will set to null now.");
_currentlyPendingYesNoCancelQuestion = null;
ShowAllActiveProgressDialogs();
@@ -869,8 +891,17 @@ namespace keepass2android
_app._activeProgressDialogs.Add(this);
// Only show if asking dialog not also showing
if (!_app._isShowingUserInputDialog)
{
_pd.Show();
{
Kp2aLog.Log("OPR: Showing progress dialog " + _pd.GetType().Name);
try
{
_pd.Show();
}
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
}
}
}
@@ -882,6 +913,7 @@ namespace keepass2android
public IProgressDialog CreateProgressDialog(Context ctx)
{
Kp2aLog.Log("OPR: CreateProgressDialog called with ctx " + ctx?.GetType().Name);
/*Kp2aLog.Log("ctx is null ? " + (ctx == null));
Kp2aLog.Log("ctx is activity ? " + (ctx is Activity));
Kp2aLog.Log("ctx is destroyed ? " + (ctx is Activity { IsDestroyed: true }));
@@ -897,12 +929,13 @@ namespace keepass2android
}
catch (Exception e)
{
//may happen if the activity is (being) destroyed
//may happen if the activity is (being) destroyed
Kp2aLog.Log("OPR: CreateProgressDialog failed with " + e.ToString());
return null;
}
}
}
public IFileStorage GetFileStorage(IOConnectionInfo iocInfo)
{
@@ -1306,7 +1339,9 @@ namespace keepass2android
public void StartBackgroundSyncService()
{
Intent intent = new Intent(App.Context, typeof(BackgroundSyncService));
Kp2aLog.Log("OPR: StartBackgroundSyncService");
Intent intent = new Intent(App.Context, typeof(BackgroundSyncService));
intent.SetAction(BackgroundSyncService.ActionStart);
App.Context.StartService(intent);
}
@@ -1504,8 +1539,10 @@ namespace keepass2android
get => _activeContext ?? Application.Context;
set
{
_activeContext = value;
_currentlyPendingYesNoCancelQuestion?.TryShow(this, OnUserInputDialogClose, OnUserInputDialogClose);
_activeContext = value;
OperationRunner.Instance.SetNewActiveContext(App.Kp2a);
Kp2aLog.Log("OPR: ActiveContext set to " + _activeContext?.GetType().Name + ". Pending question? " +(_currentlyPendingYesNoCancelQuestion != null));
_currentlyPendingYesNoCancelQuestion?.TryShow(this, OnUserInputDialogClose, OnUserInputDialogShow);
}
}
@@ -1650,9 +1687,6 @@ namespace keepass2android
{
Kp2aLog.Log("Going to background");
Kp2a.ActiveContext = null;
// notify background operation runner that there is currently no active context, i.e. no UI where status can be displayed.
// The OperationRunner will launch the background sync service if a task (even a blocking one) is running currently.
OperationRunner.Instance.SetNewActiveContext(Kp2a);
}

View File

@@ -52,7 +52,6 @@ namespace keepass2android.services
CreateNotificationChannel();
StartForeground(NotificationId, BuildNotification());
App.Kp2a.ActiveContext = this;
OperationRunner.Instance.SetNewActiveContext(App.Kp2a);
return StartCommandResult.Sticky;
}
catch (Exception ex)

View File

@@ -42,29 +42,34 @@ public class BackgroundOperationContainer : LinearLayout, IProgressUi
public void Show()
{
new Handler(Looper.MainLooper).Post(() =>
App.Kp2a.UiThreadHandler.Post(() =>
{
Kp2aLog.Log("OPR: Starting posted Show ");
Visibility = ViewStates.Visible;
FindViewById<TextView>(Resource.Id.background_ops_message)!.Visibility = ViewStates.Gone;
FindViewById<TextView>(Resource.Id.background_ops_submessage)!.Visibility = ViewStates.Gone;
Kp2aLog.Log("OPR: Finished posted Show ");
});
}
public void Hide()
{
new Handler(Looper.MainLooper).Post(() =>
App.Kp2a.UiThreadHandler.Post(() =>
{
Kp2aLog.Log("OPR: Starting posted Hide ");
String activityType = Context.GetType().FullName;
Kp2aLog.Log("Hiding background ops container in" + activityType);
Visibility = ViewStates.Gone;
Kp2aLog.Log("OPR: Finished posted Hide ");
});
}
public void UpdateMessage(string message)
{
new Handler(Looper.MainLooper).Post(() =>
App.Kp2a.UiThreadHandler.Post(() =>
{
Kp2aLog.Log("OPR: Starting posted UpdateMessage ");
TextView messageTextView = FindViewById<TextView>(Resource.Id.background_ops_message)!;
if (string.IsNullOrEmpty(message))
{
@@ -75,13 +80,15 @@ public class BackgroundOperationContainer : LinearLayout, IProgressUi
messageTextView.Visibility = ViewStates.Visible;
messageTextView.Text = message;
}
Kp2aLog.Log("OPR: Finished posted UpdateMessage ");
});
}
public void UpdateSubMessage(string submessage)
{
new Handler(Looper.MainLooper).Post(() =>
App.Kp2a.UiThreadHandler.Post(() =>
{
Kp2aLog.Log("OPR: Starting posted UpdateSubMessage ");
TextView subMessageTextView = FindViewById<TextView>(Resource.Id.background_ops_submessage)!;
if (string.IsNullOrEmpty(submessage))
{
@@ -92,6 +99,7 @@ public class BackgroundOperationContainer : LinearLayout, IProgressUi
subMessageTextView.Visibility = ViewStates.Visible;
subMessageTextView.Text = submessage;
}
Kp2aLog.Log("OPR: Finished posted UpdateSubMessage ");
});
}
}

View File

@@ -125,94 +125,103 @@ namespace keepass2android.view
if (_groupBaseActivity.IsFinishing)
return;
_entry = pw;
_pos = pos;
ev.FindViewById(Resource.Id.icon).Visibility = ViewStates.Visible;
ev.FindViewById(Resource.Id.check_mark).Visibility = ViewStates.Invisible;
_pos = pos;
try
{
_db = App.Kp2a.FindDatabaseForElement(_entry);
ImageView iv = (ImageView)ev.FindViewById(Resource.Id.icon);
bool isExpired = pw.Expires && pw.ExpiryTime < DateTime.Now;
if (isExpired)
{
_db.DrawableFactory.AssignDrawableTo(iv, Context, _db.KpDatabase, PwIcon.Expired, PwUuid.Zero, false);
} else
{
_db.DrawableFactory.AssignDrawableTo(iv, Context, _db.KpDatabase, pw.IconId, pw.CustomIconUuid, false);
}
String title = pw.Strings.ReadSafe(PwDefs.TitleField);
title = SprEngine.Compile(title, new SprContext(_entry, _db.KpDatabase, SprCompileFlags.All));
var str = new SpannableString(title);
if (isExpired)
{
str.SetSpan(new StrikethroughSpan(), 0, title.Length, SpanTypes.ExclusiveExclusive);
}
_textView.TextFormatted = str;
if (_defaultTextColor == null)
_defaultTextColor = _textView.TextColors.DefaultColor;
if (_groupActivity.IsBeingMoved(_entry.Uuid))
{
int elementBeingMoved = Context.Resources.GetColor(Resource.Color.md_theme_inversePrimary);
_textView.SetTextColor(new Color(elementBeingMoved));
}
else
_textView.SetTextColor(new Color((int)_defaultTextColor));
String detail = pw.Strings.ReadSafe(PwDefs.UserNameField);
detail = SprEngine.Compile(detail, new SprContext(_entry, _db.KpDatabase, SprCompileFlags.All));
if ((_showDetail == false) || (String.IsNullOrEmpty(detail)))
{
_textviewDetails.Visibility = ViewStates.Gone;
}
else
{
var strDetail = new SpannableString(detail);
if (isExpired)
{
strDetail.SetSpan(new StrikethroughSpan(), 0, detail.Length, SpanTypes.ExclusiveExclusive);
}
_textviewDetails.TextFormatted = strDetail;
_textviewDetails.Visibility = ViewStates.Visible;
}
if ( (!_showGroupFullPath) || (!_isSearchResult) ) {
_textgroupFullPath.Visibility = ViewStates.Gone;
}
else {
String groupDetail = pw.ParentGroup.GetFullPath();
if (App.Kp2a.OpenDatabases.Count() > 1)
{
groupDetail += "(" + App.Kp2a.GetFileStorage(_db.Ioc).GetDisplayName(_db.Ioc) + ")";
}
var strGroupDetail = new SpannableString (groupDetail);
if (isExpired) {
strGroupDetail.SetSpan (new StrikethroughSpan (), 0, groupDetail.Length, SpanTypes.ExclusiveExclusive);
}
_textgroupFullPath.TextFormatted = strGroupDetail;
_textgroupFullPath.Visibility = ViewStates.Visible;
}
//try to get totp data
UpdateTotp();
ev.FindViewById(Resource.Id.icon).Visibility = ViewStates.Visible;
ev.FindViewById(Resource.Id.check_mark).Visibility = ViewStates.Invisible;
_db = App.Kp2a.FindDatabaseForElement(_entry);
ImageView iv = (ImageView)ev.FindViewById(Resource.Id.icon);
bool isExpired = pw.Expires && pw.ExpiryTime < DateTime.Now;
if (isExpired)
{
_db.DrawableFactory.AssignDrawableTo(iv, Context, _db.KpDatabase, PwIcon.Expired, PwUuid.Zero, false);
}
else
{
_db.DrawableFactory.AssignDrawableTo(iv, Context, _db.KpDatabase, pw.IconId, pw.CustomIconUuid, false);
}
String title = pw.Strings.ReadSafe(PwDefs.TitleField);
title = SprEngine.Compile(title, new SprContext(_entry, _db.KpDatabase, SprCompileFlags.All));
var str = new SpannableString(title);
if (isExpired)
{
str.SetSpan(new StrikethroughSpan(), 0, title.Length, SpanTypes.ExclusiveExclusive);
}
_textView.TextFormatted = str;
if (_defaultTextColor == null)
_defaultTextColor = _textView.TextColors.DefaultColor;
if (_groupActivity.IsBeingMoved(_entry.Uuid))
{
int elementBeingMoved = Context.Resources.GetColor(Resource.Color.md_theme_inversePrimary);
_textView.SetTextColor(new Color(elementBeingMoved));
}
else
_textView.SetTextColor(new Color((int)_defaultTextColor));
String detail = pw.Strings.ReadSafe(PwDefs.UserNameField);
detail = SprEngine.Compile(detail, new SprContext(_entry, _db.KpDatabase, SprCompileFlags.All));
if ((_showDetail == false) || (String.IsNullOrEmpty(detail)))
{
_textviewDetails.Visibility = ViewStates.Gone;
}
else
{
var strDetail = new SpannableString(detail);
if (isExpired)
{
strDetail.SetSpan(new StrikethroughSpan(), 0, detail.Length, SpanTypes.ExclusiveExclusive);
}
_textviewDetails.TextFormatted = strDetail;
_textviewDetails.Visibility = ViewStates.Visible;
}
if ((!_showGroupFullPath) || (!_isSearchResult))
{
_textgroupFullPath.Visibility = ViewStates.Gone;
}
else
{
String groupDetail = pw.ParentGroup.GetFullPath();
if (App.Kp2a.OpenDatabases.Count() > 1)
{
groupDetail += "(" + App.Kp2a.GetFileStorage(_db.Ioc).GetDisplayName(_db.Ioc) + ")";
}
var strGroupDetail = new SpannableString(groupDetail);
if (isExpired)
{
strGroupDetail.SetSpan(new StrikethroughSpan(), 0, groupDetail.Length, SpanTypes.ExclusiveExclusive);
}
_textgroupFullPath.TextFormatted = strGroupDetail;
_textgroupFullPath.Visibility = ViewStates.Visible;
}
//try to get totp data
UpdateTotp();
}
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
}
}