entries can be moved to other groups

This commit is contained in:
Philipp Crocoll 2013-08-28 14:00:54 +02:00
parent 964d0ea512
commit e1de3e2cbf
12 changed files with 660 additions and 389 deletions

View File

@ -52,6 +52,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="database\CheckDatabaseForChanges.cs" /> <Compile Include="database\CheckDatabaseForChanges.cs" />
<Compile Include="database\edit\MoveElement.cs" />
<Compile Include="database\SynchronizeCachedDatabase.cs" /> <Compile Include="database\SynchronizeCachedDatabase.cs" />
<Compile Include="Io\BuiltInFileStorage.cs" /> <Compile Include="Io\BuiltInFileStorage.cs" />
<Compile Include="Io\CachingFileStorage.cs" /> <Compile Include="Io\CachingFileStorage.cs" />

View File

@ -0,0 +1,65 @@
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;
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

@ -35,8 +35,8 @@ namespace keepass2android
public const int Uninit = -1; public const int Uninit = -1;
protected bool AddGroupEnabled = false; protected bool AddGroupEnabled = true;
protected bool AddEntryEnabled = false; protected bool AddEntryEnabled = true;
private const String Tag = "Group Activity:"; private const String Tag = "Group Activity:";
@ -74,13 +74,15 @@ namespace keepass2android
} }
return new PwUuid(MemUtil.HexStringToByteArray(uuid)); return new PwUuid(MemUtil.HexStringToByteArray(uuid));
} }
protected void SetupButtons() public override void SetupNormalButtons()
{ {
AddGroupEnabled = true; GroupView.SetNormalButtonVisibility(AddGroupEnabled, AddEntryEnabled);
AddEntryEnabled = true; GroupView.Invalidate();
} }
protected override void OnCreate (Bundle savedInstanceState) protected override void OnCreate (Bundle savedInstanceState)
{ {
base.OnCreate (savedInstanceState); base.OnCreate (savedInstanceState);
@ -109,12 +111,6 @@ namespace keepass2android
return; return;
} }
SetupButtons ();
GroupView groupView = new GroupView(this);
SetContentView (groupView);
groupView.SetNormalButtonVisibility(AddGroupEnabled, AddEntryEnabled);
if (AddGroupEnabled) { if (AddGroupEnabled) {
// Add Group button // Add Group button

View File

@ -24,7 +24,10 @@ using Android.Views;
using Android.Widget; using Android.Widget;
using KeePassLib; using KeePassLib;
using Android.Preferences; using Android.Preferences;
using KeePassLib.Interfaces;
using KeePassLib.Utility;
using keepass2android.Io; using keepass2android.Io;
using keepass2android.database.edit;
using keepass2android.view; using keepass2android.view;
using Android.Graphics.Drawables; using Android.Graphics.Drawables;
@ -57,14 +60,20 @@ namespace keepass2android
AppTask.ToBundle(outState); AppTask.ToBundle(outState);
} }
public virtual void SetupNormalButtons()
{
GroupView.SetNormalButtonVisibility(true, true);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{ {
base.OnActivityResult(requestCode, resultCode, data); base.OnActivityResult(requestCode, resultCode, data);
AppTask.TryGetFromActivityResult(data, ref AppTask);
if (resultCode == KeePass.ExitCloseAfterTaskComplete) if (resultCode == KeePass.ExitCloseAfterTaskComplete)
{ {
SetResult(KeePass.ExitCloseAfterTaskComplete); AppTask.SetActivityResult(this, KeePass.ExitCloseAfterTaskComplete);
Finish(); Finish();
} }
@ -75,10 +84,12 @@ namespace keepass2android
protected PwGroup Group; protected PwGroup Group;
internal AppTask AppTask; internal AppTask AppTask;
private GroupView _groupView; protected GroupView GroupView;
protected override void OnResume() { protected override void OnResume() {
base.OnResume(); base.OnResume();
AppTask.SetupGroupBaseActivityButtons(this);
RefreshIfDirty(); RefreshIfDirty();
} }
@ -121,15 +132,36 @@ namespace keepass2android
_prefs = PreferenceManager.GetDefaultSharedPreferences(this); _prefs = PreferenceManager.GetDefaultSharedPreferences(this);
_groupView = new GroupView(this); GroupView = new GroupView(this);
SetContentView(_groupView); SetContentView(GroupView);
_groupView.SetNormalButtonVisibility(false, false);
FindViewById(Resource.Id.cancel_insert_element).Click += (sender, args) => StopMovingElement();
FindViewById(Resource.Id.insert_element).Click += (sender, args) => InsertElement();
SetResult(KeePass.ExitNormal); SetResult(KeePass.ExitNormal);
StyleScrollBars(); StyleScrollBars();
} }
protected override void OnStart()
{
base.OnStart();
AppTask.StartInGroupActivity(this);
}
private void InsertElement()
{
MoveElementTask moveElementTask = (MoveElementTask)AppTask;
IStructureItem elementToMove = App.Kp2a.GetDb().KpDatabase.RootGroup.FindObject(moveElementTask.Uuid, true, null);
var moveElement = new MoveElement(elementToMove, Group, this, App.Kp2a, new ActionOnFinish((success, message) => { StopMovingElement(); }));
var progressTask = new ProgressTask(App.Kp2a, this, moveElement);
progressTask.Run();
}
protected void StyleScrollBars() { protected void StyleScrollBars() {
ListView lv = ListView; ListView lv = ListView;
lv.ScrollBarStyle =ScrollbarStyles.InsideInset; lv.ScrollBarStyle =ScrollbarStyles.InsideInset;
@ -300,6 +332,7 @@ namespace keepass2android
//Currently the action bar only displays the home button when we come from a previous activity. //Currently the action bar only displays the home button when we come from a previous activity.
//So we can simply Finish. See this page for information on how to do this in more general (future?) cases: //So we can simply Finish. See this page for information on how to do this in more general (future?) cases:
//http://developer.android.com/training/implementing-navigation/ancestral.html //http://developer.android.com/training/implementing-navigation/ancestral.html
AppTask.SetActivityResult(this, KeePass.ExitNormal);
Finish(); Finish();
OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back); OverridePendingTransition(Resource.Animation.anim_enter_back, Resource.Animation.anim_leave_back);
@ -333,6 +366,12 @@ namespace keepass2android
} }
public override void OnBackPressed()
{
AppTask.SetActivityResult(this, KeePass.ExitNormal);
base.OnBackPressed();
}
private void ToggleSort() { private void ToggleSort() {
// Toggle setting // Toggle setting
String sortKey = GetString(Resource.String.sort_key); String sortKey = GetString(Resource.String.sort_key);
@ -397,6 +436,60 @@ namespace keepass2android
} }
public bool IsBeingMoved(PwUuid uuid)
{
MoveElementTask moveElementTask = AppTask as MoveElementTask;
if (moveElementTask != null)
{
if (moveElementTask.Uuid.EqualsValue(uuid))
return true;
}
return false;
}
public void StartTask(AppTask task)
{
AppTask = task;
task.StartInGroupActivity(this);
}
public void StartMovingElement()
{
ShowInsertElementButtons();
GroupView.ListView.InvalidateViews();
BaseAdapter adapter = (BaseAdapter)ListAdapter;
adapter.NotifyDataSetChanged();
}
public void ShowInsertElementButtons()
{
GroupView.ShowInsertButtons();
}
public void StopMovingElement()
{
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);
}
catch (Exception e)
{
//don't crash if adding to dirty fails but log the exception:
Kp2aLog.Log(e.ToString());
}
AppTask = new NullTask();
AppTask.SetupGroupBaseActivityButtons(this);
GroupView.ListView.InvalidateViews();
BaseAdapter adapter = (BaseAdapter)ListAdapter;
adapter.NotifyDataSetChanged();
}
} }
} }

View File

@ -39,6 +39,7 @@ namespace keepass2android
public const Result ExitRefresh = Result.FirstUser+2; public const Result ExitRefresh = Result.FirstUser+2;
public const Result ExitRefreshTitle = Result.FirstUser+3; public const Result ExitRefreshTitle = Result.FirstUser+3;
public const Result ExitCloseAfterTaskComplete = Result.FirstUser+4; public const Result ExitCloseAfterTaskComplete = Result.FirstUser+4;
public const Result TaskComplete = Result.FirstUser + 5;
public const Result ExitReloadDb = Result.FirstUser+6; public const Result ExitReloadDb = Result.FirstUser+6;
AppTask _appTask; AppTask _appTask;

File diff suppressed because it is too large Load Diff

View File

@ -51,6 +51,40 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:text="@string/add_entry" /> android:text="@string/add_entry" />
</FrameLayout> </FrameLayout>
<FrameLayout
android:id="@+id/insert_element"
style="@style/BottomBarActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
style="?android:actionBarTabTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingRight="20dp"
android:drawableLeft="@drawable/btn_new_group"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:text="@string/insert_element_here" />
</FrameLayout>
<FrameLayout
android:id="@+id/cancel_insert_element"
style="@style/BottomBarActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
style="?android:actionBarTabTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingRight="20dp"
android:drawableLeft="@android:drawable/ic_menu_close_clear_cancel"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:text="@string/cancel" />
</FrameLayout>
</LinearLayout> </LinearLayout>
<View <View
android:id="@+id/divider2" android:id="@+id/divider2"

View File

@ -121,6 +121,7 @@
<string name="menu_app_settings">Settings</string> <string name="menu_app_settings">Settings</string>
<string name="menu_db_settings">Database settings</string> <string name="menu_db_settings">Database settings</string>
<string name="menu_delete">Delete</string> <string name="menu_delete">Delete</string>
<string name="menu_move">Move to another group</string>
<string name="menu_donate">Donate a beer...</string> <string name="menu_donate">Donate a beer...</string>
<string name="menu_edit">Edit</string> <string name="menu_edit">Edit</string>
<string name="menu_hide_password">Hide Password</string> <string name="menu_hide_password">Hide Password</string>

View File

@ -15,6 +15,7 @@
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
using System; using System;
using Android.App;
using Android.Content; using Android.Content;
using Android.OS; using Android.OS;
using System.Collections.Generic; using System.Collections.Generic;
@ -135,20 +136,25 @@ namespace keepass2android
} }
public static AppTask CreateFromBundle(Bundle b) public static AppTask CreateFromBundle(Bundle b)
{
return CreateFromBundle(b, new NullTask());
}
public static AppTask CreateFromBundle(Bundle b, AppTask failureReturn)
{ {
if (b == null) if (b == null)
return new NullTask(); return failureReturn;
string taskType = b.GetString(AppTaskKey); string taskType = b.GetString(AppTaskKey);
if (string.IsNullOrEmpty(taskType)) if (string.IsNullOrEmpty(taskType))
return new NullTask(); return failureReturn;
try try
{ {
Type type = Type.GetType("keepass2android." + taskType); Type type = Type.GetType("keepass2android." + taskType);
if (type == null) if (type == null)
return new NullTask(); return failureReturn;
AppTask task = (AppTask)Activator.CreateInstance(type); AppTask task = (AppTask)Activator.CreateInstance(type);
task.Setup(b); task.Setup(b);
return task; return task;
@ -156,7 +162,7 @@ namespace keepass2android
catch (Exception e) catch (Exception e)
{ {
Kp2aLog.Log("Cannot convert " + taskType + " in task: " + e); Kp2aLog.Log("Cannot convert " + taskType + " in task: " + e);
return new NullTask(); return failureReturn;
} }
} }
@ -196,6 +202,42 @@ namespace keepass2android
return new StringExtra { Key=AppTaskKey, Value=type.Name}; return new StringExtra { Key=AppTaskKey, Value=type.Name};
} }
public virtual void StartInGroupActivity(GroupBaseActivity groupBaseActivity)
{
return;
}
public virtual void SetupGroupBaseActivityButtons(GroupBaseActivity groupBaseActivity)
{
groupBaseActivity.SetupNormalButtons();
}
public void SetActivityResult(Activity activity, Result result)
{
Intent data = new Intent();
ToIntent(data);
activity.SetResult(result, data);
}
/// <summary>
/// Tries to extract the task from the data given as an Intent object in OnActivityResult. If successful, the task is assigned,
/// otherwise, false is returned.
/// </summary>
public static bool TryGetFromActivityResult(Intent data, ref AppTask task)
{
if (data == null)
return false;
AppTask tempTask = CreateFromBundle(data.Extras, null);
if (tempTask == null)
{
Kp2aLog.Log("No AppTask in OnActivityResult");
return false;
}
task = tempTask;
Kp2aLog.Log("AppTask " +task+" in OnActivityResult");
return true;
}
} }
/// <summary> /// <summary>
@ -278,6 +320,15 @@ namespace keepass2android
yield return new StringExtra { Key = UuidKey, Value = MemUtil.ByteArrayToHexString(Uuid.UuidBytes) }; yield return new StringExtra { Key = UuidKey, Value = MemUtil.ByteArrayToHexString(Uuid.UuidBytes) };
} }
} }
public override void StartInGroupActivity(GroupBaseActivity groupBaseActivity)
{
base.StartInGroupActivity(groupBaseActivity);
groupBaseActivity.StartMovingElement();
}
public override void SetupGroupBaseActivityButtons(GroupBaseActivity groupBaseActivity)
{
groupBaseActivity.ShowInsertElementButtons();
}
} }

View File

@ -88,17 +88,9 @@ namespace keepass2android.search
return; return;
} }
if ( Group == null || (!Group.Entries.Any()) ) { if ( Group == null || (!Group.Entries.Any()) ) {
SetContentView(new GroupEmptyView(this)); SetContentView(new GroupEmptyView(this));
} else }
{
_groupView = new GroupView(this);
SetContentView(_groupView);
_groupView.SetNormalButtonVisibility(false, false);
}
SetGroupTitle(); SetGroupTitle();

View File

@ -39,7 +39,12 @@ namespace keepass2android.view
public GroupView(Context context, IAttributeSet attrs): base(context, attrs) { public GroupView(Context context, IAttributeSet attrs): base(context, attrs) {
Inflate(context); Inflate(context);
} }
public ListView ListView
{
get { return (ListView) FindViewById(Android.Resource.Id.List); }
}
private void Inflate(Context context) { private void Inflate(Context context) {
LayoutInflater inflater = (LayoutInflater) context.GetSystemService(Context.LayoutInflaterService); LayoutInflater inflater = (LayoutInflater) context.GetSystemService(Context.LayoutInflaterService);
inflater.Inflate(Resource.Layout.group_add_entry, this); inflater.Inflate(Resource.Layout.group_add_entry, this);
@ -48,24 +53,27 @@ namespace keepass2android.view
} }
public void SetNormalButtonVisibility(bool showAddGroup, bool showAddEntry) public void SetNormalButtonVisibility(bool showAddGroup, bool showAddEntry)
{ {
if (!showAddGroup)
{ View insertElement = FindViewById(Resource.Id.insert_element);
View addGroup = FindViewById(Resource.Id.add_group); insertElement.Visibility = ViewStates.Gone;
addGroup.Visibility = ViewStates.Invisible;
} View insertElementCancel = FindViewById(Resource.Id.cancel_insert_element);
insertElementCancel.Visibility = ViewStates.Gone;
View addGroup = FindViewById(Resource.Id.add_group);
addGroup.Visibility = showAddGroup? ViewStates.Visible : ViewStates.Gone;
View addEntry = FindViewById(Resource.Id.add_entry);
addEntry.Visibility = showAddEntry ? ViewStates.Visible : ViewStates.Gone;
if (!showAddEntry)
{
View addEntry = FindViewById(Resource.Id.add_entry);
addEntry.Visibility = ViewStates.Invisible;
}
if (!showAddEntry && !showAddGroup) if (!showAddEntry && !showAddGroup)
{ {
View divider2 = FindViewById(Resource.Id.divider2); View divider2 = FindViewById(Resource.Id.divider2);
divider2.Visibility = ViewStates.Invisible; divider2.Visibility = ViewStates.Gone;
FindViewById(Resource.Id.bottom_bar).Visibility = ViewStates.Invisible; FindViewById(Resource.Id.bottom_bar).Visibility = ViewStates.Gone;
View list = FindViewById(Android.Resource.Id.List); View list = FindViewById(Android.Resource.Id.List);
LayoutParams lp = (RelativeLayout.LayoutParams) list.LayoutParameters; LayoutParams lp = (RelativeLayout.LayoutParams) list.LayoutParameters;
@ -76,7 +84,21 @@ namespace keepass2android.view
public void ShowInsertButtons() public void ShowInsertButtons()
{ {
View addGroup = FindViewById(Resource.Id.add_group);
addGroup.Visibility = ViewStates.Gone;
View addEntry = FindViewById(Resource.Id.add_entry);
addEntry.Visibility = ViewStates.Gone;
View insertElement = FindViewById(Resource.Id.insert_element);
insertElement.Visibility = ViewStates.Visible;
View insertElementCancel = FindViewById(Resource.Id.cancel_insert_element);
insertElementCancel.Visibility = ViewStates.Visible;
View divider2 = FindViewById(Resource.Id.divider2);
divider2.Visibility = ViewStates.Visible;
} }

View File

@ -15,6 +15,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>. along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/ */
using System; using System;
using Android.Graphics;
using Android.OS; using Android.OS;
using Android.Runtime; using Android.Runtime;
using Android.Views; using Android.Views;
@ -38,6 +39,7 @@ namespace keepass2android.view
private const int MenuOpen = Menu.First; private const int MenuOpen = Menu.First;
private const int MenuDelete = MenuOpen + 1; private const int MenuDelete = MenuOpen + 1;
private const int MenuMove = MenuDelete + 1;
public static PwEntryView GetInstance(GroupBaseActivity act, PwEntry pw, int pos) public static PwEntryView GetInstance(GroupBaseActivity act, PwEntry pw, int pos)
{ {
@ -96,6 +98,12 @@ namespace keepass2android.view
} }
_textView.TextFormatted = str; _textView.TextFormatted = str;
//todo: get colors from resources
if (_groupActivity.IsBeingMoved(_entry.Uuid))
_textView.SetTextColor(new Color(180,180,180));
else
_textView.SetTextColor(new Color(0,0,0));
String detail = pw.Strings.ReadSafe(PwDefs.UserNameField); String detail = pw.Strings.ReadSafe(PwDefs.UserNameField);
@ -137,6 +145,7 @@ namespace keepass2android.view
{ {
menu.Add(0, MenuOpen, 0, Resource.String.menu_open); menu.Add(0, MenuOpen, 0, Resource.String.menu_open);
menu.Add(0, MenuDelete, 0, Resource.String.menu_delete); menu.Add(0, MenuDelete, 0, Resource.String.menu_delete);
menu.Add(0, MenuMove, 0, Resource.String.menu_move);
} }
public override bool OnContextItemSelected(IMenuItem item) public override bool OnContextItemSelected(IMenuItem item)
@ -152,6 +161,9 @@ namespace keepass2android.view
DeleteEntry task = new DeleteEntry(Context, App.Kp2a, _entry, new GroupBaseActivity.RefreshTask(handler, _groupActivity)); DeleteEntry task = new DeleteEntry(Context, App.Kp2a, _entry, new GroupBaseActivity.RefreshTask(handler, _groupActivity));
task.Start(); task.Start();
return true; return true;
case MenuMove:
_groupActivity.StartTask(new MoveElementTask { Uuid = _entry.Uuid});
return true;
default: default:
return false; return false;