From 5d8aa8afe098c428b7014185d326cbfe2dc18fae Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Thu, 7 Jan 2016 05:13:04 +0100 Subject: [PATCH] Added support for Entry templates --- src/keepass2android/EntryActivity.cs | 15 +- src/keepass2android/EntryEditActivity.cs | 197 +++++++++++++----- src/keepass2android/EntryEditActivityState.cs | 10 + src/keepass2android/GroupActivity.cs | 105 +++++++++- src/keepass2android/IconPickerActivity.cs | 28 +-- src/keepass2android/KeePass.cs | 5 + src/keepass2android/KpEntryTemplatedEdit.cs | 151 ++++++++++++++ .../Resources/layout/entry_edit.xml | 21 +- .../Resources/menu/entry_edit.xml | 4 + .../Resources/values/strings.xml | 7 +- src/keepass2android/Utils/Util.cs | 31 +++ src/keepass2android/keepass2android.csproj | 1 + 12 files changed, 477 insertions(+), 98 deletions(-) create mode 100644 src/keepass2android/KpEntryTemplatedEdit.cs diff --git a/src/keepass2android/EntryActivity.cs b/src/keepass2android/EntryActivity.cs index a1a1f23f..f2574a3c 100644 --- a/src/keepass2android/EntryActivity.cs +++ b/src/keepass2android/EntryActivity.cs @@ -438,11 +438,18 @@ namespace keepass2android { ViewGroup extraGroup = (ViewGroup) FindViewById(Resource.Id.extra_strings); bool hasExtras = false; - foreach (var pair in Entry.Strings.Where(pair => !PwDefs.IsStandardField(pair.Key)).OrderBy(pair => pair.Key)) + IEditMode editMode = new DefaultEdit(); + if (KpEntryTemplatedEdit.IsTemplated(App.Kp2a.GetDb(), this.Entry)) + editMode = new KpEntryTemplatedEdit(App.Kp2a.GetDb(), this.Entry); + foreach (var key in editMode.SortExtraFieldKeys(Entry.Strings.GetKeys().Where(key=> !PwDefs.IsStandardField(key)))) { - hasExtras = true; - var stringView = CreateExtraSection(pair.Key, pair.Value.ReadString(), pair.Value.IsProtected); - extraGroup.AddView(stringView.View); + if (editMode.IsVisible(key)) + { + hasExtras = true; + var value = Entry.Strings.Get(key); + var stringView = CreateExtraSection(key, value.ReadString(), value.IsProtected); + extraGroup.AddView(stringView.View); + } } FindViewById(Resource.Id.extra_strings_container).Visibility = hasExtras ? ViewStates.Visible : ViewStates.Gone; } diff --git a/src/keepass2android/EntryEditActivity.cs b/src/keepass2android/EntryEditActivity.cs index e8d55a12..cb2e3cf2 100644 --- a/src/keepass2android/EntryEditActivity.cs +++ b/src/keepass2android/EntryEditActivity.cs @@ -33,16 +33,23 @@ using KeePassLib.Security; using Android.Content.PM; using System.IO; using System.Globalization; +using Android.Graphics; using Android.Util; +using Debug = System.Diagnostics.Debug; using File = System.IO.File; +using Object = Java.Lang.Object; using Uri = Android.Net.Uri; 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")] public class EntryEditActivity : LockCloseActivity { + + + public const String KeyEntry = "entry"; public const String KeyParent = "parent"; + public const String KeyTemplateUuid = "KeyTemplateUuid"; public const int ResultOkIconPicker = (int)Result.FirstUser + 1000; @@ -64,11 +71,12 @@ namespace keepass2android act.StartActivityForResult(i, 0); } - public static void Launch(Activity act, PwGroup pw, AppTask appTask) { + public static void Launch(Activity act, PwGroup pw, PwUuid templateUuid, AppTask appTask) { Intent i = new Intent(act, typeof(EntryEditActivity)); PwGroup parent = pw; i.PutExtra(KeyParent, parent.Uuid.ToHexString()); + i.PutExtra(KeyTemplateUuid, templateUuid.ToHexString()); appTask.ToIntent(i); @@ -127,62 +135,39 @@ namespace keepass2android State.ParentGroup = null; if (entryId.Equals(PwUuid.Zero)) { + //creating new entry String groupId = i.GetStringExtra(KeyParent); State.ParentGroup = db.KpDatabase.RootGroup.FindGroup(new PwUuid(MemUtil.HexStringToByteArray(groupId)), true); - State.EntryInDatabase = new PwEntry(true, true); - State.EntryInDatabase.Strings.Set(PwDefs.UserNameField, new ProtectedString( - db.KpDatabase.MemoryProtection.ProtectUserName, db.KpDatabase.DefaultUserName)); - - /*KPDesktop - * ProtectedString psAutoGen; - PwGenerator.Generate(out psAutoGen, Program.Config.PasswordGenerator.AutoGeneratedPasswordsProfile, - null, Program.PwGeneratorPool); - psAutoGen = psAutoGen.WithProtection(pwDb.MemoryProtection.ProtectPassword); - pwe.Strings.Set(PwDefs.PasswordField, psAutoGen); - - int nExpireDays = Program.Config.Defaults.NewEntryExpiresInDays; - if(nExpireDays >= 0) - { - pwe.Expires = true; - pwe.ExpiryTime = DateTime.Now.AddDays(nExpireDays); - }*/ - - if ((State.ParentGroup.IconId != PwIcon.Folder) && (State.ParentGroup.IconId != PwIcon.FolderOpen) && - (State.ParentGroup.IconId != PwIcon.FolderPackage)) + PwUuid templateId = new PwUuid(MemUtil.HexStringToByteArray(i.GetStringExtra(KeyTemplateUuid))); + PwEntry templateEntry = null; + if (!PwUuid.Zero.Equals(templateId)) { - State.EntryInDatabase.IconId = State.ParentGroup.IconId; // Inherit icon from group + templateEntry = db.Entries[templateId]; + } + + if (KpEntryTemplatedEdit.IsTemplate(templateEntry)) + { + CreateNewFromKpEntryTemplate(db, templateEntry); + } + else if (templateEntry != null) + { + CreateNewFromStandardTemplate(templateEntry); } else - State.EntryInDatabase.IconId = PwIcon.Key; - State.EntryInDatabase.CustomIconUuid = State.ParentGroup.CustomIconUuid; - - /* - * KPDesktop - if(strDefaultSeq.Length == 0) - { - PwGroup pg = m_pwEntry.ParentGroup; - if(pg != null) - { - strDefaultSeq = pg.GetAutoTypeSequenceInherited(); - - if(strDefaultSeq.Length == 0) { - if(PwDefs.IsTanEntry(m_pwEntry)) - strDefaultSeq = PwDefs.DefaultAutoTypeSequenceTan; - else - strDefaultSeq = PwDefs.DefaultAutoTypeSequence; + CreateNewWithoutTemplate(db); } - } - }*/ + _appTask.PrepareNewEntry(State.EntryInDatabase); State.IsNew = true; State.EntryModified = true; - } else + } + else { - System.Diagnostics.Debug.Assert(entryId != null); + Debug.Assert(entryId != null); State.EntryInDatabase = db.Entries [entryId]; State.IsNew = false; @@ -191,6 +176,10 @@ namespace keepass2android } State.Entry = State.EntryInDatabase.CloneDeep(); + if (KpEntryTemplatedEdit.IsTemplated(db, State.Entry)) + State.EditMode = new KpEntryTemplatedEdit(db, State.Entry); + else + State.EditMode = new DefaultEdit(); } @@ -200,6 +189,7 @@ namespace keepass2android SetResult(KeePass.ExitRefreshTitle); + FillData(); View scrollView = FindViewById(Resource.Id.entry_scroll); scrollView.ScrollBarStyle = ScrollbarStyles.InsideInset; @@ -250,8 +240,8 @@ namespace keepass2android State.ShowPassword = !State.ShowPassword; MakePasswordVisibleOrHidden(); }; - Android.Graphics.PorterDuff.Mode mMode = Android.Graphics.PorterDuff.Mode.SrcAtop; - Android.Graphics.Color color = new Android.Graphics.Color (189,189,189); + PorterDuff.Mode mMode = PorterDuff.Mode.SrcAtop; + Color color = new Color (189,189,189); btnTogglePassword.SetColorFilter (color, mMode); @@ -288,6 +278,40 @@ namespace keepass2android } + private void CreateNewFromKpEntryTemplate(Database db, PwEntry templateEntry) + { + var entry = new PwEntry(true, true); + KpEntryTemplatedEdit.InitializeEntry(entry, templateEntry); + + + State.EntryInDatabase = entry; + } + + private void CreateNewFromStandardTemplate(PwEntry templateEntry) + { + var newEntry = templateEntry.CloneDeep(); + newEntry.SetUuid(new PwUuid(true), true); // Create new UUID + newEntry.CreationTime = newEntry.LastModificationTime = newEntry.LastAccessTime = DateTime.Now; + State.EntryInDatabase = newEntry; + } + + private void CreateNewWithoutTemplate(Database db) + { + State.EntryInDatabase = new PwEntry(true, true); + State.EntryInDatabase.Strings.Set(PwDefs.UserNameField, new ProtectedString( + db.KpDatabase.MemoryProtection.ProtectUserName, db.KpDatabase.DefaultUserName)); + + if ((State.ParentGroup.IconId != PwIcon.Folder) && (State.ParentGroup.IconId != PwIcon.FolderOpen) && + (State.ParentGroup.IconId != PwIcon.FolderPackage)) + { + State.EntryInDatabase.IconId = State.ParentGroup.IconId; // Inherit icon from group + } + else + State.EntryInDatabase.IconId = PwIcon.Key; + State.EntryInDatabase.CustomIconUuid = State.ParentGroup.CustomIconUuid; + + } + private void SetAddExtraStringEnabled() { if (!App.Kp2a.GetDb().DatabaseFormat.CanHaveCustomFields) @@ -785,6 +809,7 @@ namespace keepass2android public override bool OnPrepareOptionsMenu(IMenu menu) { Util.PrepareDonateOptionMenu(menu, this); + menu.FindItem(Resource.Id.menu_show_all).SetVisible(_editModeHiddenViews.Any()); return base.OnPrepareOptionsMenu(menu); } @@ -806,6 +831,11 @@ namespace keepass2android case Resource.Id.menu_cancel: Finish(); return true; + case Resource.Id.menu_show_all: + item.SetVisible(false); + foreach (View v in _editModeHiddenViews) + v.Visibility = ViewStates.Visible; + return true; case Android.Resource.Id.Home: OnBackPressed(); return true; @@ -849,11 +879,12 @@ namespace keepass2android ((CheckBox)ees.FindViewById(Resource.Id.protection)).Checked = pair.Value.IsProtected; //ees.FindViewById(Resource.Id.edit_extra).Click += (sender, e) => DeleteAdvancedString((View)sender); - ees.FindViewById(Resource.Id.edit_extra).Click += (sender, e) => EditAdvancedString(ees); + ees.FindViewById(Resource.Id.edit_extra).Click += (sender, e) => EditAdvancedString(ees.FindViewById(Resource.Id.edit_extra)); return ees; } private string[] _additionalKeys = null; + private List _editModeHiddenViews; public string[] AdditionalKeys { @@ -864,6 +895,7 @@ namespace keepass2android _additionalKeys = App.Kp2a.GetDb().Entries .Select(kvp => kvp.Value) .SelectMany(x => x.Strings.GetKeys().Where(k => !PwDefs.IsStandardField(k))) + .Where(k => (k != null) && !k.StartsWith("_etm_") ) .Distinct() .ToArray(); } @@ -934,7 +966,9 @@ namespace keepass2android } } - private void FillData() { + private void FillData() + { + _editModeHiddenViews = new List(); ImageButton currIconButton = (ImageButton) FindViewById(Resource.Id.icon_button); App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(currIconButton, this, App.Kp2a.GetDb().KpDatabase, State.Entry.IconId, State.Entry.CustomIconUuid, false); @@ -950,12 +984,15 @@ namespace keepass2android LinearLayout container = (LinearLayout) FindViewById(Resource.Id.advanced_container); - foreach (var pair in State.Entry.Strings) + foreach (var key in State.EditMode.SortExtraFieldKeys(State.Entry.Strings.Select(ps => ps.Key))) { - String key = pair.Key; - - if (!PwDefs.IsStandardField(key)) { - var ees = CreateExtraStringView(pair); + if (!PwDefs.IsStandardField(key)) + { + RelativeLayout ees = CreateExtraStringView(new KeyValuePair(key, State.Entry.Strings.Get(key))); + var isVisible = State.EditMode.IsVisible(key); + ees.Visibility = isVisible ? ViewStates.Visible : ViewStates.Gone; + if (!isVisible) + _editModeHiddenViews.Add(ees); container.AddView(ees); } } @@ -985,6 +1022,29 @@ namespace keepass2android UpdateExpires(); + + List> keyLayoutIds = new List>() + { + new KeyValuePair(PwDefs.TitleField, Resource.Id.title_section), + new KeyValuePair(PwDefs.UserNameField, Resource.Id.user_section), + new KeyValuePair(PwDefs.PasswordField, Resource.Id.password_section), + new KeyValuePair(PwDefs.UrlField, Resource.Id.url_section), + new KeyValuePair(PwDefs.NotesField, Resource.Id.comments_section), + new KeyValuePair(KeePass.TagsKey, Resource.Id.tags_section), + new KeyValuePair(KeePass.OverrideUrlKey, Resource.Id.entry_override_url_container), + new KeyValuePair(KeePass.ExpDateKey, Resource.Id.expires_section), + }; + foreach (var kvp in keyLayoutIds) + { + var isVisible = State.EditMode.IsVisible(kvp.Key); + var field = FindViewById(kvp.Value); + if (!isVisible) + _editModeHiddenViews.Add(field); + + field.Visibility = isVisible ? ViewStates.Visible : ViewStates.Gone; + } + + } private String getDateTime(DateTime dt) { return dt.ToString ("g", CultureInfo.CurrentUICulture); @@ -1052,7 +1112,7 @@ namespace keepass2android if (allKeys.Contains(key)) { - Toast.MakeText(this, GetString(Resource.String.error_string_duplicate_key, new Java.Lang.Object[]{key}), ToastLength.Long).Show(); + Toast.MakeText(this, GetString(Resource.String.error_string_duplicate_key, new Object[]{key}), ToastLength.Long).Show(); return false; } @@ -1083,9 +1143,34 @@ namespace keepass2android base.OnPause(); } - - } + public class DefaultEdit : IEditMode + { + public DefaultEdit() + { + + } + + public bool IsVisible(string fieldKey) + { + return true; + } + + public IEnumerable SortExtraFieldKeys(IEnumerable keys) + { + return keys; + } + + public bool ShowAddAttachments + { + get { throw new NotImplementedException(); } + } + + public bool ShowAddExtras + { + get { throw new NotImplementedException(); } + } + } } diff --git a/src/keepass2android/EntryEditActivityState.cs b/src/keepass2android/EntryEditActivityState.cs index 7678c943..21866841 100644 --- a/src/keepass2android/EntryEditActivityState.cs +++ b/src/keepass2android/EntryEditActivityState.cs @@ -1,7 +1,16 @@ +using System.Collections.Generic; using KeePassLib; namespace keepass2android { + interface IEditMode + { + bool IsVisible(string fieldKey); + + IEnumerable SortExtraFieldKeys(IEnumerable keys); + + } + /// /// Holds the state of the EntrryEditActivity. This is required to be able to keep a partially modified entry in memory /// through the App variable. Serializing this state (especially the Entry/EntryInDatabase) can be a performance problem @@ -20,6 +29,7 @@ namespace keepass2android internal bool EntryModified; + public IEditMode EditMode { get; set; } } } diff --git a/src/keepass2android/GroupActivity.cs b/src/keepass2android/GroupActivity.cs index e1cff226..f2a29368 100644 --- a/src/keepass2android/GroupActivity.cs +++ b/src/keepass2android/GroupActivity.cs @@ -16,6 +16,9 @@ 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; using Android.Content; using Android.OS; @@ -26,9 +29,15 @@ using Android.Util; using KeePassLib.Utility; using keepass2android.view; using Android.Content.PM; +using Android.Graphics; +using Android.Graphics.Drawables; +using Android.Preferences; using Android.Runtime; using Android.Support.V4.View; using Android.Support.V7.App; +using KeePassLib.Security; +using AlertDialog = Android.App.AlertDialog; +using Object = Java.Lang.Object; namespace keepass2android { @@ -96,7 +105,77 @@ namespace keepass2android { get { return App.Kp2a.GetDb().CanWrite && ((this.Group.ParentGroup != null) || App.Kp2a.GetDb().DatabaseFormat.CanHaveEntriesInRootGroup); } } - + + private class TemplateListAdapter : ArrayAdapter + { + public TemplateListAdapter(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer) + { + } + + public TemplateListAdapter(Context context, int textViewResourceId) : base(context, textViewResourceId) + { + } + + public TemplateListAdapter(Context context, int resource, int textViewResourceId) : base(context, resource, textViewResourceId) + { + } + + public TemplateListAdapter(Context context, int textViewResourceId, PwEntry[] objects) : base(context, textViewResourceId, objects) + { + } + + public TemplateListAdapter(Context context, int resource, int textViewResourceId, PwEntry[] objects) : base(context, resource, textViewResourceId, objects) + { + } + + public TemplateListAdapter(Context context, int textViewResourceId, IList objects) : base(context, textViewResourceId, objects) + { + } + + public TemplateListAdapter(Context context, int resource, int textViewResourceId, IList objects) : base(context, resource, textViewResourceId, objects) + { + } + + public override View GetView(int position, View convertView, ViewGroup parent) + { + View v = base.GetView(position, convertView, parent); + + TextView tv = (TextView)v.FindViewById(Android.Resource.Id.Text1); + tv.SetPadding(tv.PaddingLeft,0,tv.PaddingRight,0); + + PwEntry templateEntry = this.GetItem(position); + + var bmp = + Bitmap.CreateScaledBitmap( + Util.DrawableToBitmap(App.Kp2a.GetDb() + .DrawableFactory.GetIconDrawable(Context, App.Kp2a.GetDb().KpDatabase, templateEntry.IconId, PwUuid.Zero, false)), + (int)Util.convertDpToPixel(80, Context), + (int)Util.convertDpToPixel(80, Context), + true); + + + Drawable icon = new BitmapDrawable(bmp); + + if ( + PreferenceManager.GetDefaultSharedPreferences(Context) + .GetString("IconSetKey", Context.PackageName) == Context.PackageName) + { + Android.Graphics.PorterDuff.Mode mMode = Android.Graphics.PorterDuff.Mode.SrcAtop; + Color color = new Color(189, 189, 189); + icon.SetColorFilter(color, mMode); + } + + //Put the image on the TextView + tv.SetCompoundDrawablesWithIntrinsicBounds(icon, null, null, null); + tv.Text = templateEntry.Strings.ReadSafe(PwDefs.TitleField); + tv.SetTextSize(ComplexUnitType.Dip, 20); + + tv.CompoundDrawablePadding = (int)Util.convertDpToPixel(8, Context); + + return v; + } + }; + protected override void OnCreate (Bundle savedInstanceState) { base.OnCreate (savedInstanceState); @@ -139,7 +218,29 @@ namespace keepass2android if (AddEntryEnabled) { View addEntry = FindViewById (Resource.Id.fabAddNewEntry); - addEntry.Click += (sender, e) => { EntryEditActivity.Launch (this, Group, AppTask); }; + addEntry.Click += (sender, e) => + { + PwEntry defaultTemplate = new PwEntry(false, false); + defaultTemplate.IconId = PwIcon.Key; + defaultTemplate.Strings.Set(PwDefs.TitleField, new ProtectedString(false, GetString(Resource.String.DefaultTemplate))); + List templates = new List() { defaultTemplate }; + if (!PwUuid.Zero.Equals(App.Kp2a.GetDb().KpDatabase.EntryTemplatesGroup)) + { + templates.AddRange(App.Kp2a.GetDb().Groups[App.Kp2a.GetDb().KpDatabase.EntryTemplatesGroup].Entries.OrderBy(entr => entr.Strings.ReadSafe(PwDefs.TitleField))); + } + + new AlertDialog.Builder(this) + .SetAdapter(new TemplateListAdapter(this, Android.Resource.Layout.SelectDialogItem, + Android.Resource.Id.Text1, templates), (o, args) => + { + + EntryEditActivity.Launch(this, Group, templates[args.Which].Uuid, AppTask); + }) + .Show(); + + + + }; } diff --git a/src/keepass2android/IconPickerActivity.cs b/src/keepass2android/IconPickerActivity.cs index fceab804..706fd725 100644 --- a/src/keepass2android/IconPickerActivity.cs +++ b/src/keepass2android/IconPickerActivity.cs @@ -180,32 +180,6 @@ namespace keepass2android return 0; } - - -public static Bitmap DrawableToBitmap (Drawable drawable) { - Bitmap bitmap = null; - - if (drawable is BitmapDrawable) { - BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; - if(bitmapDrawable.Bitmap != null) { - return bitmapDrawable.Bitmap; - } - } - - if(drawable.IntrinsicWidth <= 0 || drawable.IntrinsicHeight <= 0) { - bitmap = Bitmap.CreateBitmap(1, 1, Bitmap.Config.Argb8888); // Single color bitmap will be created of 1x1 pixel - } else { - bitmap = Bitmap.CreateBitmap(drawable.IntrinsicWidth, drawable.IntrinsicHeight, Bitmap.Config.Argb8888); - } - - Canvas canvas = new Canvas(bitmap); - drawable.SetBounds(0, 0, canvas.Width, canvas.Height); - drawable.Draw(canvas); - return bitmap; -} - - - public override View GetView(int position, View convertView, ViewGroup parent) { View currView; @@ -226,7 +200,7 @@ public static Bitmap DrawableToBitmap (Drawable drawable) { tv.Text = "" + position; var drawable = App.Kp2a.GetDb() .DrawableFactory.GetIconDrawable(_act, App.Kp2a.GetDb().KpDatabase, (KeePassLib.PwIcon) position, null, false); - drawable = new BitmapDrawable(DrawableToBitmap(drawable)); + drawable = new BitmapDrawable(Util.DrawableToBitmap(drawable)); iv.SetImageDrawable(drawable); //App.Kp2a.GetDb().DrawableFactory.AssignDrawableTo(iv, _act, App.Kp2a.GetDb().KpDatabase, (KeePassLib.PwIcon) position, null, false); diff --git a/src/keepass2android/KeePass.cs b/src/keepass2android/KeePass.cs index d5037450..c02eb6bd 100644 --- a/src/keepass2android/KeePass.cs +++ b/src/keepass2android/KeePass.cs @@ -86,6 +86,11 @@ namespace keepass2android public const Result ExitFileStorageSelectionOk = Result.FirstUser + 8; public const Result ResultOkPasswordGenerator = Result.FirstUser + 9; + public const string TagsKey = "@tags"; + public const string OverrideUrlKey = "@override"; + public const string ExpDateKey = "@exp_date"; + + AppTask _appTask; private ActivityDesign _design; diff --git a/src/keepass2android/KpEntryTemplatedEdit.cs b/src/keepass2android/KpEntryTemplatedEdit.cs new file mode 100644 index 00000000..368ca923 --- /dev/null +++ b/src/keepass2android/KpEntryTemplatedEdit.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using KeePassLib; +using KeePassLib.Security; +using KeePassLib.Utility; + +namespace keepass2android +{ + class KpEntryTemplatedEdit : IEditMode + { + internal class KeyOrderComparer : IComparer + { + private readonly KpEntryTemplatedEdit _kpEntryTemplatedEdit; + + public KeyOrderComparer(KpEntryTemplatedEdit kpEntryTemplatedEdit) + { + _kpEntryTemplatedEdit = kpEntryTemplatedEdit; + } + + public int Compare(string x, string y) + { + int orderX = _kpEntryTemplatedEdit.GetPosition(x); + int orderY = _kpEntryTemplatedEdit.GetPosition(y); + if (orderX == orderY) + return String.Compare(x, y, StringComparison.CurrentCulture); + else + return orderX.CompareTo(orderY); + } + } + + private int GetPosition(string key) + { + int res; + if (!Int32.TryParse(_templateEntry.Strings.ReadSafe("_etm_position_" + key), out res)) + return Int32.MaxValue; + return res; + } + + private const string EtmTemplateUuid = "_etm_template_uuid"; + private const string EtmTitle = "_etm_title_"; + private readonly Database _db; + private readonly PwEntry _entry; + private readonly PwEntry _templateEntry; + + public static bool IsTemplated(Database db, PwEntry entry) + { + if (entry.Strings.Exists(EtmTemplateUuid)) + { + PwUuid templateUuid = new PwUuid(MemUtil.HexStringToByteArray(entry.Strings.ReadSafe(EtmTemplateUuid))); + return db.Entries.ContainsKey(templateUuid); + } + else + return false; + } + + public KpEntryTemplatedEdit(Database db, PwEntry entry) + { + _db = db; + _entry = entry; + PwUuid templateUuid = new PwUuid(MemUtil.HexStringToByteArray(entry.Strings.ReadSafe(EtmTemplateUuid))); + _templateEntry = db.Entries[templateUuid]; + } + + public static void InitializeEntry(PwEntry entry, PwEntry templateEntry) + { + entry.Strings.Set("_etm_template_uuid", new ProtectedString(false, templateEntry.Uuid.ToHexString())); + entry.IconId = templateEntry.IconId; + + foreach (string name in templateEntry.Strings.GetKeys()) + { + if (name.StartsWith(EtmTitle)) + { + String fieldName = name.Substring(EtmTitle.Length); + + if (fieldName.StartsWith("@")) + { + if (fieldName == KeePass.TagsKey) entry.Tags = templateEntry.Tags; + if (fieldName == KeePass.OverrideUrlKey) entry.OverrideUrl = templateEntry.OverrideUrl; + if (fieldName == KeePass.ExpDateKey) + { + entry.Expires = templateEntry.Expires; + if (entry.Expires) + entry.ExpiryTime = templateEntry.ExpiryTime; + } + continue; + } + + String type = templateEntry.Strings.ReadSafe("_etm_type_" + fieldName); + + if (type == "Divider") + continue; + + bool protectedField = type.StartsWith("Protected"); + entry.Strings.Set(fieldName, new ProtectedString(protectedField, templateEntry.Strings.ReadSafe(fieldName))); + + } + } + } + + public bool IsVisible(string fieldKey) + { + if (fieldKey == EtmTemplateUuid) + return false; + if (fieldKey == PwDefs.TitleField) + return true; + + if ((fieldKey.StartsWith("@") || (PwDefs.IsStandardField(fieldKey)))) + { + return !String.IsNullOrEmpty(GetFieldValue(fieldKey)) + || _templateEntry.Strings.Exists(EtmTitle+fieldKey); + } + + return true; + } + + private string GetFieldValue(string fieldKey) + { + if (fieldKey == KeePass.ExpDateKey) + return _entry.Expires ? _entry.ExpiryTime.ToString(CultureInfo.CurrentUICulture) : ""; + if (fieldKey == KeePass.OverrideUrlKey) + return _entry.OverrideUrl; + if (fieldKey == KeePass.TagsKey) + return StrUtil.TagsToString(_entry.Tags, true); + return _entry.Strings.ReadSafe(fieldKey); + } + + public IEnumerable SortExtraFieldKeys(IEnumerable keys) + { + var c = new KeyOrderComparer(this); + return keys.OrderBy(s => s, c); + } + + public bool ShowAddAttachments + { + get { return false; } + } + + public bool ShowAddExtras + { + get { return false; } + } + + public static bool IsTemplate(PwEntry entry) + { + if (entry == null) return false; + return entry.Strings.Exists("_etm_template"); + } + } +} \ No newline at end of file diff --git a/src/keepass2android/Resources/layout/entry_edit.xml b/src/keepass2android/Resources/layout/entry_edit.xml index d7fb35c9..baee3ea3 100644 --- a/src/keepass2android/Resources/layout/entry_edit.xml +++ b/src/keepass2android/Resources/layout/entry_edit.xml @@ -11,6 +11,7 @@ android:padding="16dp"> - - - - - - + \ No newline at end of file diff --git a/src/keepass2android/Resources/values/strings.xml b/src/keepass2android/Resources/values/strings.xml index e2eee437..da18efe1 100644 --- a/src/keepass2android/Resources/values/strings.xml +++ b/src/keepass2android/Resources/values/strings.xml @@ -106,7 +106,7 @@ Enter database filename Accessed Cancel - Comments + Notes Tags Override URL Confirm password @@ -144,7 +144,7 @@ File Browser Generate Password Group - comment + notes confirm password generated password Group name @@ -194,6 +194,7 @@ Advanced Search Go to URL Change database + Show all fields Minus Never Yes @@ -578,6 +579,8 @@ Copy + Standard-Eintrag + Change log diff --git a/src/keepass2android/Utils/Util.cs b/src/keepass2android/Utils/Util.cs index e8c5321a..fe6ca4e0 100644 --- a/src/keepass2android/Utils/Util.cs +++ b/src/keepass2android/Utils/Util.cs @@ -29,6 +29,8 @@ using Android.Views; using Android.Widget; using Android.Content.PM; using Android.Content.Res; +using Android.Graphics; +using Android.Graphics.Drawables; using Android.Util; using KeePassLib.Serialization; using Uri = Android.Net.Uri; @@ -38,6 +40,35 @@ namespace keepass2android public class Util { + public static Bitmap DrawableToBitmap(Drawable drawable) + { + Bitmap bitmap = null; + + if (drawable is BitmapDrawable) + { + BitmapDrawable bitmapDrawable = (BitmapDrawable)drawable; + if (bitmapDrawable.Bitmap != null) + { + return bitmapDrawable.Bitmap; + } + } + + if (drawable.IntrinsicWidth <= 0 || drawable.IntrinsicHeight <= 0) + { + bitmap = Bitmap.CreateBitmap(1, 1, Bitmap.Config.Argb8888); // Single color bitmap will be created of 1x1 pixel + } + else + { + bitmap = Bitmap.CreateBitmap(drawable.IntrinsicWidth, drawable.IntrinsicHeight, Bitmap.Config.Argb8888); + } + + Canvas canvas = new Canvas(bitmap); + drawable.SetBounds(0, 0, canvas.Width, canvas.Height); + drawable.Draw(canvas); + return bitmap; + } + + public static float convertDpToPixel(float dp, Context context) { Resources resources = context.Resources; diff --git a/src/keepass2android/keepass2android.csproj b/src/keepass2android/keepass2android.csproj index d96039f0..ec8f30ef 100644 --- a/src/keepass2android/keepass2android.csproj +++ b/src/keepass2android/keepass2android.csproj @@ -149,6 +149,7 @@ +