implemented action items for move and delete (includes extension of apptasks to delete/move multiple items)

fixed display issue in QuickUnlock
fixed text color of recent files
This commit is contained in:
Philipp Crocoll
2015-08-19 07:03:12 +02:00
parent fabe0904c0
commit af02ca7599
22 changed files with 394 additions and 374 deletions

View File

@@ -56,8 +56,9 @@
</ItemGroup>
<ItemGroup>
<Compile Include="database\CheckDatabaseForChanges.cs" />
<Compile Include="database\edit\DeleteMultipleItems.cs" />
<Compile Include="database\edit\EditGroup.cs" />
<Compile Include="database\edit\MoveElement.cs" />
<Compile Include="database\edit\MoveElements.cs" />
<Compile Include="database\KdbDatabaseFormat.cs" />
<Compile Include="database\KdbxDatabaseFormat.cs" />
<Compile Include="database\PwEntryOutput.cs" />

View File

@@ -57,6 +57,8 @@ namespace keepass2android
CopyFileRequiredForEditing,
DuplicateUuidsError,
DuplicateUuidsErrorAdditional,
KdbBetaWarning
KdbBetaWarning,
DeletingItems,
AskDeletePermanentlyItems
}
}

View File

@@ -16,16 +16,19 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
*/
using System;
using System.Collections.Generic;
using Android.Content;
using KeePassLib;
using KeePassLib.Interfaces;
namespace keepass2android
{
public class DeleteEntry : DeleteRunnable {
private readonly PwEntry _entry;
private UiStringKey _statusMessage;
public DeleteEntry(Context ctx, IKp2aApp app, PwEntry entry, OnFinish finish):base(finish, app) {
public DeleteEntry(Context ctx, IKp2aApp app, PwEntry entry, OnFinish finish):base(finish, app) {
Ctx = ctx;
Db = app.GetDb();
_entry = entry;
@@ -48,76 +51,15 @@ namespace keepass2android
}
}
public override void Run()
{
StatusLogger.UpdateMessage(UiStringKey.DeletingEntry);
PwDatabase pd = Db.KpDatabase;
protected override void PerformDelete(List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups)
{
DoDeleteEntry(_entry, touchedGroups);
}
PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
bool bUpdateGroupList = false;
DateTime dtNow = DateTime.Now;
PwEntry pe = _entry;
PwGroup pgParent = pe.ParentGroup;
if(pgParent != null)
{
pgParent.Entries.Remove(pe);
//TODO check if RecycleBin is deleted
//TODO no recycle bin in KDB
if ((DeletePermanently) || (!CanRecycle))
{
PwDeletedObject pdo = new PwDeletedObject(pe.Uuid, dtNow);
pd.DeletedObjects.Add(pdo);
_onFinishToRun = new ActionOnFinish((success, message) =>
{
if (success)
{
// Mark parent dirty
Db.Dirty.Add(pgParent);
}
else
{
// Let's not bother recovering from a failure to save a deleted entry. It is too much work.
App.LockDatabase(false);
}
}, OnFinishToRun);
}
else // Recycle
{
EnsureRecycleBinExists(ref pgRecycleBin, ref bUpdateGroupList);
pgRecycleBin.AddEntry(pe, true, true);
pe.Touch(false);
_onFinishToRun = new ActionOnFinish( (success, message) =>
{
if ( success ) {
// Mark previous parent dirty
Db.Dirty.Add(pgParent);
// Mark new parent dirty
Db.Dirty.Add(pgRecycleBin);
// mark root dirty if recycle bin was created
if (bUpdateGroupList)
Db.Dirty.Add(Db.Root);
} else {
// Let's not bother recovering from a failure to save a deleted entry. It is too much work.
App.LockDatabase(false);
}
}, OnFinishToRun);
}
}
// Commit database
SaveDb save = new SaveDb(Ctx, App, OnFinishToRun, false);
save.SetStatusLogger(StatusLogger);
save.Run();
}
public override UiStringKey StatusMessage
{
get { return UiStringKey.DeletingEntry; }
}
}
}

View File

@@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
*/
using System;
using System.Collections.Generic;
using Android.Content;
using KeePassLib;
@@ -68,96 +69,15 @@ namespace keepass2android
}
}
public override void Run() {
StatusLogger.UpdateMessage(UiStringKey.DeletingGroup);
//from KP Desktop
PwGroup pg = _group;
PwGroup pgParent = pg.ParentGroup;
if(pgParent == null) return; // Can't remove virtual or root group
PwDatabase pd = Db.KpDatabase;
PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
pgParent.Groups.Remove(pg);
if ((DeletePermanently) || (!CanRecycle))
{
pg.DeleteAllObjects(pd);
PwDeletedObject pdo = new PwDeletedObject(pg.Uuid, DateTime.Now);
pd.DeletedObjects.Add(pdo);
_onFinishToRun = new AfterDeletePermanently(OnFinishToRun, App, _group);
}
else // Recycle
{
bool groupListUpdateRequired = false;
EnsureRecycleBinExists(ref pgRecycleBin, ref groupListUpdateRequired);
pgRecycleBin.AddGroup(pg, true, true);
pg.Touch(false);
_onFinishToRun = new ActionOnFinish((success, message) =>
{
if ( success ) {
// Mark new parent (Recycle bin) dirty
PwGroup parent = _group.ParentGroup;
if ( parent != null ) {
Db.Dirty.Add(parent);
}
//Mark old parent dirty:
Db.Dirty.Add(pgParent);
protected override void PerformDelete(List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups)
{
DoDeleteGroup(_group, touchedGroups, permanentlyDeletedGroups);
}
// mark root dirty if recycle bin was created
if (groupListUpdateRequired)
Db.Dirty.Add(Db.Root);
} else {
// Let's not bother recovering from a failure to save a deleted group. It is too much work.
App.LockDatabase(false);
}
}, OnFinishToRun);
}
// Save
SaveDb save = new SaveDb(Ctx, App, OnFinishToRun, DontSave);
save.SetStatusLogger(StatusLogger);
save.Run();
}
private class AfterDeletePermanently : OnFinish {
readonly IKp2aApp _app;
readonly PwGroup _group;
public AfterDeletePermanently(OnFinish finish, IKp2aApp app, PwGroup group):base(finish) {
_app = app;
_group = group;
}
public override void Run() {
if ( Success ) {
// Remove from group global
_app.GetDb().Groups.Remove(_group.Uuid);
// Remove group from the dirty global (if it is present), not a big deal if this fails (doesn't throw)
_app.GetDb().Dirty.Remove(_group);
// Mark parent dirty
PwGroup parent = _group.ParentGroup;
if ( parent != null ) {
_app.GetDb().Dirty.Add(parent);
}
} else {
// Let's not bother recovering from a failure to save a deleted group. It is too much work.
_app.LockDatabase(false);
}
base.Run();
}
}
public override UiStringKey StatusMessage
{
get { return UiStringKey.DeletingGroup; }
}
}
}

View File

@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using Android.Content;
using KeePassLib;
@@ -5,12 +7,13 @@ namespace keepass2android
{
public abstract class DeleteRunnable : RunnableOnFinish
{
protected DeleteRunnable(OnFinish finish, IKp2aApp app):base(finish)
protected DeleteRunnable(OnFinish finish, IKp2aApp app)
: base(finish)
{
App = app;
App = app;
}
protected IKp2aApp App;
protected IKp2aApp App;
protected Database Db;
@@ -22,9 +25,9 @@ namespace keepass2android
Db = db;
}
private bool _deletePermanently = true;
public bool DeletePermanently
{
get
@@ -44,52 +47,61 @@ namespace keepass2android
protected bool CanRecycleGroup(PwGroup pgParent)
{
bool bShiftPressed = false;
PwDatabase pd = Db.KpDatabase;
PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
bool bPermanent = false;
if (pgParent != null)
{
if (pd.RecycleBinEnabled == false)
{
Android.Util.Log.Debug("KP2A", "CanRecycle? No, RecycleBinIsNotEnabled");
bPermanent = true;
else if (bShiftPressed)
bPermanent = true;
}
else if (pgRecycleBin == null)
{
} // Recycle
else if (pgParent == pgRecycleBin)
{
Android.Util.Log.Debug("KP2A", "CanRecycle? No, Can't recycle RecycleBin");
bPermanent = true;
}
else if (pgParent.IsContainedIn(pgRecycleBin))
{
Android.Util.Log.Debug("KP2A", "CanRecycle? No, "+pgParent.Name+" is in RecycleBin");
bPermanent = true;
}
}
return !bPermanent;
}
protected void EnsureRecycleBinExists(ref PwGroup pgRecycleBin,
ref bool bGroupListUpdateRequired)
ref bool bGroupListUpdateRequired)
{
if ((Db == null) || (Db.KpDatabase == null)) { return; }
if(pgRecycleBin == Db.KpDatabase.RootGroup)
if (pgRecycleBin == Db.KpDatabase.RootGroup)
{
pgRecycleBin = null;
}
if(pgRecycleBin == null)
if (pgRecycleBin == null)
{
pgRecycleBin = new PwGroup(true, true, App.GetResourceString(UiStringKey.RecycleBin),
PwIcon.TrashBin)
PwIcon.TrashBin)
{
EnableAutoType = false,
EnableSearching = false,
EnableAutoType = false,
EnableSearching = false,
IsExpanded = false
};
Db.KpDatabase.RootGroup.AddGroup(pgRecycleBin, true);
Db.Groups[pgRecycleBin.Uuid] = pgRecycleBin;
Db.KpDatabase.RecycleBinUuid = pgRecycleBin.Uuid;
bGroupListUpdateRequired = true;
}
else { System.Diagnostics.Debug.Assert(pgRecycleBin.Uuid.Equals(Db.KpDatabase.RecycleBinUuid)); }
@@ -99,36 +111,155 @@ namespace keepass2android
{
get;
}
public void Start()
{
if (CanRecycle)
{
App.AskYesNoCancel(UiStringKey.AskDeletePermanently_title,
QuestionsResourceId,
(dlgSender, dlgEvt) =>
{
DeletePermanently = true;
ProgressTask pt = new ProgressTask(App, Ctx, this);
pt.Run();
},
(dlgSender, dlgEvt) => {
DeletePermanently = false;
ProgressTask pt = new ProgressTask(App, Ctx, this);
pt.Run();
},
(dlgSender, dlgEvt) => {},
Ctx);
App.AskYesNoCancel(UiStringKey.AskDeletePermanently_title,
QuestionsResourceId,
(dlgSender, dlgEvt) =>
{
DeletePermanently = true;
ProgressTask pt = new ProgressTask(App, Ctx, this);
pt.Run();
},
(dlgSender, dlgEvt) =>
{
DeletePermanently = false;
ProgressTask pt = new ProgressTask(App, Ctx, this);
pt.Run();
},
(dlgSender, dlgEvt) => { },
Ctx);
} else
}
else
{
ProgressTask pt = new ProgressTask(App, Ctx, this);
pt.Run();
}
}
protected void DoDeleteEntry(PwEntry pe, List<PwGroup> touchedGroups)
{
PwDatabase pd = Db.KpDatabase;
PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
bool bUpdateGroupList = false;
DateTime dtNow = DateTime.Now;
PwGroup pgParent = pe.ParentGroup;
if (pgParent != null)
{
pgParent.Entries.Remove(pe);
//TODO check if RecycleBin is deleted
//TODO no recycle bin in KDB
if ((DeletePermanently) || (!CanRecycle))
{
PwDeletedObject pdo = new PwDeletedObject(pe.Uuid, dtNow);
pd.DeletedObjects.Add(pdo);
touchedGroups.Add(pgParent);
}
else // Recycle
{
EnsureRecycleBinExists(ref pgRecycleBin, ref bUpdateGroupList);
pgRecycleBin.AddEntry(pe, true, true);
pe.Touch(false);
touchedGroups.Add(pgParent);
// Mark new parent dirty
touchedGroups.Add(pgRecycleBin);
// mark root dirty if recycle bin was created
touchedGroups.Add(Db.Root);
}
}
}
public override void Run()
{
StatusLogger.UpdateMessage(StatusMessage);
List<PwGroup> touchedGroups = new List<PwGroup>();
List<PwGroup> permanentlyDeletedGroups = new List<PwGroup>();
Android.Util.Log.Debug("KP2A", "Calling PerformDelete..");
PerformDelete(touchedGroups, permanentlyDeletedGroups);
_onFinishToRun = new ActionOnFinish((success, message) =>
{
if (success)
{
foreach (var g in touchedGroups)
Db.Dirty.Add(g);
foreach (var g in permanentlyDeletedGroups)
{
//remove groups from global lists if present there
Db.Dirty.Remove(g);
Db.Groups.Remove(g.Uuid);
}
}
else
{
// Let's not bother recovering from a failure to save. It is too much work.
App.LockDatabase(false);
}
}, OnFinishToRun);
// Commit database
SaveDb save = new SaveDb(Ctx, App, OnFinishToRun, false);
save.SetStatusLogger(StatusLogger);
save.Run();
}
protected abstract void PerformDelete(List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups);
public abstract UiStringKey StatusMessage { get; }
protected bool DoDeleteGroup(PwGroup pg, List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups)
{
PwGroup pgParent = pg.ParentGroup;
if (pgParent == null) return false;
PwDatabase pd = Db.KpDatabase;
PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
pgParent.Groups.Remove(pg);
touchedGroups.Add(pgParent);
if ((DeletePermanently) || (!CanRecycle))
{
pg.DeleteAllObjects(pd);
PwDeletedObject pdo = new PwDeletedObject(pg.Uuid, DateTime.Now);
pd.DeletedObjects.Add(pdo);
permanentlyDeletedGroups.Add(pg);
}
else // Recycle
{
bool groupListUpdateRequired = false;
EnsureRecycleBinExists(ref pgRecycleBin, ref groupListUpdateRequired);
pgRecycleBin.AddGroup(pg, true, true);
pg.Touch(false);
// Mark new parent (Recycle bin) touched
touchedGroups.Add(pg.ParentGroup);
// mark root touched if recycle bin was created
if (groupListUpdateRequired)
touchedGroups.Add(Db.Root);
}
return true;
}
}
}

View File

@@ -1,70 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Android.Content;
using KeePassLib;
using KeePassLib.Interfaces;
namespace keepass2android.database.edit
{
public class MoveElement: RunnableOnFinish
{
private readonly IStructureItem _elementToMove;
private readonly PwGroup _targetGroup;
private readonly Context _ctx;
private readonly IKp2aApp _app;
public MoveElement(IStructureItem elementToMove, PwGroup targetGroup, Context ctx, IKp2aApp app, OnFinish finish) : base(finish)
{
_elementToMove = elementToMove;
_targetGroup = targetGroup;
_ctx = ctx;
_app = app;
}
public override void Run()
{
_app.GetDb().Dirty.Add(_elementToMove.ParentGroup);
PwGroup pgParent = _elementToMove.ParentGroup;
if (pgParent != _targetGroup)
{
if (pgParent != null) // Remove from parent
{
PwEntry entry = _elementToMove as PwEntry;
if (entry != null)
{
pgParent.Entries.Remove(entry);
_targetGroup.AddEntry(entry, true, true);
}
else
{
PwGroup group = (PwGroup)_elementToMove;
if ((_targetGroup == group) || (_targetGroup.IsContainedIn(group)))
{
Finish(false, _app.GetResourceString(UiStringKey.CannotMoveGroupHere));
return;
}
pgParent.Groups.Remove(group);
_targetGroup.AddGroup(group, true, true);
}
}
}
_onFinishToRun = new ActionOnFinish((success, message) =>
{
if (!success)
{ // Let's not bother recovering from a failure.
_app.LockDatabase(false);
}
}, OnFinishToRun);
// Save
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, false);
save.SetStatusLogger(StatusLogger);
save.Run();
}
}
}