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}.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.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.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.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.Build.0 = Release|Any CPU
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Mixed Platforms.Deploy.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>
<ItemGroup> <ItemGroup>
<Compile Include="database\CheckDatabaseForChanges.cs" /> <Compile Include="database\CheckDatabaseForChanges.cs" />
<Compile Include="database\edit\DeleteMultipleItems.cs" />
<Compile Include="database\edit\EditGroup.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\KdbDatabaseFormat.cs" />
<Compile Include="database\KdbxDatabaseFormat.cs" /> <Compile Include="database\KdbxDatabaseFormat.cs" />
<Compile Include="database\PwEntryOutput.cs" /> <Compile Include="database\PwEntryOutput.cs" />

View File

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

View File

@ -16,14 +16,17 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
*/ */
using System; using System;
using System.Collections.Generic;
using Android.Content; using Android.Content;
using KeePassLib; using KeePassLib;
using KeePassLib.Interfaces;
namespace keepass2android namespace keepass2android
{ {
public class DeleteEntry : DeleteRunnable { public class DeleteEntry : DeleteRunnable {
private readonly PwEntry _entry; 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; Ctx = ctx;
@ -48,76 +51,15 @@ namespace keepass2android
} }
} }
public override void Run() protected override void PerformDelete(List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups)
{ {
StatusLogger.UpdateMessage(UiStringKey.DeletingEntry); DoDeleteEntry(_entry, touchedGroups);
PwDatabase pd = Db.KpDatabase; }
PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true); public override UiStringKey StatusMessage
bool bUpdateGroupList = false;
DateTime dtNow = DateTime.Now;
PwEntry pe = _entry;
PwGroup pgParent = pe.ParentGroup;
if(pgParent != null)
{ {
pgParent.Entries.Remove(pe); get { return UiStringKey.DeletingEntry; }
//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();
}
} }
} }

View File

@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
*/ */
using System; using System;
using System.Collections.Generic;
using Android.Content; using Android.Content;
using KeePassLib; using KeePassLib;
@ -68,95 +69,14 @@ namespace keepass2android
} }
} }
protected override void PerformDelete(List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups)
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); DoDeleteGroup(_group, touchedGroups, permanentlyDeletedGroups);
PwDeletedObject pdo = new PwDeletedObject(pg.Uuid, DateTime.Now);
pd.DeletedObjects.Add(pdo);
_onFinishToRun = new AfterDeletePermanently(OnFinishToRun, App, _group);
} }
else // Recycle
public override UiStringKey StatusMessage
{ {
bool groupListUpdateRequired = false; get { return UiStringKey.DeletingGroup; }
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);
// 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();
}
} }
} }

View File

@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using Android.Content; using Android.Content;
using KeePassLib; using KeePassLib;
@ -5,7 +7,8 @@ namespace keepass2android
{ {
public abstract class DeleteRunnable : RunnableOnFinish 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;
} }
@ -44,24 +47,33 @@ namespace keepass2android
protected bool CanRecycleGroup(PwGroup pgParent) protected bool CanRecycleGroup(PwGroup pgParent)
{ {
bool bShiftPressed = false;
PwDatabase pd = Db.KpDatabase; PwDatabase pd = Db.KpDatabase;
PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true); PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
bool bPermanent = false; bool bPermanent = false;
if (pgParent != null) if (pgParent != null)
{ {
if (pd.RecycleBinEnabled == false) if (pd.RecycleBinEnabled == false)
{
Android.Util.Log.Debug("KP2A", "CanRecycle? No, RecycleBinIsNotEnabled");
bPermanent = true; bPermanent = true;
else if (bShiftPressed) }
bPermanent = true;
else if (pgRecycleBin == null) else if (pgRecycleBin == null)
{ {
} // Recycle } // Recycle
else if (pgParent == pgRecycleBin) else if (pgParent == pgRecycleBin)
{
Android.Util.Log.Debug("KP2A", "CanRecycle? No, Can't recycle RecycleBin");
bPermanent = true; bPermanent = true;
}
else if (pgParent.IsContainedIn(pgRecycleBin)) else if (pgParent.IsContainedIn(pgRecycleBin))
{
Android.Util.Log.Debug("KP2A", "CanRecycle? No, "+pgParent.Name+" is in RecycleBin");
bPermanent = true; bPermanent = true;
} }
}
return !bPermanent; return !bPermanent;
} }
@ -112,7 +124,8 @@ namespace keepass2android
ProgressTask pt = new ProgressTask(App, Ctx, this); ProgressTask pt = new ProgressTask(App, Ctx, this);
pt.Run(); pt.Run();
}, },
(dlgSender, dlgEvt) => { (dlgSender, dlgEvt) =>
{
DeletePermanently = false; DeletePermanently = false;
ProgressTask pt = new ProgressTask(App, Ctx, this); ProgressTask pt = new ProgressTask(App, Ctx, this);
pt.Run(); pt.Run();
@ -122,13 +135,131 @@ namespace keepass2android
} else }
else
{ {
ProgressTask pt = new ProgressTask(App, Ctx, this); ProgressTask pt = new ProgressTask(App, Ctx, this);
pt.Run(); 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 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; FindViewById(Resource.Id.entry_tags).Visibility = ViewStates.Gone;
} }

View File

@ -30,6 +30,7 @@ using Android.Content.PM;
namespace keepass2android 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")] [MetaData("android.app.default_searchable",Value="keepass2android.search.SearchResults")]
public class GroupActivity : GroupBaseActivity { public class GroupActivity : GroupBaseActivity {
@ -83,6 +84,10 @@ namespace keepass2android
get { return App.Kp2a.GetDb().CanWrite && ((this.Group.ParentGroup != null) || App.Kp2a.GetDb().DatabaseFormat.CanHaveEntriesInRootGroup); } 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)
{ {

View File

@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
*/ */
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Android.App; using Android.App;
@ -182,6 +183,7 @@ namespace keepass2android
private String strCachedGroupUuid = null; private String strCachedGroupUuid = null;
public String UuidGroup { public String UuidGroup {
get { get {
if (strCachedGroupUuid == null) { if (strCachedGroupUuid == null) {
@ -222,6 +224,11 @@ namespace keepass2android
get { return (BaseAdapter) FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment).ListAdapter; } get { return (BaseAdapter) FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment).ListAdapter; }
} }
public virtual bool IsSearchResult
{
get { return false; }
}
/*TODO /*TODO
* protected override void OnListItemClick(ListView l, View v, int position, long id) { * protected override void OnListItemClick(ListView l, View v, int position, long id) {
base.OnListItemClick(l, v, position, id); base.OnListItemClick(l, v, position, id);
@ -235,6 +242,8 @@ namespace keepass2android
protected override void OnCreate(Bundle savedInstanceState) { protected override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState); base.OnCreate(savedInstanceState);
Android.Util.Log.Debug("KP2A", "Creating GBA");
AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent); AppTask = AppTask.GetTaskInOnCreate(savedInstanceState, Intent);
// Likely the app has been killed exit the activity // 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.cancel_insert_element).Click += (sender, args) => StopMovingElements();
FindViewById(Resource.Id.insert_element).Click += (sender, args) => InsertElement(); FindViewById(Resource.Id.insert_element).Click += (sender, args) => InsertElements();
SetResult(KeePass.ExitNormal); SetResult(KeePass.ExitNormal);
@ -275,13 +284,15 @@ namespace keepass2android
} }
private void InsertElement() private void InsertElements()
{ {
MoveElementTask moveElementTask = (MoveElementTask)AppTask; MoveElementsTask moveElementsTask = (MoveElementsTask)AppTask;
IStructureItem elementToMove = App.Kp2a.GetDb().KpDatabase.RootGroup.FindObject(moveElementTask.Uuid, true, null); 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); var progressTask = new ProgressTask(App.Kp2a, this, moveElement);
progressTask.Run(); progressTask.Run();
@ -381,9 +392,9 @@ namespace keepass2android
return true; return true;
} }
} }
/*
public override bool OnCreateOptionsMenu(IMenu menu) { public override bool OnCreateOptionsMenu(IMenu menu) {
base.OnCreateOptionsMenu(menu); return base.OnCreateOptionsMenu(menu);
MenuInflater inflater = MenuInflater; MenuInflater inflater = MenuInflater;
inflater.Inflate(Resource.Menu.group, menu); inflater.Inflate(Resource.Menu.group, menu);
@ -404,9 +415,9 @@ namespace keepass2android
else else
item.SetVisible(true); item.SetVisible(true);
} }
//return true;
return true;
} }
*/
@ -613,10 +624,10 @@ namespace keepass2android
public bool IsBeingMoved(PwUuid uuid) public bool IsBeingMoved(PwUuid uuid)
{ {
MoveElementTask moveElementTask = AppTask as MoveElementTask; MoveElementsTask moveElementsTask = AppTask as MoveElementsTask;
if (moveElementTask != null) if (moveElementsTask != null)
{ {
if (moveElementTask.Uuid.Equals(uuid)) if (moveElementsTask.Uuids.Any(uuidMoved => uuidMoved.Equals(uuid)))
return true; return true;
} }
return false; return false;
@ -629,16 +640,16 @@ namespace keepass2android
} }
public void StartMovingElement() public void StartMovingElements()
{ {
ShowInsertElementButtons(); ShowInsertElementsButtons();
//TODO Required? GroupView.ListView.InvalidateViews(); //TODO Required? GroupView.ListView.InvalidateViews();
BaseAdapter adapter = (BaseAdapter)ListAdapter; BaseAdapter adapter = (BaseAdapter)ListAdapter;
adapter.NotifyDataSetChanged(); adapter.NotifyDataSetChanged();
} }
public void ShowInsertElementButtons() public void ShowInsertElementsButtons()
{ {
FindViewById(Resource.Id.fabCancelAddNew).Visibility = ViewStates.Gone; FindViewById(Resource.Id.fabCancelAddNew).Visibility = ViewStates.Gone;
FindViewById(Resource.Id.fabAddNewGroup).Visibility = ViewStates.Gone; FindViewById(Resource.Id.fabAddNewGroup).Visibility = ViewStates.Gone;
@ -649,15 +660,18 @@ namespace keepass2android
FindViewById(Resource.Id.divider2).Visibility = ViewStates.Visible; FindViewById(Resource.Id.divider2).Visibility = ViewStates.Visible;
} }
public void StopMovingElement() public void StopMovingElements()
{ {
try try
{ {
MoveElementTask moveElementTask = (MoveElementTask)AppTask; MoveElementsTask moveElementsTask = (MoveElementsTask)AppTask;
IStructureItem elementToMove = App.Kp2a.GetDb().KpDatabase.RootGroup.FindObject(moveElementTask.Uuid, true, null); foreach (var uuid in moveElementsTask.Uuids)
{
IStructureItem elementToMove = App.Kp2a.GetDb().KpDatabase.RootGroup.FindObject(uuid, true, null);
if (elementToMove.ParentGroup != Group) if (elementToMove.ParentGroup != Group)
App.Kp2a.GetDb().Dirty.Add(elementToMove.ParentGroup); App.Kp2a.GetDb().Dirty.Add(elementToMove.ParentGroup);
} }
}
catch (Exception e) catch (Exception e)
{ {
//don't crash if adding to dirty fails but log the exception: //don't crash if adding to dirty fails but log the exception:
@ -680,6 +694,8 @@ namespace keepass2android
public class GroupListFragment : ListFragment, AbsListView.IMultiChoiceModeListener public class GroupListFragment : ListFragment, AbsListView.IMultiChoiceModeListener
{ {
private ActionMode _mode;
public override void OnActivityCreated(Bundle savedInstanceState) public override void OnActivityCreated(Bundle savedInstanceState)
{ {
base.OnActivityCreated(savedInstanceState); base.OnActivityCreated(savedInstanceState);
@ -709,29 +725,53 @@ namespace keepass2android
public bool OnActionItemClicked(ActionMode mode, IMenuItem item) 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) switch (item.ItemId)
{ {
case Resource.Id.menu_delete: case Resource.Id.menu_delete:
/*Handler handler = new Handler(); Handler handler = new Handler();
DeleteEntry task = new DeleteEntry(Activity, App.Kp2a, _entry, DeleteMultipleItems task = new DeleteMultipleItems((GroupBaseActivity)Activity, App.Kp2a.GetDb(), checkedItems,
new GroupBaseActivity.RefreshTask(handler, ((GroupBaseActivity)Activity))); new GroupBaseActivity.RefreshTask(handler, ((GroupBaseActivity)Activity)), App.Kp2a);
task.Start();*/ task.Start();
Toast.MakeText(((GroupBaseActivity) Activity), "todo delete", ToastLength.Long).Show(); break;
return true;
case Resource.Id.menu_move: case Resource.Id.menu_move:
/*NavigateToFolderAndLaunchMoveElementTask navMove = var navMove = new NavigateToFolderAndLaunchMoveElementTask(checkedItems.First().ParentGroup, checkedItems.Select(i => i.Uuid).ToList(), ((GroupBaseActivity)Activity).IsSearchResult);
new NavigateToFolderAndLaunchMoveElementTask(_entry.ParentGroup, _entry.Uuid, _isSearchResult); ((GroupBaseActivity)Activity).StartTask(navMove);
((GroupBaseActivity)Activity).StartTask(navMove);*/ break;
Toast.MakeText(((GroupBaseActivity)Activity), "todo move", ToastLength.Long).Show();
return true;
/*TODO for search results case Resource.Id.menu_navigate: /*TODO for search results case Resource.Id.menu_navigate:
NavigateToFolder navNavigate = new NavigateToFolder(_entry.ParentGroup, true); NavigateToFolder navNavigate = new NavigateToFolder(_entry.ParentGroup, true);
((GroupBaseActivity)Activity).StartTask(navNavigate); ((GroupBaseActivity)Activity).StartTask(navNavigate);
return true;*/ 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) public bool OnCreateActionMode(ActionMode mode, IMenu menu)
@ -741,7 +781,7 @@ namespace keepass2android
//mode.Title = "Select Items"; //mode.Title = "Select Items";
Android.Util.Log.Debug("KP2A", "Create action mode" + mode); Android.Util.Log.Debug("KP2A", "Create action mode" + mode);
((PwGroupListAdapter)ListView.Adapter).NotifyDataSetChanged(); ((PwGroupListAdapter)ListView.Adapter).NotifyDataSetChanged();
_mode = mode;
return true; return true;
} }
@ -750,6 +790,7 @@ namespace keepass2android
Android.Util.Log.Debug("KP2A", "Destroy action mode" + mode); Android.Util.Log.Debug("KP2A", "Destroy action mode" + mode);
((PwGroupListAdapter)ListView.Adapter).NotifyDataSetChanged(); ((PwGroupListAdapter)ListView.Adapter).NotifyDataSetChanged();
_mode = null;
} }
public bool OnPrepareActionMode(ActionMode mode, IMenu menu) public bool OnPrepareActionMode(ActionMode mode, IMenu menu)

View File

@ -24,6 +24,7 @@ using Android.Widget;
using Android.Preferences; using Android.Preferences;
using KeePassLib; using KeePassLib;
using keepass2android.view; using keepass2android.view;
using KeePassLib.Interfaces;
namespace keepass2android namespace keepass2android
{ {
@ -328,6 +329,17 @@ namespace keepass2android
return ev; 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 != "") if (App.Kp2a.GetDb().KpDatabase.Name != "")
{ {
FindViewById(Resource.Id.filename_label).Visibility = ViewStates.Invisible; FindViewById(Resource.Id.filename_label).Visibility = ViewStates.Visible;
((TextView) FindViewById(Resource.Id.qu_filename)).Text = App.Kp2a.GetDb().KpDatabase.Name; ((TextView) FindViewById(Resource.Id.filename_label)).Text = App.Kp2a.GetDb().KpDatabase.Name;
} }
else else
{ {
@ -78,11 +78,11 @@ namespace keepass2android
.GetBoolean(GetString(Resource.String.RememberRecentFiles_key), .GetBoolean(GetString(Resource.String.RememberRecentFiles_key),
Resources.GetBoolean(Resource.Boolean.RememberRecentFiles_default))) 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 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" android:src="@drawable/toolbar_bg_quickunlock"
app:layout_collapseMode="parallax" /> app:layout_collapseMode="parallax" />
<TextView <TextView
android:id="@+id/qu_filename" android:id="@+id/filename_label"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
@ -114,7 +114,7 @@ android:paddingRight="16dp"
android:text="@string/QuickUnlock_label" android:text="@string/QuickUnlock_label"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/qu_filename" android:layout_below="@id/filename_label"
android:textSize="14sp" android:textSize="14sp"
/> />

View File

@ -20,5 +20,7 @@
<TextView android:id="@+id/file_filename" xmlns:android="http://schemas.android.com/apk/res/android" <TextView android:id="@+id/file_filename" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" 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_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:text="Insert here" android:text="@string/insert_element_here"
style="@style/BottomBarButton" /> style="@style/BottomBarButton" />
<Button <Button
android:id="@+id/cancel_insert_element" android:id="@+id/cancel_insert_element"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:text="Cancel" android:text="@string/cancel"
style="@style/BottomBarButton" /> style="@style/BottomBarButton" />
</RelativeLayout> </RelativeLayout>
<View <View
@ -47,6 +47,8 @@
android:id="@+id/main_content" android:id="@+id/main_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_above="@id/divider2"
android:layout_below="@id/top"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true">
<fragment <fragment
android:name="keepass2android.GroupListFragment" android:name="keepass2android.GroupListFragment"

View File

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

View File

@ -9,7 +9,7 @@
<string name="accept">Accept</string> <string name="accept">Accept</string>
<string name="deny">Deny</string> <string name="deny">Deny</string>
<string name="add_entry">Add entry</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_url_entry">Create entry for URL</string>
<string name="add_group">Add group</string> <string name="add_group">Add group</string>
<string name="add_group_title">Add Group</string> <string name="add_group_title">Add Group</string>
@ -348,6 +348,7 @@
<string name="RecycleBin">Recycle Bin</string> <string name="RecycleBin">Recycle Bin</string>
<string name="AskDeletePermanentlyEntry">Do you want to delete this entry permanently? Press No to recycle.</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="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="AskDeletePermanently_title">Delete permanently?</string>
<string name="AskReloadFile_title">Reload file?</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> <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="AddingGroup">Adding group…</string>
<string name="DeletingEntry">Deleting entry…</string> <string name="DeletingEntry">Deleting entry…</string>
<string name="DeletingGroup">Deleting group…</string> <string name="DeletingGroup">Deleting group…</string>
<string name="DeletingItems">Deleting elements…</string>
<string name="SettingPassword">Setting password…</string> <string name="SettingPassword">Setting password…</string>
<string name="UndoingChanges">Undoing changes…</string> <string name="UndoingChanges">Undoing changes…</string>
<string name="TransformingKey">Transforming master key…</string> <string name="TransformingKey">Transforming master key…</string>

View File

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

View File

@ -5,6 +5,7 @@ using Android.Content;
using Android.OS; using Android.OS;
using Android.Widget; using Android.Widget;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using KeePassLib; using KeePassLib;
using KeePassLib.Security; using KeePassLib.Security;
using KeePassLib.Utility; using KeePassLib.Utility;
@ -546,13 +547,13 @@ namespace keepass2android
/// <summary> /// <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> /// </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; get;
set; set;
@ -560,23 +561,33 @@ namespace keepass2android
public override void Setup(Bundle b) public override void Setup(Bundle b)
{ {
Uuid = new PwUuid(MemUtil.HexStringToByteArray(b.GetString(UuidKey))); 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 public override IEnumerable<IExtra> Extras
{ {
get get
{ {
yield return new StringExtra { Key = UuidKey, Value = MemUtil.ByteArrayToHexString(Uuid.UuidBytes) }; 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) public override void StartInGroupActivity(GroupBaseActivity groupBaseActivity)
{ {
base.StartInGroupActivity(groupBaseActivity); base.StartInGroupActivity(groupBaseActivity);
groupBaseActivity.StartMovingElement(); groupBaseActivity.StartMovingElements();
} }
public override void SetupGroupBaseActivityButtons(GroupBaseActivity groupBaseActivity) public override void SetupGroupBaseActivityButtons(GroupBaseActivity groupBaseActivity)
{ {
groupBaseActivity.ShowInsertElementButtons(); groupBaseActivity.ShowInsertElementsButtons();
} }
} }
@ -902,19 +913,20 @@ namespace keepass2android
public class NavigateToFolderAndLaunchMoveElementTask: NavigateAndLaunchTask { public class NavigateToFolderAndLaunchMoveElementTask: NavigateAndLaunchTask {
public NavigateToFolderAndLaunchMoveElementTask():base(){ public NavigateToFolderAndLaunchMoveElementTask()
{
} }
public NavigateToFolderAndLaunchMoveElementTask(PwGroup groups, PwUuid uuid, bool toastEnable = false) public NavigateToFolderAndLaunchMoveElementTask(PwGroup groups, List<PwUuid> uuids, bool toastEnable = false)
:base(groups, new MoveElementTask() { Uuid = uuid }, toastEnable) { :base(groups, new MoveElementsTask() { Uuids = uuids }, toastEnable) {
} }
public override void Setup(Bundle b) { public override void Setup(Bundle b) {
base.Setup(b); base.Setup(b);
TaskToBeLaunchedAfterNavigation = new MoveElementTask (); TaskToBeLaunchedAfterNavigation = new MoveElementsTask ();
TaskToBeLaunchedAfterNavigation.Setup (b); TaskToBeLaunchedAfterNavigation.Setup (b);
} }

View File

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

View File

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

View File

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