From af02ca7599a529be4907ac85d6ca33df7b89b544 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Wed, 19 Aug 2015 07:03:12 +0200 Subject: [PATCH] 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 --- src/KeePass.sln | 1 + .../Kp2aBusinessLogic.csproj | 3 +- src/Kp2aBusinessLogic/UiStringKey.cs | 4 +- .../database/edit/DeleteEntry.cs | 82 ++----- .../database/edit/DeleteGroup.cs | 98 +-------- .../database/edit/DeleteRunnable.cs | 203 ++++++++++++++---- .../database/edit/MoveElement.cs | 70 ------ src/keepass2android/EntryEditActivity.cs | 4 +- src/keepass2android/GroupActivity.cs | 9 +- src/keepass2android/GroupBaseActivity.cs | 161 ++++++++------ src/keepass2android/PwGroupListAdapter.cs | 14 +- src/keepass2android/QuickUnlock.cs | 8 +- .../Resources/layout/QuickUnlock.xml | 4 +- .../Resources/layout/file_row.xml | 4 +- .../Resources/layout/group.xml | 6 +- .../Resources/layout/recent_files.xml | 5 +- .../Resources/values/strings.xml | 4 +- src/keepass2android/ShareUrlResults.cs | 9 +- src/keepass2android/app/AppTask.cs | 48 +++-- src/keepass2android/keepass2android.csproj | 21 +- src/keepass2android/search/SearchResults.cs | 8 +- src/keepass2android/views/PwGroupView.cs | 2 +- 22 files changed, 394 insertions(+), 374 deletions(-) delete mode 100644 src/Kp2aBusinessLogic/database/edit/MoveElement.cs diff --git a/src/KeePass.sln b/src/KeePass.sln index 9ec7b121..9c7c80a1 100644 --- a/src/KeePass.sln +++ b/src/KeePass.sln @@ -95,6 +95,7 @@ Global {A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|x64.Build.0 = Debug|Any CPU {A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Any CPU.ActiveCfg = Release|Any CPU {A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Any CPU.Build.0 = Release|Any CPU + {A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Any CPU.Deploy.0 = Release|Any CPU {A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Mixed Platforms.Build.0 = Release|Any CPU {A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU diff --git a/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj b/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj index b9c8faa5..c087ccfd 100644 --- a/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj +++ b/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj @@ -56,8 +56,9 @@ + - + diff --git a/src/Kp2aBusinessLogic/UiStringKey.cs b/src/Kp2aBusinessLogic/UiStringKey.cs index 3bc22d20..32921742 100644 --- a/src/Kp2aBusinessLogic/UiStringKey.cs +++ b/src/Kp2aBusinessLogic/UiStringKey.cs @@ -57,6 +57,8 @@ namespace keepass2android CopyFileRequiredForEditing, DuplicateUuidsError, DuplicateUuidsErrorAdditional, - KdbBetaWarning + KdbBetaWarning, + DeletingItems, + AskDeletePermanentlyItems } } diff --git a/src/Kp2aBusinessLogic/database/edit/DeleteEntry.cs b/src/Kp2aBusinessLogic/database/edit/DeleteEntry.cs index 4fcd4139..4280697c 100644 --- a/src/Kp2aBusinessLogic/database/edit/DeleteEntry.cs +++ b/src/Kp2aBusinessLogic/database/edit/DeleteEntry.cs @@ -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 touchedGroups, List 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; } + } } } diff --git a/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs b/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs index 6341eb91..61b1d10a 100644 --- a/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs +++ b/src/Kp2aBusinessLogic/database/edit/DeleteGroup.cs @@ -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 touchedGroups, List 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; } + } } } diff --git a/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs b/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs index df2c3f7e..e8fa4147 100644 --- a/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs +++ b/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs @@ -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 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 touchedGroups = new List(); + List permanentlyDeletedGroups = new List(); + 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 touchedGroups, List permanentlyDeletedGroups); + + public abstract UiStringKey StatusMessage { get; } + + protected bool DoDeleteGroup(PwGroup pg, List touchedGroups, List 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; + } } } diff --git a/src/Kp2aBusinessLogic/database/edit/MoveElement.cs b/src/Kp2aBusinessLogic/database/edit/MoveElement.cs deleted file mode 100644 index 3b6bcfe0..00000000 --- a/src/Kp2aBusinessLogic/database/edit/MoveElement.cs +++ /dev/null @@ -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(); - } - } -} diff --git a/src/keepass2android/EntryEditActivity.cs b/src/keepass2android/EntryEditActivity.cs index 412a5d2d..df9499ed 100644 --- a/src/keepass2android/EntryEditActivity.cs +++ b/src/keepass2android/EntryEditActivity.cs @@ -951,7 +951,9 @@ namespace keepass2android } else { - FindViewById(Resource.Id.entry_tags_label).Visibility = ViewStates.Gone; + var view = FindViewById(Resource.Id.entry_tags_label); + if (view != null) + view.Visibility = ViewStates.Gone; FindViewById(Resource.Id.entry_tags).Visibility = ViewStates.Gone; } diff --git a/src/keepass2android/GroupActivity.cs b/src/keepass2android/GroupActivity.cs index ed6db2bb..051cf4d2 100644 --- a/src/keepass2android/GroupActivity.cs +++ b/src/keepass2android/GroupActivity.cs @@ -29,7 +29,8 @@ using Android.Content.PM; namespace keepass2android { - [Activity(Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, Theme = "@style/MyTheme_ActionBar")] + [Activity(Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, Theme = "@style/MyTheme_ActionBar")] + [MetaData("android.app.searchable", Resource = AppNames.Searchable)] [MetaData("android.app.default_searchable",Value="keepass2android.search.SearchResults")] public class GroupActivity : GroupBaseActivity { @@ -83,8 +84,12 @@ namespace keepass2android get { return App.Kp2a.GetDb().CanWrite && ((this.Group.ParentGroup != null) || App.Kp2a.GetDb().DatabaseFormat.CanHaveEntriesInRootGroup); } } + public override bool OnCreateOptionsMenu(IMenu menu) + { + return base.OnCreateOptionsMenu(menu); + } - protected override void OnCreate (Bundle savedInstanceState) + protected override void OnCreate (Bundle savedInstanceState) { base.OnCreate (savedInstanceState); diff --git a/src/keepass2android/GroupBaseActivity.cs b/src/keepass2android/GroupBaseActivity.cs index 16113561..01e26d61 100644 --- a/src/keepass2android/GroupBaseActivity.cs +++ b/src/keepass2android/GroupBaseActivity.cs @@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file */ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using Android.App; @@ -182,6 +183,7 @@ namespace keepass2android private String strCachedGroupUuid = null; + public String UuidGroup { get { if (strCachedGroupUuid == null) { @@ -222,6 +224,11 @@ namespace keepass2android get { return (BaseAdapter) FragmentManager.FindFragmentById(Resource.Id.list_fragment).ListAdapter; } } + public virtual bool IsSearchResult + { + get { return false; } + } + /*TODO * protected override void OnListItemClick(ListView l, View v, int position, long id) { base.OnListItemClick(l, v, position, id); @@ -234,7 +241,9 @@ namespace keepass2android */ protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); - + + Android.Util.Log.Debug("KP2A", "Creating GBA"); + AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent); // Likely the app has been killed exit the activity @@ -266,8 +275,8 @@ namespace keepass2android - FindViewById(Resource.Id.cancel_insert_element).Click += (sender, args) => StopMovingElement(); - FindViewById(Resource.Id.insert_element).Click += (sender, args) => InsertElement(); + FindViewById(Resource.Id.cancel_insert_element).Click += (sender, args) => StopMovingElements(); + FindViewById(Resource.Id.insert_element).Click += (sender, args) => InsertElements(); SetResult(KeePass.ExitNormal); @@ -275,13 +284,15 @@ namespace keepass2android } - private void InsertElement() + private void InsertElements() { - MoveElementTask moveElementTask = (MoveElementTask)AppTask; - IStructureItem elementToMove = App.Kp2a.GetDb().KpDatabase.RootGroup.FindObject(moveElementTask.Uuid, true, null); + MoveElementsTask moveElementsTask = (MoveElementsTask)AppTask; + IEnumerable elementsToMove = + moveElementsTask.Uuids.Select(uuid => App.Kp2a.GetDb().KpDatabase.RootGroup.FindObject(uuid, true, null)); + - var moveElement = new MoveElement(elementToMove, Group, this, App.Kp2a, new ActionOnFinish((success, message) => { StopMovingElement(); if (!String.IsNullOrEmpty(message)) Toast.MakeText(this, message, ToastLength.Long).Show();})); + var moveElement = new MoveElements(elementsToMove.ToList(), Group, this, App.Kp2a, new ActionOnFinish((success, message) => { StopMovingElements(); if (!String.IsNullOrEmpty(message)) Toast.MakeText(this, message, ToastLength.Long).Show();})); var progressTask = new ProgressTask(App.Kp2a, this, moveElement); progressTask.Run(); @@ -381,34 +392,34 @@ namespace keepass2android return true; } } + /* + public override bool OnCreateOptionsMenu(IMenu menu) { + return base.OnCreateOptionsMenu(menu); - public override bool OnCreateOptionsMenu(IMenu menu) { - base.OnCreateOptionsMenu(menu); - - MenuInflater inflater = MenuInflater; - inflater.Inflate(Resource.Menu.group, menu); - var searchManager = (SearchManager) GetSystemService(SearchService); - IMenuItem searchItem = menu.FindItem(Resource.Id.menu_search); - var view = MenuItemCompat.GetActionView(searchItem); - var searchView = view.JavaCast(); + MenuInflater inflater = MenuInflater; + inflater.Inflate(Resource.Menu.group, menu); + var searchManager = (SearchManager) GetSystemService(SearchService); + IMenuItem searchItem = menu.FindItem(Resource.Id.menu_search); + var view = MenuItemCompat.GetActionView(searchItem); + var searchView = view.JavaCast(); - searchView.SetSearchableInfo(searchManager.GetSearchableInfo(ComponentName)); - searchView.SetOnSuggestionListener(new SuggestionListener(searchView.SuggestionsAdapter, this, searchItem)); - searchView.SetOnQueryTextListener(new OnQueryTextListener(this)); + searchView.SetSearchableInfo(searchManager.GetSearchableInfo(ComponentName)); + searchView.SetOnSuggestionListener(new SuggestionListener(searchView.SuggestionsAdapter, this, searchItem)); + searchView.SetOnQueryTextListener(new OnQueryTextListener(this)); - var item = menu.FindItem(Resource.Id.menu_sync); - if (item != null) - { - if (App.Kp2a.GetDb().Ioc.IsLocalFile()) - item.SetVisible(false); - else - item.SetVisible(true); + var item = menu.FindItem(Resource.Id.menu_sync); + if (item != null) + { + if (App.Kp2a.GetDb().Ioc.IsLocalFile()) + item.SetVisible(false); + else + item.SetVisible(true); + } + //return true; } - - return true; - } - - + */ + + public override bool OnPrepareOptionsMenu(IMenu menu) { if ( ! base.OnPrepareOptionsMenu(menu) ) { @@ -613,10 +624,10 @@ namespace keepass2android public bool IsBeingMoved(PwUuid uuid) { - MoveElementTask moveElementTask = AppTask as MoveElementTask; - if (moveElementTask != null) + MoveElementsTask moveElementsTask = AppTask as MoveElementsTask; + if (moveElementsTask != null) { - if (moveElementTask.Uuid.Equals(uuid)) + if (moveElementsTask.Uuids.Any(uuidMoved => uuidMoved.Equals(uuid))) return true; } return false; @@ -629,16 +640,16 @@ namespace keepass2android } - public void StartMovingElement() + public void StartMovingElements() { - ShowInsertElementButtons(); + ShowInsertElementsButtons(); //TODO Required? GroupView.ListView.InvalidateViews(); BaseAdapter adapter = (BaseAdapter)ListAdapter; adapter.NotifyDataSetChanged(); } - public void ShowInsertElementButtons() + public void ShowInsertElementsButtons() { FindViewById(Resource.Id.fabCancelAddNew).Visibility = ViewStates.Gone; FindViewById(Resource.Id.fabAddNewGroup).Visibility = ViewStates.Gone; @@ -649,14 +660,17 @@ namespace keepass2android FindViewById(Resource.Id.divider2).Visibility = ViewStates.Visible; } - public void StopMovingElement() + public void StopMovingElements() { try { - MoveElementTask moveElementTask = (MoveElementTask)AppTask; - IStructureItem elementToMove = App.Kp2a.GetDb().KpDatabase.RootGroup.FindObject(moveElementTask.Uuid, true, null); - if (elementToMove.ParentGroup != Group) - App.Kp2a.GetDb().Dirty.Add(elementToMove.ParentGroup); + MoveElementsTask moveElementsTask = (MoveElementsTask)AppTask; + foreach (var uuid in moveElementsTask.Uuids) + { + IStructureItem elementToMove = App.Kp2a.GetDb().KpDatabase.RootGroup.FindObject(uuid, true, null); + if (elementToMove.ParentGroup != Group) + App.Kp2a.GetDb().Dirty.Add(elementToMove.ParentGroup); + } } catch (Exception e) { @@ -680,7 +694,9 @@ namespace keepass2android public class GroupListFragment : ListFragment, AbsListView.IMultiChoiceModeListener { - public override void OnActivityCreated(Bundle savedInstanceState) + private ActionMode _mode; + + public override void OnActivityCreated(Bundle savedInstanceState) { base.OnActivityCreated(savedInstanceState); if (App.Kp2a.GetDb().CanWrite) @@ -709,29 +725,53 @@ namespace keepass2android public bool OnActionItemClicked(ActionMode mode, IMenuItem item) { + var listView = FragmentManager.FindFragmentById(Resource.Id.list_fragment).ListView; + var checkedItemPositions = listView.CheckedItemPositions; + + List checkedItems = new List(); + for (int i = 0; i < checkedItemPositions.Size(); i++) + { + if (checkedItemPositions.ValueAt(i)) + { + //TODO make sure position is also correct when scrolling (more items in adapter than on screen) + checkedItems.Add(((PwGroupListAdapter) ListAdapter).GetItemAtPosition(checkedItemPositions.KeyAt(i))); + } + } + + //shouldn't happen, just in case... + if (!checkedItems.Any()) + { + return false; + } + switch (item.ItemId) { case Resource.Id.menu_delete: - /*Handler handler = new Handler(); - DeleteEntry task = new DeleteEntry(Activity, App.Kp2a, _entry, - new GroupBaseActivity.RefreshTask(handler, ((GroupBaseActivity)Activity))); - task.Start();*/ - Toast.MakeText(((GroupBaseActivity) Activity), "todo delete", ToastLength.Long).Show(); - return true; + Handler handler = new Handler(); + DeleteMultipleItems task = new DeleteMultipleItems((GroupBaseActivity)Activity, App.Kp2a.GetDb(), checkedItems, + new GroupBaseActivity.RefreshTask(handler, ((GroupBaseActivity)Activity)), App.Kp2a); + task.Start(); + break; case Resource.Id.menu_move: - /*NavigateToFolderAndLaunchMoveElementTask navMove = - new NavigateToFolderAndLaunchMoveElementTask(_entry.ParentGroup, _entry.Uuid, _isSearchResult); - ((GroupBaseActivity)Activity).StartTask(navMove);*/ - Toast.MakeText(((GroupBaseActivity)Activity), "todo move", ToastLength.Long).Show(); - return true; - /*TODO for search results case Resource.Id.menu_navigate: - NavigateToFolder navNavigate = new NavigateToFolder(_entry.ParentGroup, true); - ((GroupBaseActivity)Activity).StartTask(navNavigate); - return true;*/ + var navMove = new NavigateToFolderAndLaunchMoveElementTask(checkedItems.First().ParentGroup, checkedItems.Select(i => i.Uuid).ToList(), ((GroupBaseActivity)Activity).IsSearchResult); + ((GroupBaseActivity)Activity).StartTask(navMove); + break; + /*TODO for search results case Resource.Id.menu_navigate: + NavigateToFolder navNavigate = new NavigateToFolder(_entry.ParentGroup, true); + ((GroupBaseActivity)Activity).StartTask(navNavigate); + break;*/ + default: + return false; + } - return false; + listView.ClearChoices(); + ((BaseAdapter)ListAdapter).NotifyDataSetChanged(); + if (_mode != null) + mode.Finish(); + + return true; } public bool OnCreateActionMode(ActionMode mode, IMenu menu) @@ -741,7 +781,7 @@ namespace keepass2android //mode.Title = "Select Items"; Android.Util.Log.Debug("KP2A", "Create action mode" + mode); ((PwGroupListAdapter)ListView.Adapter).NotifyDataSetChanged(); - + _mode = mode; return true; } @@ -750,6 +790,7 @@ namespace keepass2android Android.Util.Log.Debug("KP2A", "Destroy action mode" + mode); ((PwGroupListAdapter)ListView.Adapter).NotifyDataSetChanged(); + _mode = null; } public bool OnPrepareActionMode(ActionMode mode, IMenu menu) diff --git a/src/keepass2android/PwGroupListAdapter.cs b/src/keepass2android/PwGroupListAdapter.cs index ca96c51e..d6a064ba 100644 --- a/src/keepass2android/PwGroupListAdapter.cs +++ b/src/keepass2android/PwGroupListAdapter.cs @@ -24,6 +24,7 @@ using Android.Widget; using Android.Preferences; using KeePassLib; using keepass2android.view; +using KeePassLib.Interfaces; namespace keepass2android { @@ -327,7 +328,18 @@ namespace keepass2android return ev; } - + + public IStructureItem GetItemAtPosition(int keyAt) + { + if (keyAt < _groupsForViewing.Count) + { + return _groupsForViewing[keyAt]; + } + else + { + return _entriesForViewing[keyAt - _groupsForViewing.Count]; + } + } } } diff --git a/src/keepass2android/QuickUnlock.cs b/src/keepass2android/QuickUnlock.cs index 2aa46a0b..aea34942 100644 --- a/src/keepass2android/QuickUnlock.cs +++ b/src/keepass2android/QuickUnlock.cs @@ -68,8 +68,8 @@ namespace keepass2android if (App.Kp2a.GetDb().KpDatabase.Name != "") { - FindViewById(Resource.Id.filename_label).Visibility = ViewStates.Invisible; - ((TextView) FindViewById(Resource.Id.qu_filename)).Text = App.Kp2a.GetDb().KpDatabase.Name; + FindViewById(Resource.Id.filename_label).Visibility = ViewStates.Visible; + ((TextView) FindViewById(Resource.Id.filename_label)).Text = App.Kp2a.GetDb().KpDatabase.Name; } else { @@ -78,11 +78,11 @@ namespace keepass2android .GetBoolean(GetString(Resource.String.RememberRecentFiles_key), Resources.GetBoolean(Resource.Boolean.RememberRecentFiles_default))) { - ((TextView) FindViewById(Resource.Id.qu_filename)).Text = App.Kp2a.GetFileStorage(_ioc).GetDisplayName(_ioc); + ((TextView) FindViewById(Resource.Id.filename_label)).Text = App.Kp2a.GetFileStorage(_ioc).GetDisplayName(_ioc); } else { - ((TextView) FindViewById(Resource.Id.qu_filename)).Text = "*****"; + ((TextView) FindViewById(Resource.Id.filename_label)).Text = "*****"; } } diff --git a/src/keepass2android/Resources/layout/QuickUnlock.xml b/src/keepass2android/Resources/layout/QuickUnlock.xml index b54d7d6a..86769f1c 100644 --- a/src/keepass2android/Resources/layout/QuickUnlock.xml +++ b/src/keepass2android/Resources/layout/QuickUnlock.xml @@ -77,7 +77,7 @@ android:src="@drawable/toolbar_bg_quickunlock" app:layout_collapseMode="parallax" /> diff --git a/src/keepass2android/Resources/layout/file_row.xml b/src/keepass2android/Resources/layout/file_row.xml index 9db6e43e..998154ca 100644 --- a/src/keepass2android/Resources/layout/file_row.xml +++ b/src/keepass2android/Resources/layout/file_row.xml @@ -20,5 +20,7 @@ \ No newline at end of file diff --git a/src/keepass2android/Resources/layout/group.xml b/src/keepass2android/Resources/layout/group.xml index a096282d..85852ad7 100644 --- a/src/keepass2android/Resources/layout/group.xml +++ b/src/keepass2android/Resources/layout/group.xml @@ -26,14 +26,14 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" - android:text="Insert here" + android:text="@string/insert_element_here" style="@style/BottomBarButton" />