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

@ -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

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();
}
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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<GroupListFragment>(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<IStructureItem> 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<Android.Support.V7.Widget.SearchView>();
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<Android.Support.V7.Widget.SearchView>();
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<GroupListFragment>(Resource.Id.list_fragment).ListView;
var checkedItemPositions = listView.CheckedItemPositions;
List<IStructureItem> checkedItems = new List<IStructureItem>();
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)

View File

@ -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];
}
}
}
}

View File

@ -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 = "*****";
}
}

View File

@ -77,7 +77,7 @@
android:src="@drawable/toolbar_bg_quickunlock"
app:layout_collapseMode="parallax" />
<TextView
android:id="@+id/qu_filename"
android:id="@+id/filename_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
@ -114,7 +114,7 @@ android:paddingRight="16dp"
android:text="@string/QuickUnlock_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/qu_filename"
android:layout_below="@id/filename_label"
android:textSize="14sp"
/>

View File

@ -20,5 +20,7 @@
<TextView android:id="@+id/file_filename" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="4sp"
android:textColor="#cccccc"
android:paddingTop="4sp"
android:paddingBottom="4sp"
/>

View File

@ -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" />
<Button
android:id="@+id/cancel_insert_element"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="Cancel"
android:text="@string/cancel"
style="@style/BottomBarButton" />
</RelativeLayout>
<View
@ -47,6 +47,8 @@
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/divider2"
android:layout_below="@id/top"
android:fitsSystemWindows="true">
<fragment
android:name="keepass2android.GroupListFragment"

View File

@ -17,10 +17,9 @@
<ListView
android:id="@android:id/list"
android:layout_weight="1"
android:textColor="#cccccc"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingRight="8dp"
android:paddingLeft="8dp" />
android:paddingRight="0dp"
android:paddingLeft="0dp" />
</LinearLayout>

View File

@ -9,7 +9,7 @@
<string name="accept">Accept</string>
<string name="deny">Deny</string>
<string name="add_entry">Add entry</string>
<string name="edit_entry">Add entry</string>
<string name="edit_entry">Edit entry</string>
<string name="add_url_entry">Create entry for URL</string>
<string name="add_group">Add group</string>
<string name="add_group_title">Add Group</string>
@ -348,6 +348,7 @@
<string name="RecycleBin">Recycle Bin</string>
<string name="AskDeletePermanentlyEntry">Do you want to delete this entry permanently? Press No to recycle.</string>
<string name="AskDeletePermanentlyGroup">Do you want to delete this group permanently? Press No to recycle.</string>
<string name="AskDeletePermanentlyItems">Do you want to delete the selected elements permanently? Press No to recycle.</string>
<string name="AskDeletePermanently_title">Delete permanently?</string>
<string name="AskReloadFile_title">Reload file?</string>
<string name="AskReloadFile">The file which is currently open was changed by another program. Do you want to reload it?</string>
@ -360,6 +361,7 @@
<string name="AddingGroup">Adding group…</string>
<string name="DeletingEntry">Deleting entry…</string>
<string name="DeletingGroup">Deleting group…</string>
<string name="DeletingItems">Deleting elements…</string>
<string name="SettingPassword">Setting password…</string>
<string name="UndoingChanges">Undoing changes…</string>
<string name="TransformingKey">Transforming master key…</string>

View File

@ -53,9 +53,14 @@ namespace keepass2android
private Database _db;
protected override void OnCreate(Bundle savedInstanceState)
public override bool IsSearchResult
{
get { return true; }
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);

View File

@ -5,6 +5,7 @@ using Android.Content;
using Android.OS;
using Android.Widget;
using System.Collections.Generic;
using System.Linq;
using KeePassLib;
using KeePassLib.Security;
using KeePassLib.Utility;
@ -546,13 +547,13 @@ namespace keepass2android
/// <summary>
/// User is about to move an entry or group to another group
/// User is about to move entries and/or groups to another group
/// </summary>
public class MoveElementTask: AppTask
public class MoveElementsTask: AppTask
{
public const String UuidKey = "MoveElement_Uuid";
public const String UuidsKey = "MoveElement_Uuids";
public PwUuid Uuid
public IEnumerable<PwUuid> Uuids
{
get;
set;
@ -560,23 +561,33 @@ namespace keepass2android
public override void Setup(Bundle b)
{
Uuid = new PwUuid(MemUtil.HexStringToByteArray(b.GetString(UuidKey)));
}
public override IEnumerable<IExtra> Extras
{
get
{
yield return new StringExtra { Key = UuidKey, Value = MemUtil.ByteArrayToHexString(Uuid.UuidBytes) };
}
Uuids = b.GetString(UuidsKey).Split(';')
.Where(s => !String.IsNullOrEmpty(s))
.Select(stringPart => new PwUuid(MemUtil.HexStringToByteArray(stringPart)))
.ToList(); //property might be accessed several times, avoid parsing each time
}
public override IEnumerable<IExtra> Extras
{
get
{
yield return new StringExtra
{
Key = UuidsKey,
Value = Uuids.Select(uuid => MemUtil.ByteArrayToHexString(uuid.UuidBytes))
.Aggregate((a, b) => a + ";" + b)
};
}
}
public override void StartInGroupActivity(GroupBaseActivity groupBaseActivity)
{
base.StartInGroupActivity(groupBaseActivity);
groupBaseActivity.StartMovingElement();
groupBaseActivity.StartMovingElements();
}
public override void SetupGroupBaseActivityButtons(GroupBaseActivity groupBaseActivity)
{
groupBaseActivity.ShowInsertElementButtons();
groupBaseActivity.ShowInsertElementsButtons();
}
}
@ -902,19 +913,20 @@ namespace keepass2android
public class NavigateToFolderAndLaunchMoveElementTask: NavigateAndLaunchTask {
public NavigateToFolderAndLaunchMoveElementTask():base(){
public NavigateToFolderAndLaunchMoveElementTask()
{
}
public NavigateToFolderAndLaunchMoveElementTask(PwGroup groups, PwUuid uuid, bool toastEnable = false)
:base(groups, new MoveElementTask() { Uuid = uuid }, toastEnable) {
public NavigateToFolderAndLaunchMoveElementTask(PwGroup groups, List<PwUuid> uuids, bool toastEnable = false)
:base(groups, new MoveElementsTask() { Uuids = uuids }, toastEnable) {
}
public override void Setup(Bundle b) {
base.Setup(b);
TaskToBeLaunchedAfterNavigation = new MoveElementTask ();
TaskToBeLaunchedAfterNavigation = new MoveElementsTask ();
TaskToBeLaunchedAfterNavigation.Setup (b);
}

View File

@ -35,17 +35,18 @@
<Command type="BeforeBuild" command="UseManifestDebug.bat" />
</CustomCommands>
</CustomCommands>
<EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis>
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
<AndroidLinkSkip />
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidStoreUncompressedFileExtensions />
<MandroidI18n />
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
<JavaOptions />
<MonoDroidExtraArgs />
<Debugger>Xamarin</Debugger>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<DevInstrumentationEnabled>True</DevInstrumentationEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
@ -223,7 +224,9 @@
<Compile Include="views\TextWithHelp.cs" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\xml\searchable.xml" />
<AndroidResource Include="Resources\xml\searchable.xml">
<SubType>Designer</SubType>
</AndroidResource>
<AndroidAsset Include="Assets\fontawesome-webfont.ttf" />
<AndroidAsset Include="Assets\SourceCodePro-Regular.ttf" />
<None Include="packages.config" />
@ -540,7 +543,7 @@
<Name>KeePassLib2Android</Name>
</ProjectReference>
<ProjectReference Include="..\Kp2aBusinessLogic\Kp2aBusinessLogic.csproj">
<Project>{53A9CB7F-6553-4BC0-B56B-9410BB2E59AA}</Project>
<Project>{53a9cb7f-6553-4bc0-b56b-9410bb2e59aa}</Project>
<Name>Kp2aBusinessLogic</Name>
</ProjectReference>
<ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj">
@ -842,7 +845,9 @@
<AndroidResource Include="Resources\drawable\ic_menu_copy_holo_light.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\xml\searchable_debug.xml" />
<AndroidResource Include="Resources\xml\searchable_debug.xml">
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\device_access_new_account_holodark.png" />
@ -1317,7 +1322,7 @@
<AndroidResource Include="Resources\drawable-xhdpi\ic_nav_about.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mhdpi\ic_arrow_back_white_24dp.png" />
<AndroidResource Include="Resources\drawable-mdpi\ic_arrow_back_white_24dp.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-hdpi\ic_arrow_back_white_24dp.png" />

View File

@ -58,7 +58,8 @@ namespace keepass2android.search
get { return false; }
}
protected override void OnNewIntent(Intent intent)
{
ProcessIntent(intent);
@ -156,6 +157,11 @@ namespace keepass2android.search
StartActivity(i);
return true;
}
public override bool IsSearchResult
{
get { return true; }
}
}
}

View File

@ -125,7 +125,7 @@ namespace keepass2android.view
task.Start();
return true;
case MenuMove:
_groupBaseActivity.StartTask(new MoveElementTask { Uuid = _pwGroup.Uuid });
_groupBaseActivity.StartTask(new MoveElementsTask { Uuid = _pwGroup.Uuid });
return true;
case MenuEdit:
_groupBaseActivity.EditGroup(_pwGroup);