start implementing the GUI for editing AutoOpen entries

This commit is contained in:
Philipp Crocoll
2018-11-13 21:57:43 +01:00
parent 9427fb9ecc
commit 2eb7b22100
9 changed files with 489 additions and 6 deletions

View File

@@ -37,7 +37,7 @@ namespace keepass2android
return new AddEntry(ctx, app, entry, parentGroup, finish);
}
protected AddEntry(Activity ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish):base(ctx, finish) {
public AddEntry(Activity ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish):base(ctx, finish) {
_ctx = ctx;
_parentGroup = parentGroup;
_app = app;

View File

@@ -0,0 +1,256 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Provider;
using Android.Runtime;
using Android.Support.V7.App;
using Android.Text;
using Android.Util;
using Android.Views;
using Android.Widget;
using keepass2android.database.edit;
using KeePass.Util.Spr;
using KeePassLib;
using KeePassLib.Security;
using Object = Java.Lang.Object;
namespace keepass2android
{
[Activity(Label = "@string/child_dbs_title", MainLauncher = false, Theme = "@style/MyTheme_Blue", LaunchMode = LaunchMode.SingleInstance)]
[IntentFilter(new[] { "kp2a.action.ConfigureChildDatabasesActivity" }, Categories = new[] { Intent.CategoryDefault })]
public class ConfigureChildDatabasesActivity : LockCloseActivity
{
private ChildDatabasesAdapter _adapter;
public class ChildDatabasesAdapter : BaseAdapter
{
private readonly ConfigureChildDatabasesActivity _context;
internal List<AutoExecItem> _displayedChildDatabases;
public ChildDatabasesAdapter(ConfigureChildDatabasesActivity context)
{
_context = context;
Update();
}
public override Object GetItem(int position)
{
return position;
}
public override long GetItemId(int position)
{
return position;
}
private LayoutInflater cursorInflater;
public override View GetView(int position, View convertView, ViewGroup parent)
{
if (cursorInflater == null)
cursorInflater = (LayoutInflater)_context.GetSystemService(Context.LayoutInflaterService);
View view;
if (convertView == null)
{
// if it's not recycled, initialize some attributes
view = cursorInflater.Inflate(Resource.Layout.child_db_config_row, parent, false);
view.FindViewById<Button>(Resource.Id.child_db_enable_on_this_device).Click += (sender, args) =>
{
View sending_view = (View) sender;
_context.OnEnable(_displayedChildDatabases[GetClickedPos(sending_view)]);
};
view.FindViewById<Button>(Resource.Id.child_db_disable_on_this_device).Click += (sender, args) =>
{
View sending_view = (View)sender;
_context.OnDisable(_displayedChildDatabases[GetClickedPos(sending_view)]);
};
view.FindViewById<Button>(Resource.Id.child_db_edit).Click += (sender, args) =>
{
View sending_view = (View)sender;
_context.OnEdit(_displayedChildDatabases[GetClickedPos(sending_view)]);
};
view.FindViewById<Button>(Resource.Id.child_db_enable_a_copy_for_this_device).Click += (sender, args) =>
{
View sending_view = (View)sender;
_context.OnEnableCopy(_displayedChildDatabases[GetClickedPos(sending_view)]);
};
}
else
{
view = convertView;
}
var iv = view.FindViewById<ImageView>(Resource.Id.child_db_icon);
var autoExecItem = _displayedChildDatabases[position];
var pw = autoExecItem.Entry;
SprContext ctx = new SprContext(pw, App.Kp2a.FindDatabaseForElement(pw).KpDatabase, SprCompileFlags.All);
string deviceId = KeeAutoExecExt.ThisDeviceId;
view.FindViewById<TextView>(Resource.Id.child_db_title).Text =
SprEngine.Compile(pw.Strings.GetSafe(PwDefs.TitleField).ReadString(), ctx);
view.FindViewById<TextView>(Resource.Id.child_db_url).Text =
_context.GetString(Resource.String.entry_url) + ": " + SprEngine.Compile(pw.Strings.GetSafe(PwDefs.UrlField).ReadString(),ctx);
bool deviceEnabledExplict;
bool deviceEnabled = KeeAutoExecExt.IsDeviceEnabled(autoExecItem, deviceId, out deviceEnabledExplict);
deviceEnabled &= deviceEnabledExplict;
if (!autoExecItem.Enabled)
{
view.FindViewById<TextView>(Resource.Id.child_db_enabled_here).Text =
_context.GetString(Resource.String.plugin_disabled);
}
else
{
view.FindViewById<TextView>(Resource.Id.child_db_enabled_here).Text =
_context.GetString(Resource.String.child_db_enabled_on_this_device) + ": " +
(!deviceEnabledExplict ?
_context.GetString(Resource.String.unspecified)
:
((autoExecItem.Enabled && deviceEnabled)
? _context.GetString(Resource.String.yes)
: _context.GetString(Resource.String.no)));
}
view.FindViewById<Button>(Resource.Id.child_db_enable_on_this_device).Visibility = !deviceEnabled && autoExecItem.Enabled ? ViewStates.Gone : ViewStates.Visible;
view.FindViewById<Button>(Resource.Id.child_db_disable_on_this_device).Visibility = deviceEnabled && autoExecItem.Enabled ? ViewStates.Visible : ViewStates.Gone;
view.FindViewById<Button>(Resource.Id.child_db_enable_a_copy_for_this_device_container).Visibility = !deviceEnabled && autoExecItem.Enabled ? ViewStates.Visible : ViewStates.Gone;
Database db = App.Kp2a.FindDatabaseForElement(pw);
bool isExpired = pw.Expires && pw.ExpiryTime < DateTime.Now;
if (isExpired)
{
db.DrawableFactory.AssignDrawableTo(iv, _context, db.KpDatabase, PwIcon.Expired, PwUuid.Zero, false);
}
else
{
db.DrawableFactory.AssignDrawableTo(iv, _context, db.KpDatabase, pw.IconId, pw.CustomIconUuid, false);
}
view.Tag = position.ToString();
return view;
}
private static int GetClickedPos(View sending_view)
{
View viewWithTag = sending_view;
while (viewWithTag.Tag == null)
viewWithTag = (View) viewWithTag.Parent;
int clicked_pos = int.Parse((string) viewWithTag.Tag);
return clicked_pos;
}
public override int Count
{
get { return _displayedChildDatabases.Count; }
}
public void Update()
{
_displayedChildDatabases = App.Kp2a.OpenDatabases.SelectMany(db => KeeAutoExecExt.GetAutoExecItems(db.KpDatabase)
.OrderBy(e => SprEngine.Compile(e.Entry.Strings.ReadSafe(PwDefs.TitleField),new SprContext(e.Entry, App.Kp2a.FindDatabaseForElement(e.Entry).KpDatabase, SprCompileFlags.All)))
.ThenByDescending(e => e.Entry.LastModificationTime))
.ToList();
}
}
private void OnEnableCopy(AutoExecItem item)
{
//disable this device for the cloned entry
KeeAutoExecExt.SetDeviceEnabled(item, KeeAutoExecExt.ThisDeviceId, false);
//remember the original device settings
ProtectedString ifDeviceOrig = item.Entry.Strings.GetSafe(KeeAutoExecExt._ifDevice);
//reset device settings so only the current device is enabled
item.Entry.Strings.Set(KeeAutoExecExt._ifDevice,new ProtectedString(false,""));
KeeAutoExecExt.SetDeviceEnabled(item, KeeAutoExecExt.ThisDeviceId, true);
//now clone
var newEntry = item.Entry.CloneDeep();
//reset device settings
item.Entry.Strings.Set(KeeAutoExecExt._ifDevice, ifDeviceOrig);
newEntry.SetUuid(new PwUuid(true), true); // Create new UUID
string strTitle = newEntry.Strings.ReadSafe(PwDefs.TitleField);
newEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, strTitle + " (" + Android.OS.Build.Model + ")"));
var addTask = new AddEntry(this, App.Kp2a, newEntry,item.Entry.ParentGroup,new ActionOnFinish(this, (success, message, activity) => ((ConfigureChildDatabasesActivity)activity).Update()));
ProgressTask pt = new ProgressTask(App.Kp2a, this, addTask);
pt.Run();
}
private void Update()
{
_adapter.Update();
_adapter.NotifyDataSetChanged();
}
private void OnEdit(AutoExecItem item)
{
EntryActivity.Launch(this,item.Entry,0,new NullTask());
}
private void OnDisable(AutoExecItem item)
{
KeeAutoExecExt.SetDeviceEnabled(item,KeeAutoExecExt.ThisDeviceId, false);
}
private void OnEnable(AutoExecItem item)
{
KeeAutoExecExt.SetDeviceEnabled(item, KeeAutoExecExt.ThisDeviceId, true);
}
protected override void OnResume()
{
base.OnResume();
_adapter.Update();
_adapter.NotifyDataSetChanged();
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.config_child_db);
_adapter = new ChildDatabasesAdapter(this);
var listView = FindViewById<ListView>(Android.Resource.Id.List);
listView.Adapter = _adapter;
SetSupportActionBar(FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.mytoolbar));
}
}
}

View File

@@ -1,7 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Android.App;
using Android.OS;
using Android.Provider;
using Android.Webkit;
using Java.Nio.FileNio;
using KeePass.DataExchange;
using KeePass.Util.Spr;
using KeePassLib;
@@ -9,6 +13,7 @@ using KeePassLib.Keys;
using KeePassLib.Security;
using KeePassLib.Serialization;
using KeePassLib.Utility;
using Debug = System.Diagnostics.Debug;
namespace keepass2android
{
@@ -44,6 +49,23 @@ namespace keepass2android
public sealed class KeeAutoExecExt
{
public const string _ifDevice = "IfDevice";
public static string ThisDeviceId
{
get {
String android_id = Settings.Secure.GetString(Application.Context.ContentResolver, Settings.Secure.AndroidId);
string deviceName = Build.Manufacturer+" "+Build.Model;
string deviceId = deviceName + " (" + android_id + ")";
deviceId = deviceId.Replace("!", "_");
deviceId = deviceId.Replace(",", "_");
deviceId = deviceId.Replace(";", "_");
return deviceId;
}
}
private static int PrioritySort(AutoExecItem x, AutoExecItem y)
@@ -108,7 +130,72 @@ namespace keepass2android
return (bHasExcl || !bHasIncl);
}
private static List<AutoExecItem> GetAutoExecItems(PwDatabase pd)
public static void SetDeviceEnabled(AutoExecItem a, string strDevice, bool enabled=true)
{
string strList = a.IfDevice;
string deviceEntry = (enabled ? "" : "!") + strDevice;
if (string.IsNullOrEmpty(strList) || string.IsNullOrEmpty(strDevice))
{
a.Entry.Strings.Set(_ifDevice, new ProtectedString(false,deviceEntry));
return;
}
CsvOptions opt = new CsvOptions();
opt.BackslashIsEscape = false;
opt.TrimFields = true;
CsvStreamReaderEx csv = new CsvStreamReaderEx(strList, opt);
string[] vFlt = csv.ReadLine();
if (vFlt == null) { Debug.Assert(false); a.Entry.Strings.Set(_ifDevice, new ProtectedString(false, deviceEntry)); return; }
string result = "";
bool hasDevice = false;
foreach (string strFlt in vFlt)
{
string modifiedValue = strFlt;
if (string.IsNullOrEmpty(strFlt)) continue;
if ((strFlt == strDevice) || (strFlt == "!" + strDevice))
{
modifiedValue = deviceEntry;
hasDevice = true;
}
if (result != "")
{
result += opt.FieldSeparator;
}
if (modifiedValue.Contains(opt.FieldSeparator) || modifiedValue.Contains("\\") ||
modifiedValue.Contains("\""))
{
//add escaping:
modifiedValue = modifiedValue.Replace("\"","\\\"");
modifiedValue = "\""+modifiedValue+"\"";
}
result += modifiedValue;
}
if (!hasDevice)
{
if (result != "")
{
result += opt.FieldSeparator;
}
result += deviceEntry;
}
a.Entry.Strings.Set(_ifDevice, new ProtectedString(false,result));
}
public static List<AutoExecItem> GetAutoExecItems(PwDatabase pd)
{
List<AutoExecItem> l = new List<AutoExecItem>();
if (pd == null) { Debug.Assert(false); return l; }
@@ -145,7 +232,8 @@ namespace keepass2android
long.TryParse(str, out lItemPri);
a.Priority = lItemPri;
if (GetString(pe, "IfDevice", ctx, true, out str))
if (GetString(pe, _ifDevice, ctx, true, out str))
a.IfDevice = str;
++lPriStd;

View File

@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8" ?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/title_container"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="horizontal"
>
<ImageView
android:id="@+id/child_db_icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:paddingBottom="4sp"
android:paddingTop="4sp"
/>
<TextView
android:id="@+id/child_db_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/child_db_icon"
android:textSize="16sp"
android:gravity="left|center"
android:paddingBottom="8sp"
android:paddingTop="8sp"
/>
</RelativeLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:orientation="vertical">
<TextView
android:id="@+id/child_db_url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="none"
android:maxLines="100"
android:scrollHorizontally="false"
android:textSize="12sp"
android:text="@string/entry_url"/>
<TextView
android:id="@+id/child_db_enabled_here"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="none"
android:maxLines="100"
android:scrollHorizontally="false"
android:textSize="12sp"
android:text="@string/child_db_enabled_on_this_device"/>
<Button
android:id="@+id/child_db_enable_on_this_device"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/child_db_enable_on_this_device"/>
<Button
android:id="@+id/child_db_disable_on_this_device"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/child_db_disable_on_this_device"/>
<Button
android:id="@+id/child_db_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/edit"/>
<LinearLayout
android:id="@+id/child_db_enable_a_copy_for_this_device_container"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/child_db_enable_a_copy_for_this_device"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/child_db_enable_a_copy_for_this_device"/>
<keepass2android.views.Kp2aShortHelpView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/TextAppearance_Help_Dense"
android:textColor="@color/light_gray"
app:help_text="@string/EnableCopyForThisDevice_Info"
app:title_text="@string/child_db_enable_a_copy_for_this_device"
android:text="@string/EnableCopyForThisDevice_Info"
android:background="?android:attr/selectableItemBackground"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="8dp" />

View File

@@ -736,6 +736,16 @@
<string name="open_other_db">Open another database…</string>
<string name="select_database">Select database</string>
<string name="configure_child_dbs">Configure child databases…</string>
<string name="child_dbs_title">Child databases</string>
<string name="unspecified">unspecified</string>
<string name="child_db_enabled_on_this_device">Enabled on this device</string>
<string name="child_db_enable_on_this_device">Enable on this device</string>
<string name="child_db_disable_on_this_device">Disable on this device</string>
<string name="child_db_enable_a_copy_for_this_device">Copy for this device</string>
<string name="EnableCopyForThisDevice_Info">This will create and enable a copy of the child database settings. These copied settings can then be adjusted specifically for this device.</string>
<string name="DbUnlockedChannel_name">Database unlocked</string>
<string name="DbUnlockedChannel_desc">Notification about the database being unlocked</string>

View File

@@ -112,6 +112,13 @@
<intent android:action="kp2a.action.ExportDatabaseActivity"/>
</PreferenceScreen>
<PreferenceScreen
android:key="configure_child_dbs_prefs"
android:title="@string/configure_child_dbs"
>
<intent android:action="kp2a.action.ConfigureChildDatabasesActivity"/>
</PreferenceScreen>
<Preference
android:key="import_db_prefs"
android:title="@string/import_db_prefs"

View File

@@ -176,6 +176,7 @@
<Compile Include="ActivityLaunchMode.cs" />
<Compile Include="addons\CsvStreamReaderEx.cs" />
<Compile Include="ChallengeXCKey.cs" />
<Compile Include="ConfigureChildDatabasesActivity.cs" />
<Compile Include="EntryActivityClasses\ViewImagePopupItem.cs" />
<Compile Include="FileSaveProcessManager.cs" />
<Compile Include="fileselect\FilteredCursor.cs" />
@@ -821,7 +822,9 @@
<AndroidResource Include="Resources\layout\searchurlresults.xml">
<SubType>Designer</SubType>
</AndroidResource>
<AndroidResource Include="Resources\layout\searchurlresults_empty.xml" />
<AndroidResource Include="Resources\layout\searchurlresults_empty.xml">
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<Folder Include="libs\" />
@@ -1890,6 +1893,12 @@
<ItemGroup>
<AndroidResource Include="Resources\layout\open_db_selection.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\layout\child_db_config_row.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\layout\config_child_db.xml" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">