* EntryOutput is passed to CopyToClipboardService
* Modifications of EntryOutput are passed to plugins to enable actions on added fields
* PluginDatabase checks if Plugin is still installed and always updates the list of plugins (had an issue where a plugin had a request token but was not in pluginList)
* first version of QR plugin implemented
This commit is contained in:
Philipp Crocoll
2014-05-07 05:58:20 +02:00
parent 07038d7549
commit 53dd47044b
42 changed files with 1853 additions and 137 deletions

View File

@@ -1,17 +1,74 @@
using System;
using KeePassLib;
using KeePassLib.Collections;
using KeePassLib.Keys;
using KeePassLib.Security;
using KeePassLib.Serialization;
namespace keepass2android
{
/// <summary>
/// Represents the strings which are output from a PwEntry.
/// </summary>
/// In contrast to the original PwEntry, this means that placeholders are replaced. Also, plugins may modify
/// or add fields.
public class PwEntryOutput
{
private readonly PwEntry _entry;
private readonly PwDatabase _db;
private readonly ProtectedStringDictionary _outputStrings = new ProtectedStringDictionary();
/// <summary>
/// Constructs the PwEntryOutput by replacing the placeholders
/// </summary>
public PwEntryOutput(PwEntry entry, PwDatabase db)
{
_entry = entry;
_db = db;
foreach (var pair in entry.Strings)
{
_outputStrings.Set(pair.Key, new ProtectedString(entry.Strings.Get(pair.Key).IsProtected, GetStringAndReplacePlaceholders(pair.Key)));
}
}
string GetStringAndReplacePlaceholders(string key)
{
String value = Entry.Strings.ReadSafe(key);
value = SprEngine.Compile(value, new SprContext(Entry, _db, SprCompileFlags.All));
return value;
}
/// <summary>
/// Returns the ID of the entry
/// </summary>
public PwUuid Uuid
{
get { return Entry.Uuid; }
}
/// <summary>
/// The output strings for the represented entry
/// </summary>
public ProtectedStringDictionary OutputStrings { get { return _outputStrings; } }
public PwEntry Entry
{
get { return _entry; }
}
}
public class App
{
public class Kp2A
{
private static Db _mDb;
public class Db
{
public PwEntryOutput LastOpenedEntry { get; set; }
public void SetEntry(PwEntry e)
{
KpDatabase = new PwDatabase();

View File

@@ -1,14 +1,35 @@
using System;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Widget;
using KeePassLib.Security;
namespace keepass2android
{
internal class CopyToClipboardService
[Service]
public class CopyToClipboardService: Service
{
public CopyToClipboardService()
{
}
public CopyToClipboardService(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public static void CopyValueToClipboardWithTimeout(Context ctx, string text)
{
Toast.MakeText(ctx, text, ToastLength.Short).Show();
}
public override IBinder OnBind(Intent intent)
{
return null;
}
}
}

View File

@@ -113,6 +113,7 @@ namespace keepass2android
}
_activity.AddPluginAction(pluginPackage,
intent.GetStringExtra(Strings.ExtraFieldId),
intent.GetStringExtra(Strings.ExtraActionId),
intent.GetStringExtra(Strings.ExtraActionDisplayText),
intent.GetIntExtra(Strings.ExtraActionIconResId, -1),
intent.GetBundleExtra(Strings.ExtraActionData));
@@ -156,6 +157,7 @@ namespace keepass2android
private void SetPluginField(string key, string value, bool isProtected)
{
//update or add the string view:
IStringView existingField;
if (_stringViews.TryGetValue(key, out existingField))
{
@@ -168,13 +170,47 @@ namespace keepass2android
extraGroup.AddView(view.View);
}
//update the Entry output in the App database and notify the CopyToClipboard service
App.Kp2A.GetDb().LastOpenedEntry.OutputStrings.Set(key, new ProtectedString(isProtected, value));
Intent updateKeyboardIntent = new Intent(this, typeof(CopyToClipboardService));
Intent.SetAction(Intents.UpdateKeyboard);
updateKeyboardIntent.PutExtra(KeyEntry, Entry.Uuid.ToHexString());
StartService(updateKeyboardIntent);
//notify plugins
NotifyPluginsOnModification(Strings.PrefixString+key);
}
private void AddPluginAction(string pluginPackage, string fieldId, string displayText, int iconId, Bundle bundleExtra)
private void AddPluginAction(string pluginPackage, string fieldId, string popupItemId, string displayText, int iconId, Bundle bundleExtra)
{
if (fieldId != null)
{
_popupMenuItems[fieldId].Add(new PluginPopupMenuItem(this, pluginPackage, fieldId, displayText, iconId, bundleExtra));
try
{
//create a new popup item for the plugin action:
var newPopup = new PluginPopupMenuItem(this, pluginPackage, fieldId, popupItemId, displayText, iconId, bundleExtra);
//see if we already have a popup item for this field with the same item id
var popupsForField = _popupMenuItems[fieldId];
var popupItemPos = popupsForField.FindIndex(0,
item =>
(item is PluginPopupMenuItem) &&
((PluginPopupMenuItem)item).PopupItemId == popupItemId);
//replace existing or add
if (popupItemPos >= 0)
{
popupsForField[popupItemPos] = newPopup;
}
else
{
popupsForField.Add(newPopup);
}
}
catch (Exception e)
{
Kp2aLog.Log(e.ToString());
}
}
else
{
@@ -185,6 +221,7 @@ namespace keepass2android
i.SetPackage(pluginPackage);
i.PutExtra(Strings.ExtraActionData, bundleExtra);
i.PutExtra(Strings.ExtraSender, PackageName);
PluginHost.AddEntryToIntent(i, App.Kp2A.GetDb().LastOpenedEntry);
var menuOption = new PluginMenuOption()
{
@@ -407,6 +444,8 @@ namespace keepass2android
SetupEditButtons();
App.Kp2A.GetDb().LastOpenedEntry = new PwEntryOutput(Entry, App.Kp2A.GetDb().KpDatabase);
RegisterReceiver(new PluginActionReceiver(this), new IntentFilter(Strings.ActionAddEntryAction));
RegisterReceiver(new PluginFieldReceiver(this), new IntentFilter(Strings.ActionSetEntryField));
@@ -419,7 +458,22 @@ namespace keepass2android
Intent i = new Intent(Strings.ActionOpenEntry);
i.PutExtra(Strings.ExtraSender, PackageName);
PluginHost.AddEntryToIntent(i, Entry);
AddEntryToIntent(i);
foreach (var plugin in new PluginDatabase(this).GetPluginsWithAcceptedScope(Strings.ScopeCurrentEntry))
{
i.SetPackage(plugin);
SendBroadcast(i);
}
}
private void NotifyPluginsOnModification(string fieldId)
{
Intent i = new Intent(Strings.ActionEntryOutputModified);
i.PutExtra(Strings.ExtraSender, PackageName);
i.PutExtra(Strings.ExtraFieldId, fieldId);
AddEntryToIntent(i);
foreach (var plugin in new PluginDatabase(this).GetPluginsWithAcceptedScope(Strings.ScopeCurrentEntry))
{
@@ -842,5 +896,10 @@ namespace keepass2android
{
Toast.MakeText(this, "opening file TODO", ToastLength.Short).Show();
}
public void AddEntryToIntent(Intent intent)
{
PluginHost.AddEntryToIntent(intent, App.Kp2A.GetDb().LastOpenedEntry);
}
}
}

View File

@@ -0,0 +1,41 @@
using Android.Content;
using Android.Graphics.Drawables;
using PluginHostTest;
namespace keepass2android
{
/// <summary>
/// Reperesents the popup menu item in EntryActivity to copy a string to clipboard
/// </summary>
class CopyToClipboardPopupMenuIcon : IPopupMenuItem
{
private readonly Context _context;
private readonly IStringView _stringView;
public CopyToClipboardPopupMenuIcon(Context context, IStringView stringView)
{
_context = context;
_stringView = stringView;
}
public Drawable Icon
{
get
{
return _context.Resources.GetDrawable(Resource.Drawable.ic_menu_copy_holo_light);
}
}
public string Text
{
//TODO localize
get { return "Copy to clipboard"; }
}
public void HandleClick()
{
CopyToClipboardService.CopyValueToClipboardWithTimeout(_context, _stringView.Text);
}
}
}

View File

@@ -0,0 +1,34 @@
using Android.Graphics.Drawables;
using PluginHostTest;
namespace keepass2android
{
/// <summary>
/// Reperesents the popup menu item in EntryActivity to go to the URL in the field
/// </summary>
class GotoUrlMenuItem : IPopupMenuItem
{
private readonly EntryActivity _ctx;
public GotoUrlMenuItem(EntryActivity ctx)
{
_ctx = ctx;
}
public Drawable Icon
{
get { return _ctx.Resources.GetDrawable(Android.Resource.Drawable.IcMenuUpload); }
}
public string Text
{
get { return _ctx.Resources.GetString(Resource.String.menu_url); }
}
public void HandleClick()
{
//TODO
_ctx.GotoUrl();
}
}
}

View File

@@ -1,11 +1,12 @@
using System;
using Android.Content;
using Android.Graphics.Drawables;
using KeePassLib;
using PluginHostTest;
namespace keepass2android
{
/// <summary>
/// Interface for popup menu items in EntryActivity
/// </summary>
internal interface IPopupMenuItem
{
Drawable Icon { get; }
@@ -13,100 +14,4 @@ namespace keepass2android
void HandleClick();
}
class GotoUrlMenuItem : IPopupMenuItem
{
private readonly EntryActivity _ctx;
public GotoUrlMenuItem(EntryActivity ctx)
{
_ctx = ctx;
}
public Drawable Icon
{
get { return _ctx.Resources.GetDrawable(Android.Resource.Drawable.IcMenuUpload); }
}
public string Text
{
get { return _ctx.Resources.GetString(Resource.String.menu_url); }
}
public void HandleClick()
{
//TODO
_ctx.GotoUrl();
}
}
class ToggleVisibilityPopupMenuItem : IPopupMenuItem
{
private readonly EntryActivity _activity;
public ToggleVisibilityPopupMenuItem(EntryActivity activity)
{
_activity = activity;
}
public Drawable Icon
{
get
{
//return new TextDrawable("\uF06E", _activity);
return _activity.Resources.GetDrawable(Resource.Drawable.ic_action_eye_open);
}
}
public string Text
{
get
{
return _activity.Resources.GetString(
_activity._showPassword ?
Resource.String.menu_hide_password
: Resource.String.show_password);
}
}
public void HandleClick()
{
_activity.ToggleVisibility();
}
}
class CopyToClipboardPopupMenuIcon : IPopupMenuItem
{
private readonly Context _context;
private readonly IStringView _stringView;
public CopyToClipboardPopupMenuIcon(Context context, IStringView stringView)
{
_context = context;
_stringView = stringView;
}
public Drawable Icon
{
get
{
return _context.Resources.GetDrawable(Resource.Drawable.ic_menu_copy_holo_light);
}
}
public string Text
{
//TODO localize
get { return "Copy to clipboard"; }
}
public void HandleClick()
{
CopyToClipboardService.CopyValueToClipboardWithTimeout(_context, _stringView.Text);
}
}
}

View File

@@ -3,6 +3,9 @@ using PluginHostTest;
namespace keepass2android
{
/// <summary>
/// Represents the popup menu item in EntryActivity to open the associated attachment
/// </summary>
internal class OpenBinaryPopupItem : IPopupMenuItem
{
private readonly string _key;

View File

@@ -5,20 +5,25 @@ using Keepass2android.Pluginsdk;
namespace keepass2android
{
/// <summary>
/// Represents a popup menu item in EntryActivity which was added by a plugin. The click will therefore broadcast to the plugin.
/// </summary>
class PluginPopupMenuItem : IPopupMenuItem
{
private readonly Context _ctx;
private readonly EntryActivity _activity;
private readonly string _pluginPackage;
private readonly string _fieldId;
private readonly string _popupItemId;
private readonly string _displayText;
private readonly int _iconId;
private readonly Bundle _bundleExtra;
public PluginPopupMenuItem(Context ctx, string pluginPackage, string fieldId, string displayText, int iconId, Bundle bundleExtra)
public PluginPopupMenuItem(EntryActivity activity, string pluginPackage, string fieldId, string popupItemId, string displayText, int iconId, Bundle bundleExtra)
{
_ctx = ctx;
_activity = activity;
_pluginPackage = pluginPackage;
_fieldId = fieldId;
_popupItemId = popupItemId;
_displayText = displayText;
_iconId = iconId;
_bundleExtra = bundleExtra;
@@ -26,22 +31,29 @@ namespace keepass2android
public Drawable Icon
{
get { return _ctx.PackageManager.GetResourcesForApplication(_pluginPackage).GetDrawable(_iconId); }
get { return _activity.PackageManager.GetResourcesForApplication(_pluginPackage).GetDrawable(_iconId); }
}
public string Text
{
get { return _displayText; }
}
public string PopupItemId
{
get { return _popupItemId; }
}
public void HandleClick()
{
Intent i = new Intent(Strings.ActionEntryActionSelected);
i.SetPackage(_pluginPackage);
i.PutExtra(Strings.ExtraActionData, _bundleExtra);
i.PutExtra(Strings.ExtraFieldId, _fieldId);
i.PutExtra(Strings.ExtraSender, _ctx.PackageName);
PluginHost.AddEntryToIntent(i, Entry);
i.PutExtra(Strings.ExtraSender, _activity.PackageName);
_ctx.SendBroadcast(i);
_activity.AddEntryToIntent(i);
_activity.SendBroadcast(i);
}
}
}

View File

@@ -0,0 +1,46 @@
using Android.Graphics.Drawables;
using PluginHostTest;
namespace keepass2android
{
/// <summary>
/// Reperesents the popup menu item in EntryActivity to toggle visibility of all protected strings (e.g. Password)
/// </summary>
class ToggleVisibilityPopupMenuItem : IPopupMenuItem
{
private readonly EntryActivity _activity;
public ToggleVisibilityPopupMenuItem(EntryActivity activity)
{
_activity = activity;
}
public Drawable Icon
{
get
{
//return new TextDrawable("\uF06E", _activity);
return _activity.Resources.GetDrawable(Resource.Drawable.ic_action_eye_open);
}
}
public string Text
{
get
{
return _activity.Resources.GetString(
_activity._showPassword ?
Resource.String.menu_hide_password
: Resource.String.show_password);
}
}
public void HandleClick()
{
_activity.ToggleVisibility();
}
}
}

View File

@@ -3,6 +3,9 @@ using PluginHostTest;
namespace keepass2android
{
/// <summary>
/// Represents the popup menu item in EntryActivity to store the binary attachment on SD card
/// </summary>
internal class WriteBinaryToFilePopupItem : IPopupMenuItem
{
private readonly string _key;

View File

@@ -10,7 +10,9 @@ using PluginHostTest;
namespace keepass2android
{
/// <summary>
/// Represents information about a plugin for display in the plugin list activity
/// </summary>
public class PluginItem
{
private readonly string _package;

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Android.Content;
using Android.Content.PM;
using Android.Util;
using Keepass2android.Pluginsdk;
@@ -32,16 +33,15 @@ namespace keepass2android
var editor = prefs.Edit();
editor.PutString(_requesttoken, Guid.NewGuid().ToString());
editor.Commit();
var hostPrefs = GetHostPrefs();
var plugins = hostPrefs.GetStringSet(_pluginlist, new List<string>());
if (!plugins.Contains(packageName))
{
plugins.Add(packageName);
hostPrefs.Edit().PutStringSet(_pluginlist, plugins).Commit();
}
}
var hostPrefs = GetHostPrefs();
var plugins = hostPrefs.GetStringSet(_pluginlist, new List<string>());
if (!plugins.Contains(packageName))
{
plugins.Add(packageName);
hostPrefs.Edit().PutStringSet(_pluginlist, plugins).Commit();
}
return prefs;
}
@@ -63,7 +63,20 @@ namespace keepass2android
public IEnumerable<String> GetAllPluginPackages()
{
var hostPrefs = GetHostPrefs();
return hostPrefs.GetStringSet(_pluginlist, new List<string>());
return hostPrefs.GetStringSet(_pluginlist, new List<string>()).Where(IsPackageInstalled);
}
public bool IsPackageInstalled(string targetPackage)
{
try
{
PackageInfo info = _ctx.PackageManager.GetPackageInfo(targetPackage, PackageInfoFlags.MetaData);
}
catch (PackageManager.NameNotFoundException e)
{
return false;
}
return true;
}
public bool IsEnabled(string pluginPackage)

View File

@@ -13,6 +13,7 @@ using Android.Util;
using Android.Views;
using Android.Widget;
using KeePassLib;
using KeePassLib.Collections;
using KeePassLib.Serialization;
using KeePassLib.Utility;
using Keepass2android;
@@ -142,7 +143,7 @@ namespace keepass2android
return true;
}
public static void AddEntryToIntent(Intent intent, PwEntry entry)
public static void AddEntryToIntent(Intent intent, PwEntryOutput entry)
{
/*//add the entry XML
not yet implemented. What to do with attachments?
@@ -151,22 +152,12 @@ namespace keepass2android
string entryData = StrUtil.Utf8.GetString(memStream.ToArray());
intent.PutExtra(Strings.ExtraEntryData, entryData);
*/
//add the compiled string array (placeholders replaced taking into account the db context)
Dictionary<string, string> compiledFields = new Dictionary<string, string>();
foreach (var pair in entry.Strings)
{
String key = pair.Key;
//add the output string array (placeholders replaced taking into account the db context)
Dictionary<string, string> outputFields = entry.OutputStrings.ToDictionary(pair => StrUtil.SafeXmlString(pair.Key), pair => pair.Value.ReadString());
String value = entry.Strings.ReadSafe(key);
value = SprEngine.Compile(value, new SprContext(entry, App.Kp2A.GetDb().KpDatabase, SprCompileFlags.All));
compiledFields.Add(StrUtil.SafeXmlString(pair.Key), value);
}
JSONObject json = new JSONObject(compiledFields);
JSONObject json = new JSONObject(outputFields);
var jsonStr = json.ToString();
intent.PutExtra(Strings.ExtraCompiledEntryData, jsonStr);
intent.PutExtra(Strings.ExtraEntryOutputData, jsonStr);
intent.PutExtra(Strings.ExtraEntryId, entry.Uuid.ToHexString());

View File

@@ -60,11 +60,15 @@
<Compile Include="ClickView.cs" />
<Compile Include="CopyToClipboardService.cs" />
<Compile Include="EntryActivity.cs" />
<Compile Include="EntryActivityClasses\CopyToClipboardPopupMenuIcon.cs" />
<Compile Include="EntryActivityClasses\GotoUrlMenuItem.cs" />
<Compile Include="EntryActivityClasses\ToggleVisibilityPopupMenuItem.cs" />
<Compile Include="EntryContentsView.cs" />
<Compile Include="EntrySection.cs" />
<Compile Include="EntryActivityClasses\ExtraStringView.cs" />
<Compile Include="EntryActivityClasses\IPopupMenuItem.cs" />
<Compile Include="EntryActivityClasses\IStringView.cs" />
<Compile Include="Intents.cs" />
<Compile Include="Kp2aShortHelpView.cs" />
<Compile Include="EntryActivityClasses\OpenBinaryPopupItem.cs" />
<Compile Include="PluginDatabase.cs" />