PluginHost-Test project: modified EntryActivity for receiving actions and fields

This commit is contained in:
Philipp Crocoll 2014-05-05 06:15:19 +02:00
parent 4697dbf41c
commit 07038d7549
34 changed files with 1745 additions and 333 deletions

View File

@ -25,6 +25,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KP2AKdbLibraryBinding", "KP
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArtTestApp", "ArtTestApp\ArtTestApp.csproj", "{1FF6C335-A627-43C9-AAA7-CBAC2E74CD18}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginHostTest", "PluginHostTest\PluginHostTest.csproj", "{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginSdkBinding", "PluginSdkBinding\PluginSdkBinding.csproj", "{3DA3911E-36DE-465E-8F15-F1991B6437E5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -284,6 +288,48 @@ Global
{1FF6C335-A627-43C9-AAA7-CBAC2E74CD18}.ReleaseNoNet|Mixed Platforms.Deploy.0 = Release|Any CPU
{1FF6C335-A627-43C9-AAA7-CBAC2E74CD18}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{1FF6C335-A627-43C9-AAA7-CBAC2E74CD18}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|Win32.ActiveCfg = Debug|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|x64.ActiveCfg = Debug|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|Any CPU.Build.0 = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|Any CPU.Deploy.0 = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|Win32.ActiveCfg = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|x64.ActiveCfg = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|Any CPU.Deploy.0 = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|Mixed Platforms.Deploy.0 = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Debug|Win32.ActiveCfg = Debug|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Debug|x64.ActiveCfg = Debug|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Any CPU.Build.0 = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Win32.ActiveCfg = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|x64.ActiveCfg = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

37
src/PluginHostTest/App.cs Normal file
View File

@ -0,0 +1,37 @@
using KeePassLib;
using KeePassLib.Keys;
using KeePassLib.Serialization;
namespace keepass2android
{
public class App
{
public class Kp2A
{
private static Db _mDb;
public class Db
{
public void SetEntry(PwEntry e)
{
KpDatabase = new PwDatabase();
KpDatabase.New(new IOConnectionInfo(), new CompositeKey());
KpDatabase.RootGroup.AddEntry(e, true);
}
public PwDatabase KpDatabase
{
get; set;
}
}
public static Db GetDb()
{
if (_mDb == null)
_mDb = new Db();
return _mDb;
}
}
}
}

View File

@ -0,0 +1,14 @@
using Android.Content;
using Android.Widget;
using KeePassLib.Security;
namespace keepass2android
{
internal class CopyToClipboardService
{
public static void CopyValueToClipboardWithTimeout(Context ctx, string text)
{
Toast.MakeText(ctx, text, ToastLength.Short).Show();
}
}
}

View File

@ -19,10 +19,12 @@ using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Threading;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Text;
using Android.Views;
using Android.Widget;
using Android.Preferences;
@ -32,79 +34,412 @@ using Android.Content.PM;
using Android.Webkit;
using Android.Graphics;
using Java.IO;
using KeePassLib;
using KeePassLib.Security;
using Keepass2android.Pluginsdk;
using PluginHostTest;
using Uri = Android.Net.Uri;
namespace keepass2android
{
[Activity (Label = "@string/app_name", ConfigurationChanges=ConfigChanges.Orientation|ConfigChanges.KeyboardHidden, Theme="@style/NoTitleBar")]
public class EntryActivity : Activity {
[Activity(Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
Theme = "@style/NoTitleBar")]
public class EntryActivity : Activity
{
public const String KeyEntry = "entry";
public const String KeyRefreshPos = "refresh_pos";
public const String KeyCloseAfterCreate = "close_after_create";
protected PwEntry Entry = new PwEntry(true, true);
private static Typeface _passwordFont;
private bool _showPassword;
internal bool _showPassword;
private int _pos;
private List<TextView> _protectedTextViews;
private readonly Dictionary<string, List<IPopupMenuItem>> _popupMenuItems =
new Dictionary<string, List<IPopupMenuItem>>();
protected void SetEntryView() {
private readonly Dictionary<string, IStringView> _stringViews = new Dictionary<string, IStringView>();
private readonly List<PluginMenuOption> _pendingMenuOptions = new List<PluginMenuOption>();
private IMenu _menu;
protected void SetEntryView()
{
SetContentView(Resource.Layout.entry_view);
}
protected void SetupEditButtons() {
View edit = FindViewById(Resource.Id.entry_edit);
protected void SetupEditButtons()
{
View edit = FindViewById(Resource.Id.entry_edit);
if (true)
{
edit.Visibility = ViewStates.Visible;
edit.Click += (sender, e) =>
{
};
{
};
}
else
{
edit.Visibility = ViewStates.Gone;
}
}
private class PluginActionReceiver : BroadcastReceiver
{
private readonly EntryActivity _activity;
public PluginActionReceiver(EntryActivity activity)
{
_activity = activity;
}
public override void OnReceive(Context context, Intent intent)
{
var pluginPackage = intent.GetStringExtra(Strings.ExtraSender);
if (new PluginDatabase(context).IsValidAccessToken(pluginPackage,
intent.GetStringExtra(Strings.ExtraAccessToken),
Strings.ScopeCurrentEntry))
{
if (intent.GetStringExtra(Strings.ExtraEntryId) != _activity.Entry.Uuid.ToHexString())
{
Kp2aLog.Log("received action for wrong entry " + intent.GetStringExtra(Strings.ExtraEntryId));
return;
}
_activity.AddPluginAction(pluginPackage,
intent.GetStringExtra(Strings.ExtraFieldId),
intent.GetStringExtra(Strings.ExtraActionDisplayText),
intent.GetIntExtra(Strings.ExtraActionIconResId, -1),
intent.GetBundleExtra(Strings.ExtraActionData));
}
else
{
Kp2aLog.Log("received invalid request. Plugin not authorized.");
}
}
}
private class PluginFieldReceiver : BroadcastReceiver
{
private readonly EntryActivity _activity;
public PluginFieldReceiver(EntryActivity activity)
{
_activity = activity;
}
public override void OnReceive(Context context, Intent intent)
{
if (intent.GetStringExtra(Strings.ExtraEntryId) != _activity.Entry.Uuid.ToHexString())
{
Kp2aLog.Log("received field for wrong entry " + intent.GetStringExtra(Strings.ExtraEntryId));
return;
}
if (!new PluginDatabase(context).IsValidAccessToken(intent.GetStringExtra(Strings.ExtraSender),
intent.GetStringExtra(Strings.ExtraAccessToken),
Strings.ScopeCurrentEntry))
{
Kp2aLog.Log("received field with invalid access token from " + intent.GetStringExtra(Strings.ExtraSender));
return;
}
string key = intent.GetStringExtra(Strings.ExtraFieldId);
string value = intent.GetStringExtra(Strings.ExtraFieldValue);
bool isProtected = intent.GetBooleanExtra(Strings.ExtraFieldProtected, false);
_activity.SetPluginField(key, value, isProtected);
}
}
private void SetPluginField(string key, string value, bool isProtected)
{
IStringView existingField;
if (_stringViews.TryGetValue(key, out existingField))
{
existingField.Text = value;
}
else
{
ViewGroup extraGroup = (ViewGroup) FindViewById(Resource.Id.extra_strings);
var view = CreateExtraSection(key, value, isProtected);
extraGroup.AddView(view.View);
}
}
private void AddPluginAction(string pluginPackage, string fieldId, string displayText, int iconId, Bundle bundleExtra)
{
if (fieldId != null)
{
_popupMenuItems[fieldId].Add(new PluginPopupMenuItem(this, pluginPackage, fieldId, displayText, iconId, bundleExtra));
}
else
{
//we need to add an option to the menu.
//As it is not sure that OnCreateOptionsMenu was called yet, we cannot access _menu without a check:
Intent i = new Intent(Strings.ActionEntryActionSelected);
i.SetPackage(pluginPackage);
i.PutExtra(Strings.ExtraActionData, bundleExtra);
i.PutExtra(Strings.ExtraSender, PackageName);
var menuOption = new PluginMenuOption()
{
DisplayText = displayText,
Icon = PackageManager.GetResourcesForApplication(pluginPackage).GetDrawable(iconId),
Intent = i
};
if (_menu != null)
{
AddMenuOption(menuOption);
}
else
{
lock (_pendingMenuOptions)
{
_pendingMenuOptions.Add(menuOption);
}
}
}
}
private void AddMenuOption(PluginMenuOption menuOption)
{
var menuItem = _menu.Add(menuOption.DisplayText);
menuItem.SetIcon(menuOption.Icon);
menuItem.SetIntent(menuOption.Intent);
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
_menu = menu;
base.OnCreateOptionsMenu(menu);
MenuInflater inflater = MenuInflater;
inflater.Inflate(Resource.Menu.entry, menu);
lock (_pendingMenuOptions)
{
foreach (var option in _pendingMenuOptions)
AddMenuOption(option);
_pendingMenuOptions.Clear();
}
UpdateTogglePasswordMenu();
IMenuItem gotoUrl = menu.FindItem(Resource.Id.menu_goto_url);
//Disabled IMenuItem copyUser = menu.FindItem(Resource.Id.menu_copy_user);
//Disabled IMenuItem copyPass = menu.FindItem(Resource.Id.menu_copy_pass);
// In API >= 11 onCreateOptionsMenu may be called before onCreate completes
// so _entry may not be set
if (Entry == null)
{
gotoUrl.SetVisible(false);
//Disabled copyUser.SetVisible(false);
//Disabled copyPass.SetVisible(false);
}
else
{
String url = Entry.Strings.ReadSafe(PwDefs.UrlField);
if (String.IsNullOrEmpty(url))
{
// disable button if url is not available
gotoUrl.SetVisible(false);
}
if (String.IsNullOrEmpty(Entry.Strings.ReadSafe(PwDefs.UserNameField)))
{
// disable button if username is not available
//Disabled copyUser.SetVisible(false);
}
if (String.IsNullOrEmpty(Entry.Strings.ReadSafe(PwDefs.PasswordField)))
{
// disable button if password is not available
//Disabled copyPass.SetVisible(false);
}
}
return true;
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
//check if this is a plugin action
if ((item.Intent != null) && (item.Intent.Action == Strings.ActionEntryActionSelected))
{
//yes. let the plugin handle the click:
SendBroadcast(item.Intent);
return true;
}
switch (item.ItemId)
{
case Resource.Id.menu_donate:
try
{
// Util.GotoDonateUrl(this);
}
catch (ActivityNotFoundException)
{
Toast.MakeText(this, Resource.String.error_failed_to_launch_link, ToastLength.Long).Show();
return false;
}
return true;
case Resource.Id.menu_toggle_pass:
if (_showPassword)
{
item.SetTitle(Resource.String.show_password);
_showPassword = false;
}
else
{
item.SetTitle(Resource.String.menu_hide_password);
_showPassword = true;
}
SetPasswordStyle();
return true;
case Resource.Id.menu_goto_url:
string url = _stringViews[PwDefs.UrlField].Text;
if (url == null) return false;
// Default http:// if no protocol specified
if (!url.Contains("://"))
{
url = "http://" + url;
}
try
{
}
catch (ActivityNotFoundException)
{
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
}
return true;
/* TODO: required?
case Resource.Id.menu_copy_user:
timeoutCopyToClipboard(_entry.Strings.ReadSafe (PwDefs.UserNameField));
return true;
case Resource.Id.menu_copy_pass:
timeoutCopyToClipboard(_entry.Strings.ReadSafe (PwDefs.UserNameField));
return true;
*/
case Resource.Id.menu_rate:
try
{
}
catch (ActivityNotFoundException)
{
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
}
return true;
case Resource.Id.menu_suggest_improvements:
try
{
}
catch (ActivityNotFoundException)
{
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
}
return true;
case Resource.Id.menu_lock:
return true;
case Resource.Id.menu_translate:
try
{
}
catch (ActivityNotFoundException)
{
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
}
return true;
case Android.Resource.Id.Home:
//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:
//http://developer.android.com/training/implementing-navigation/ancestral.html
Finish();
return true;
}
return base.OnOptionsItemSelected(item);
}
protected override void OnCreate(Bundle savedInstanceState)
{
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
long usageCount = prefs.GetLong(GetString(Resource.String.UsageCount_key), 0);
ISharedPreferencesEditor edit = prefs.Edit();
edit.PutLong(GetString(Resource.String.UsageCount_key), usageCount+1);
edit.PutLong(GetString(Resource.String.UsageCount_key), usageCount + 1);
edit.Commit();
_showPassword = ! prefs.GetBoolean(GetString(Resource.String.maskpass_key), Resources.GetBoolean(Resource.Boolean.maskpass_default));
_showPassword =
!prefs.GetBoolean(GetString(Resource.String.maskpass_key), Resources.GetBoolean(Resource.Boolean.maskpass_default));
Entry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "philipp "));
Entry.Strings.Set(PwDefs.PasswordField, new ProtectedString(true, "password value"));
Entry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "https://www.google.com"));
Entry.Strings.Set("field header", new ProtectedString(true, "protected field value"));
Entry.Strings.Set("public field header", new ProtectedString(false, "public field value"));
base.OnCreate(savedInstanceState);
SetEntryView();
FillData(false);
FillData();
SetupEditButtons();
RegisterReceiver(new PluginActionReceiver(this), new IntentFilter(Strings.ActionAddEntryAction));
RegisterReceiver(new PluginFieldReceiver(this), new IntentFilter(Strings.ActionSetEntryField));
new Thread(NotifyPluginsOnOpen).Start();
}
private void NotifyPluginsOnOpen()
{
App.Kp2A.GetDb().SetEntry(Entry);
Intent i = new Intent(Strings.ActionOpenEntry);
i.PutExtra(Strings.ExtraSender, PackageName);
PluginHost.AddEntryToIntent(i, Entry);
foreach (var plugin in new PluginDatabase(this).GetPluginsWithAcceptedScope(Strings.ScopeCurrentEntry))
{
i.SetPackage(plugin);
SendBroadcast(i);
}
}
public void CompleteOnCreate()
{
}
private String getDateTime(DateTime dt) {
return dt.ToString ("g", CultureInfo.CurrentUICulture);
private String getDateTime(DateTime dt)
{
return dt.ToString("g", CultureInfo.CurrentUICulture);
}
String concatTags(List<string> tags)
private String concatTags(List<string> tags)
{
StringBuilder sb = new StringBuilder();
foreach (string tag in tags)
@ -113,70 +448,98 @@ namespace keepass2android
sb.Append(", ");
}
if (tags.Count > 0)
sb.Remove(sb.Length-2,2);
sb.Remove(sb.Length - 2, 2);
return sb.ToString();
}
void PopulateExtraStrings(bool trimList)
private void PopulateExtraStrings()
{
ViewGroup extraGroup = (ViewGroup)FindViewById(Resource.Id.extra_strings);
if (trimList)
ViewGroup extraGroup = (ViewGroup) FindViewById(Resource.Id.extra_strings);
foreach (var pair in Entry.Strings.Where(pair => !PwDefs.IsStandardField(pair.Key)).OrderBy(pair => pair.Key))
{
extraGroup.RemoveAllViews();
var stringView = CreateExtraSection(pair.Key, pair.Value.ReadString(), pair.Value.IsProtected);
extraGroup.AddView(stringView.View);
}
bool hasExtraFields = false;
foreach (var view in from pair in new Dictionary<string, string>() { { "Field header", "field value" }, { "another header", "_aiaeiae" } }
orderby pair.Key
select CreateEditSection(pair.Key, pair.Value, true))
{
extraGroup.AddView(view);
hasExtraFields = true;
}
FindViewById(Resource.Id.entry_extra_strings_label).Visibility = hasExtraFields ? ViewStates.Visible : ViewStates.Gone;
}
View CreateEditSection(string key, string value, bool isProtected)
private ExtraStringView CreateExtraSection(string key, string value, bool isProtected)
{
LinearLayout layout = new LinearLayout(this, null) {Orientation = Orientation.Vertical};
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
layoutParams.SetMargins(10,0,0,0);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FillParent,
ViewGroup.LayoutParams.WrapContent);
layout.LayoutParameters = layoutParams;
View viewInflated = LayoutInflater.Inflate(Resource.Layout.entry_extrastring_title,null);
TextView keyView = (TextView)viewInflated;
View viewInflated = LayoutInflater.Inflate(Resource.Layout.entry_extrastring_title, null);
TextView keyView = viewInflated.FindViewById<TextView>(Resource.Id.entry_title);
if (key != null)
keyView.Text = key;
layout.AddView(keyView);
TextView valueView = (TextView)LayoutInflater.Inflate(Resource.Layout.entry_extrastring_value, null);
layout.AddView(viewInflated);
RelativeLayout valueViewContainer =
(RelativeLayout) LayoutInflater.Inflate(Resource.Layout.entry_extrastring_value, null);
var valueView = valueViewContainer.FindViewById<TextView>(Resource.Id.entry_extra);
if (value != null)
valueView.Text = value;
SetPasswordTypeface(valueView);
if (isProtected)
{
RegisterProtectedTextView(valueView);
valueView.TransformationMethod = PasswordTransformationMethod.Instance;
}
layout.AddView(valueViewContainer);
var stringView = new ExtraStringView(layout, valueView, keyView);
_stringViews.Add(key, stringView);
RegisterTextPopup(valueViewContainer, valueViewContainer.FindViewById(Resource.Id.extra_vdots), key, isProtected);
return stringView;
if ((int)Build.VERSION.SdkInt >= 11)
valueView.SetTextIsSelectable(true);
layout.AddView(valueView);
return layout;
}
private List<IPopupMenuItem> RegisterPopup(string popupKey, View clickView, View anchorView)
{
clickView.Click += (sender, args) =>
{
ShowPopup(anchorView, popupKey);
};
_popupMenuItems[popupKey] = new List<IPopupMenuItem>();
return _popupMenuItems[popupKey];
}
private void RegisterProtectedTextView(TextView protectedTextView)
{
_protectedTextViews.Add(protectedTextView);
}
void PopulateBinaries(bool trimList)
private void PopulateBinaries()
{
ViewGroup binariesGroup = (ViewGroup)FindViewById(Resource.Id.binaries);
if (trimList)
{
binariesGroup.RemoveAllViews();
}
foreach (KeyValuePair<string, string> pair in new Dictionary<string, string>() { {"abc",""}, {"test.png","uia"} })
ViewGroup binariesGroup = (ViewGroup) FindViewById(Resource.Id.binaries);
foreach (KeyValuePair<string, string> pair in new Dictionary<string, string>() {{"abc", ""}, {"test.png", "uia"}})
{
String key = pair.Key;
RelativeLayout valueViewContainer =
(RelativeLayout) LayoutInflater.Inflate(Resource.Layout.entry_extrastring_value, null);
var valueView = valueViewContainer.FindViewById<TextView>(Resource.Id.entry_extra);
if (key != null)
valueView.Text = key;
string popupKey = Strings.PrefixBinary + key;
var itemList = RegisterPopup(popupKey, valueViewContainer, valueViewContainer.FindViewById(Resource.Id.extra_vdots));
itemList.Add(new WriteBinaryToFilePopupItem(key, this));
itemList.Add(new OpenBinaryPopupItem(key, this));
binariesGroup.AddView(valueViewContainer);
/*
Button binaryButton = new Button(this);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
binaryButton.Text = key;
@ -206,8 +569,8 @@ namespace keepass2android
};
binariesGroup.AddView(binaryButton,layoutParams);
*/
}
FindViewById(Resource.Id.entry_binaries_label).Visibility = true ? ViewStates.Visible : ViewStates.Gone;
}
@ -217,7 +580,8 @@ namespace keepass2android
{
String type = null;
String extension = MimeTypeMap.GetFileExtensionFromUrl(url);
if (extension != null) {
if (extension != null)
{
MimeTypeMap mime = MimeTypeMap.Singleton;
type = mime.GetMimeTypeFromExtension(extension);
}
@ -229,94 +593,182 @@ namespace keepass2android
base.OnBackPressed();
}
protected void FillData(bool trimList)
protected void FillData()
{
_protectedTextViews = new List<TextView>();
ImageView iv = (ImageView)FindViewById(Resource.Id.entry_icon);
ImageView iv = (ImageView) FindViewById(Resource.Id.entry_icon);
if (iv != null)
{
iv.SetImageDrawable(Resources.GetDrawable(Resource.Drawable.ic00));
}
ActionBar.Title = "Entry title";
ActionBar.SetDisplayHomeAsUpEnabled(true);
PopulateText(Resource.Id.entry_user_name, Resource.Id.entry_user_name_label, "user name");
PopulateText(Resource.Id.entry_url, Resource.Id.entry_url_label, "www.google.com");
PopulateText(Resource.Id.entry_password, Resource.Id.entry_password_label, "my password");
PopulateStandardText(Resource.Id.entry_user_name, Resource.Id.entryfield_container_username, PwDefs.UserNameField);
PopulateStandardText(Resource.Id.entry_url, Resource.Id.entryfield_container_url, PwDefs.UrlField);
PopulateStandardText(Resource.Id.entry_password, Resource.Id.entryfield_container_password, PwDefs.PasswordField);
RegisterProtectedTextView(FindViewById<TextView>(Resource.Id.entry_password));
SetPasswordTypeface(FindViewById<TextView>(Resource.Id.entry_password));
PopulateText(Resource.Id.entry_created, Resource.Id.entry_created_label, getDateTime(DateTime.Now));
PopulateText(Resource.Id.entry_modified, Resource.Id.entry_modified_label, getDateTime(DateTime.Now));
if (true)
{
FindViewById(Resource.Id.entry_expires).Visibility = ViewStates.Visible;
FindViewById(Resource.Id.entry_expires_label).Visibility = ViewStates.Visible;
PopulateText(Resource.Id.entry_expires, Resource.Id.entry_expires_label, getDateTime(DateTime.Now));
}
RegisterTextPopup(FindViewById<RelativeLayout>(Resource.Id.username_container),
FindViewById(Resource.Id.username_vdots), PwDefs.UserNameField);
RegisterTextPopup(FindViewById<RelativeLayout>(Resource.Id.url_container),
FindViewById(Resource.Id.url_vdots), PwDefs.UrlField)
.Add(new GotoUrlMenuItem(this));
RegisterTextPopup(FindViewById<RelativeLayout>(Resource.Id.password_container),
FindViewById(Resource.Id.password_vdots), PwDefs.PasswordField);
PopulateText(Resource.Id.entry_created, Resource.Id.entryfield_container_created, getDateTime(Entry.CreationTime));
PopulateText(Resource.Id.entry_modified, Resource.Id.entryfield_container_modified, getDateTime(Entry.LastModificationTime));
if (Entry.Expires)
{
PopulateText(Resource.Id.entry_expires, Resource.Id.entryfield_container_expires, getDateTime(Entry.ExpiryTime));
}
else
{
FindViewById(Resource.Id.entry_expires).Visibility = ViewStates.Gone;
FindViewById(Resource.Id.entry_expires_label).Visibility = ViewStates.Gone;
PopulateText(Resource.Id.entry_expires, Resource.Id.entryfield_container_expires, null);
}
PopulateText(Resource.Id.entry_comment, Resource.Id.entry_comment_label, "some text about this entry");
PopulateStandardText(Resource.Id.entry_comment, Resource.Id.entryfield_container_comment, PwDefs.NotesField);
PopulateText(Resource.Id.entry_tags, Resource.Id.entryfield_container_tags, concatTags(Entry.Tags));
PopulateText(Resource.Id.entry_override_url, Resource.Id.entryfield_container_overrideurl, Entry.OverrideUrl);
PopulateText(Resource.Id.entry_tags, Resource.Id.entry_tags_label, "bla; blubb; blablubb");
PopulateExtraStrings();
PopulateExtraStrings(trimList);
PopulateBinaries(trimList);
PopulateBinaries();
SetPasswordStyle();
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (resultCode == /*TODO*/ 0)
{
if (resultCode == /*TODO*/ 0)
{
RequiresRefresh();
}
Recreate();
}
}
protected override void OnDestroy()
{
NotifyPluginsOnClose();
base.OnDestroy();
}
private void NotifyPluginsOnClose()
{
Intent i = new Intent(Strings.ActionCloseEntryView);
i.PutExtra(Strings.ExtraSender, PackageName);
foreach (var plugin in new PluginDatabase(this).GetPluginsWithAcceptedScope(Strings.ScopeCurrentEntry))
{
i.SetPackage(plugin);
SendBroadcast(i);
}
}
private List<IPopupMenuItem> RegisterTextPopup(View container, View anchor, string fieldKey)
{
return RegisterTextPopup(container, anchor, fieldKey, Entry.Strings.GetSafe(fieldKey).IsProtected);
}
private List<IPopupMenuItem> RegisterTextPopup(View container, View anchor, string fieldKey, bool isProtected)
{
string popupKey = Strings.PrefixString + fieldKey;
var popupItems = RegisterPopup(
popupKey,
container,
anchor);
popupItems.Add(new CopyToClipboardPopupMenuIcon(this, _stringViews[fieldKey]));
if (isProtected)
popupItems.Add(new ToggleVisibilityPopupMenuItem(this));
return popupItems;
}
private void ShowPopup(View anchor, string popupKey)
{
//PopupMenu popupMenu = new PopupMenu(this, FindViewById(Resource.Id.entry_user_name));
PopupMenu popupMenu = new PopupMenu(this, anchor);
AccessManager.PreparePopup(popupMenu);
int itemId = 0;
foreach (IPopupMenuItem popupItem in _popupMenuItems[popupKey])
{
popupMenu.Menu.Add(0, itemId, 0, popupItem.Text)
.SetIcon(popupItem.Icon);
itemId++;
}
popupMenu.MenuItemClick += delegate(object sender, PopupMenu.MenuItemClickEventArgs args)
{
_popupMenuItems[popupKey][args.Item.ItemId].HandleClick();
};
popupMenu.Show();
}
private void ShowPopup(int resAnchor, string popupKey)
{
ShowPopup(FindViewById(resAnchor), popupKey);
}
private void SetPasswordTypeface(TextView textView)
{
}
private void PopulateText(int viewId, int headerViewId,int resId) {
View header = FindViewById(headerViewId);
TextView tv = (TextView)FindViewById(viewId);
private void PopulateText(int viewId, int containerViewId, int resId)
{
View header = FindViewById(containerViewId);
TextView tv = (TextView) FindViewById(viewId);
header.Visibility = tv.Visibility = ViewStates.Visible;
tv.SetText (resId);
tv.SetText(resId);
}
private void PopulateText(int viewId, int headerViewId, String text)
private void PopulateText(int viewId, int containerViewId, String text)
{
View header = FindViewById(headerViewId);
TextView tv = (TextView)FindViewById(viewId);
View container = FindViewById(containerViewId);
TextView tv = (TextView) FindViewById(viewId);
if (String.IsNullOrEmpty(text))
{
header.Visibility = tv.Visibility = ViewStates.Gone;
container.Visibility = tv.Visibility = ViewStates.Gone;
}
else
{
header.Visibility = tv.Visibility = ViewStates.Visible;
container.Visibility = tv.Visibility = ViewStates.Visible;
tv.Text = text;
}
}
void RequiresRefresh ()
private void PopulateStandardText(int viewId, int containerViewId, String key)
{
Intent ret = new Intent ();
ret.PutExtra (KeyRefreshPos, _pos);
PopulateText(viewId, containerViewId, Entry.Strings.ReadSafe(key));
_stringViews.Add(key, new StandardStringView(viewId, containerViewId, this));
}
private void SetPasswordStyle() {
private void RequiresRefresh()
{
Intent ret = new Intent();
ret.PutExtra(KeyRefreshPos, _pos);
}
private void SetPasswordStyle()
{
foreach (TextView password in _protectedTextViews)
{
@ -330,12 +782,13 @@ namespace keepass2android
}
}
}
protected override void OnResume()
{
base.OnResume();
}
/// <summary>
/// brings up a dialog asking the user whether he wants to add the given URL to the entry for automatic finding
/// </summary>
@ -344,24 +797,50 @@ namespace keepass2android
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.SetTitle(GetString(Resource.String.AddUrlToEntryDialog_title));
builder.SetMessage(GetString(Resource.String.AddUrlToEntryDialog_text, new Java.Lang.Object[] { url } ));
builder.SetMessage(GetString(Resource.String.AddUrlToEntryDialog_text, new Java.Lang.Object[] {url}));
builder.SetPositiveButton(GetString(Resource.String.yes), (dlgSender, dlgEvt) =>
{
});
builder.SetNegativeButton(GetString(Resource.String.no), (dlgSender, dlgEvt) =>
{
CompleteOnCreate();
});
{
CompleteOnCreate();
});
Dialog dialog = builder.Create();
dialog.Show();
}
public void ToggleVisibility()
{
_showPassword = !_showPassword;
SetPasswordStyle();
UpdateTogglePasswordMenu();
}
public Android.Net.Uri WriteBinaryToFile(string key, bool writeToCacheDirectory)
{
return Android.Net.Uri.Empty;
//TODO
}
private void UpdateTogglePasswordMenu()
{
//todo use real method
}
public void GotoUrl()
{
//TODO
}
public void OpenBinaryFile(Uri newUri)
{
Toast.MakeText(this, "opening file TODO", ToastLength.Short).Show();
}
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using Android.Views;
using Android.Widget;
namespace keepass2android
{
internal class ExtraStringView : IStringView
{
private readonly View _container;
private readonly TextView _valueView;
private readonly TextView _keyView;
public ExtraStringView(LinearLayout container, TextView valueView, TextView keyView)
{
_container = container;
_valueView = valueView;
_keyView = keyView;
}
public View View
{
get { return _container; }
}
public string Text
{
get { return _valueView.Text; }
set
{
if (String.IsNullOrEmpty(value))
{
_valueView.Visibility = ViewStates.Gone;
_keyView.Visibility = ViewStates.Gone;
}
else
{
_valueView.Visibility = ViewStates.Visible;
_keyView.Visibility = ViewStates.Visible;
_valueView.Text = value;
}
}
}
}
}

View File

@ -0,0 +1,112 @@
using System;
using Android.Content;
using Android.Graphics.Drawables;
using KeePassLib;
using PluginHostTest;
namespace keepass2android
{
internal interface IPopupMenuItem
{
Drawable Icon { get; }
String Text { get; }
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

@ -0,0 +1,7 @@
namespace keepass2android
{
internal interface IStringView
{
string Text { set; get; }
}
}

View File

@ -0,0 +1,37 @@
using Android.Graphics.Drawables;
using PluginHostTest;
namespace keepass2android
{
internal class OpenBinaryPopupItem : IPopupMenuItem
{
private readonly string _key;
private readonly EntryActivity _entryActivity;
public OpenBinaryPopupItem(string key, EntryActivity entryActivity)
{
_key = key;
_entryActivity = entryActivity;
}
public Drawable Icon
{
get { return _entryActivity.Resources.GetDrawable(Android.Resource.Drawable.IcMenuShare); }
}
public string Text
{
get { return _entryActivity.Resources.GetString(Resource.String.SaveAttachmentDialog_open); }
}
public void HandleClick()
{
Android.Net.Uri newUri = _entryActivity.WriteBinaryToFile(_key, true);
if (newUri != null)
{
_entryActivity.OpenBinaryFile(newUri);
}
}
}
}

View File

@ -0,0 +1,14 @@
using Android.Content;
using Android.Graphics.Drawables;
namespace keepass2android
{
class PluginMenuOption
{
public string DisplayText { get; set; }
public Drawable Icon { get; set; }
public Intent Intent { get; set; }
}
}

View File

@ -0,0 +1,47 @@
using Android.Content;
using Android.Graphics.Drawables;
using Android.OS;
using Keepass2android.Pluginsdk;
namespace keepass2android
{
class PluginPopupMenuItem : IPopupMenuItem
{
private readonly Context _ctx;
private readonly string _pluginPackage;
private readonly string _fieldId;
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)
{
_ctx = ctx;
_pluginPackage = pluginPackage;
_fieldId = fieldId;
_displayText = displayText;
_iconId = iconId;
_bundleExtra = bundleExtra;
}
public Drawable Icon
{
get { return _ctx.PackageManager.GetResourcesForApplication(_pluginPackage).GetDrawable(_iconId); }
}
public string Text
{
get { return _displayText; }
}
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);
_ctx.SendBroadcast(i);
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using Android.App;
using Android.Views;
using Android.Widget;
namespace keepass2android
{
internal class StandardStringView : IStringView
{
private readonly int _viewId;
private readonly int _containerViewId;
private readonly Activity _activity;
public StandardStringView(int viewId, int containerViewId, Activity activity)
{
_viewId = viewId;
_containerViewId = containerViewId;
_activity = activity;
}
public string Text
{
set
{
View container = _activity.FindViewById(_containerViewId);
TextView tv = (TextView) _activity.FindViewById(_viewId);
if (String.IsNullOrEmpty(value))
{
container.Visibility = tv.Visibility = ViewStates.Gone;
}
else
{
container.Visibility = tv.Visibility = ViewStates.Visible;
tv.Text = value;
}
}
get
{
TextView tv = (TextView) _activity.FindViewById(_viewId);
return tv.Text;
}
}
}
}

View File

@ -0,0 +1,32 @@
using Android.Graphics.Drawables;
using PluginHostTest;
namespace keepass2android
{
internal class WriteBinaryToFilePopupItem : IPopupMenuItem
{
private readonly string _key;
private readonly EntryActivity _activity;
public WriteBinaryToFilePopupItem(string key, EntryActivity activity)
{
_key = key;
_activity = activity;
}
public Drawable Icon
{
get { return _activity.Resources.GetDrawable(Android.Resource.Drawable.IcMenuSave); }
}
public string Text
{
get { return _activity.Resources.GetString(Resource.String.SaveAttachmentDialog_save); }
}
public void HandleClick()
{
_activity.WriteBinaryToFile(_key, false);
}
}
}

View File

@ -1,3 +1,6 @@
using Android.Content.PM;
using Android.Content.Res;
using Android.Graphics.Drawables;
using Android.Widget;
using Android.Content;
using Android.Views;
@ -11,26 +14,32 @@ namespace keepass2android
public class PluginItem
{
private readonly string _package;
private readonly Context _ctx;
private readonly Resources _pluginRes;
public PluginItem(string package, string _label, int _icon, string _version, string _enabledStatus)
public PluginItem(string package, string enabledStatus, Context ctx)
{
_package = package;
Label = _label;
Icon = _icon;
Version = _version;
EnabledStatus = _enabledStatus;
_ctx = ctx;
EnabledStatus = enabledStatus;
_pluginRes = _ctx.PackageManager.GetResourcesForApplication(_package);
}
public string Label
{
get;
set;
get
{
return PluginDetailsActivity.GetStringFromPlugin(_pluginRes, _package, "kp2aplugin_title");
}
}
public string Version
{
get;
set;
get
{
return _ctx.PackageManager.GetPackageInfo(_package, 0).VersionName;
}
}
public string EnabledStatus
@ -39,10 +48,12 @@ namespace keepass2android
set;
}
public int Icon
public Drawable Icon
{
get;
set;
get
{
return _ctx.PackageManager.GetApplicationIcon(_package);
}
}
public string Package
@ -104,7 +115,7 @@ namespace keepass2android
holder.txtTitle.Text = item.Label;
holder.txtVersion.Text = item.Version;
holder.txtEnabledStatus.Text = item.EnabledStatus;
holder.imgIcon.SetImageResource(item.Icon);
holder.imgIcon.SetImageDrawable(item.Icon);
return row;
}

View File

@ -125,6 +125,18 @@ namespace keepass2android
public bool IsValidAccessToken(string pluginPackage, string accessToken, string scope)
{
if (pluginPackage == null)
{
Log.Warn(_tag, "No pluginPackage specified!");
return false;
}
if (accessToken == null)
{
Log.Warn(_tag, "No accessToken specified!");
return false;
}
var prefs = GetPreferencesForPlugin(pluginPackage);
if (prefs.GetString(_accessToken, null) != accessToken)
{

View File

@ -96,7 +96,7 @@ namespace PluginHostTest
FindViewById<TextView>(resourceId).Visibility = ViewStates.Gone;
}
private static string GetStringFromPlugin(Resources pluginRes, string pluginPackage, string stringId)
public static string GetStringFromPlugin(Resources pluginRes, string pluginPackage, string stringId)
{
int titleId = pluginRes.GetIdentifier(pluginPackage + ":string/"+stringId, null, null);
string title = null;

View File

@ -1,8 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Android.App;
using Android.Content;
using Android.Content.PM;
@ -11,9 +12,12 @@ using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using KeePassLib;
using KeePassLib.Serialization;
using KeePassLib.Utility;
using Keepass2android;
using Keepass2android.Pluginsdk;
using Org.Json;
using PluginHostTest;
namespace keepass2android
@ -26,7 +30,7 @@ namespace keepass2android
private const string _tag = "KP2A_PluginHost";
private static readonly string[] _validScopes = { Strings.ScopeDatabaseActions };
private static readonly string[] _validScopes = { Strings.ScopeDatabaseActions, Strings.ScopeCurrentEntry };
public static void TriggerRequests(Context ctx)
{
@ -138,6 +142,34 @@ namespace keepass2android
return true;
}
public static void AddEntryToIntent(Intent intent, PwEntry entry)
{
/*//add the entry XML
not yet implemented. What to do with attachments?
MemoryStream memStream = new MemoryStream();
KdbxFile.WriteEntries(memStream, new[] {entry});
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;
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);
var jsonStr = json.ToString();
intent.PutExtra(Strings.ExtraCompiledEntryData, jsonStr);
intent.PutExtra(Strings.ExtraEntryId, entry.Uuid.ToHexString());
}
}
}

View File

@ -56,24 +56,38 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Activity1.cs" />
<Compile Include="App.cs" />
<Compile Include="ClickView.cs" />
<Compile Include="CopyToClipboardService.cs" />
<Compile Include="EntryActivity.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="Kp2aShortHelpView.cs" />
<Compile Include="EntryActivityClasses\OpenBinaryPopupItem.cs" />
<Compile Include="PluginDatabase.cs" />
<Compile Include="PluginDetailsActivity.cs" />
<Compile Include="PluginArrayAdapter.cs" />
<Compile Include="PluginListActivity.cs" />
<Compile Include="PluginHost.cs" />
<Compile Include="EntryActivityClasses\PluginMenuOption.cs" />
<Compile Include="EntryActivityClasses\PluginPopupMenuItem.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SprCompileFlags.cs" />
<Compile Include="SprContext.cs" />
<Compile Include="SprEngine.cs" />
<Compile Include="EntryActivityClasses\StandardStringView.cs" />
<Compile Include="TextDrawable.cs" />
<Compile Include="TextViewSelect.cs" />
<Compile Include="TextWithHelp.cs" />
<Compile Include="EntryActivityClasses\WriteBinaryToFilePopupItem.cs" />
</ItemGroup>
<ItemGroup>
<TransformFile Include="Assets\DejaVuSansMono.ttf" />
<TransformFile Include="Assets\fontawesome-webfont.ttf" />
<AndroidAsset Include="Assets\DejaVuSansMono.ttf" />
<AndroidAsset Include="Assets\fontawesome-webfont.ttf" />
<None Include="Resources\AboutResources.txt" />
<None Include="Assets\AboutAssets.txt" />
<AndroidResource Include="Resources\Layout\plugin_list.xml">
@ -93,6 +107,10 @@
<TransformFile Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\KeePassLib2Android\KeePassLib2Android.csproj">
<Project>{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}</Project>
<Name>KeePassLib2Android</Name>
</ProjectReference>
<ProjectReference Include="..\PluginSdkBinding\PluginSdkBinding.csproj">
<Project>{3da3911e-36de-465e-8f15-f1991b6437e5}</Project>
<Name>PluginSdkBinding</Name>
@ -567,6 +585,18 @@
<ItemGroup>
<AndroidResource Include="Resources\Layout\entry_view.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\Drawable\vdots_holodark.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\Drawable\vdots.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\Drawable\ic_menu_copy_holo_light.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\Menu\entry.xml" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -14,6 +14,7 @@ namespace keepass2android
public class PluginListActivity : ListActivity
{
private PluginArrayAdapter _pluginArrayAdapter;
private List<PluginItem> _items;
protected override void OnCreate(Bundle bundle)
{
@ -22,31 +23,35 @@ namespace keepass2android
//TODO _design.ApplyTheme();
SetContentView(Resource.Layout.plugin_list);
ListView listView = FindViewById<ListView>(Android.Resource.Id.List);
listView.ItemClick +=
(sender, args) =>
{
Intent i = new Intent(this, typeof(PluginDetailsActivity));
i.PutExtra("PluginPackage", _items[args.Position].Package);
StartActivity(i);
};
// Create your application here
}
protected override void OnResume()
{
base.OnResume();
PluginDatabase pluginDb = new PluginDatabase(this);
List<PluginItem> items = (from pluginPackage in pluginDb.GetAllPluginPackages()
let version = PackageManager.GetPackageInfo(pluginPackage, 0).VersionName
let enabledStatus = pluginDb.IsEnabled(pluginPackage) ? GetString(Resource.String.plugin_enabled) : GetString(Resource.String.plugin_disabled)
select new PluginItem(pluginPackage, "the plugin", Resource.Drawable.Icon, version, enabledStatus)).ToList();
_items = (from pluginPackage in pluginDb.GetAllPluginPackages()
let version = PackageManager.GetPackageInfo(pluginPackage, 0).VersionName
let enabledStatus = pluginDb.IsEnabled(pluginPackage) ? GetString(Resource.String.plugin_enabled) : GetString(Resource.String.plugin_disabled)
select new PluginItem(pluginPackage, enabledStatus, this)).ToList();
/*
{
new PluginItem("PluginA", Resource.Drawable.Icon, "keepass2android.plugina", "connected"),
new PluginItem("KeepassNFC", Resource.Drawable.Icon, "com.bla.blubb.plugina", "disconnected")
};
* */
_pluginArrayAdapter = new PluginArrayAdapter(this, Resource.Layout.ListViewPluginRow, items);
_pluginArrayAdapter = new PluginArrayAdapter(this, Resource.Layout.ListViewPluginRow, _items);
ListAdapter = _pluginArrayAdapter;
ListView listView = FindViewById<ListView>(Android.Resource.Id.List);
listView.ItemClick +=
(sender, args) =>
{
Intent i = new Intent(this, typeof(PluginDetailsActivity));
i.PutExtra("PluginPackage", items[args.Position].Package);
StartActivity(i);
};
// Create your application here
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

View File

@ -1,7 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Title"
android:layout_marginLeft="10dp"
style="@style/ExtraFieldHeader" />
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/extra_title_container"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/entry_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Title"
style="@style/EntryFieldHeader" />
</LinearLayout>

View File

@ -1,8 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:typeface="monospace"
android:text="Value"
android:layout_marginLeft="30dp"
style="@style/EntryItem" />
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/extra_container"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="horizontal"
android:clickable="true"
android:background="?android:attr/selectableItemBackground">
<ImageView
android:id="@+id/extra_vdots"
android:layout_width="wrap_content"
android:layout_height="15dp"
android:src="@drawable/vdots"
android:gravity="right|bottom"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
/>
<TextView
android:id="@+id/entry_extra"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:typeface="monospace"
android:layout_toLeftOf="@id/extra_vdots"
style="@style/EntryItem" />
</RelativeLayout>

View File

@ -5,162 +5,260 @@
android:layout_width="fill_parent"
android:orientation="vertical">
<!-- Username -->
<TextView
android:id="@+id/entry_user_name_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_user_name"
style="@style/EntryFieldHeader"
<LinearLayout android:id="@+id/entryfield_container_username"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="vertical">
<TextView
android:id="@+id/entry_user_name_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_user_name"
style="@style/EntryFieldHeader"
/>
<TextView
android:id="@+id/entry_user_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
style="@style/EntryItem" />
<!-- URL -->
<TextView
android:id="@+id/entry_url_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_url"
style="@style/EntryFieldHeader"
/>
<TextView
android:id="@+id/entry_url"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:autoLink="all"
android:textIsSelectable="true"
style="@style/EntryItem" />
<!-- Password -->
<TextView
android:id="@+id/entry_password_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_password"
style="@style/EntryFieldHeader"
/>
<TextView
android:id="@+id/entry_password"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:password="true"
android:textIsSelectable="true"
android:typeface="monospace"
style="@style/EntryItem" />
<!-- Comment -->
<TextView
android:id="@+id/entry_comment_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_comment"
style="@style/EntryFieldHeader"
/>
<TextView
android:id="@+id/entry_comment"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
style="@style/EntryItem" />
<!-- Username -->
<RelativeLayout android:id="@+id/username_container"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="horizontal"
android:clickable="true"
android:background="?android:attr/selectableItemBackground">
<TextView
android:id="@+id/entry_extra_strings_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_extra_strings"
style="@style/EntryFieldHeader"
/>
<LinearLayout
android:id="@+id/extra_strings"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:orientation="vertical" />
<!-- file attachments -->
<TextView
android:id="@+id/entry_binaries_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_binaries"
style="@style/EntryFieldHeader"
/>
<LinearLayout
android:id="@+id/binaries"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<!--Tags -->
<TextView
android:id="@+id/entry_tags_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_tags"
style="@style/EntryFieldHeader"
/>
<TextView
android:id="@+id/entry_tags"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
style="@style/EntryItem" />
<!--Override URL-->
<TextView
android:id="@+id/entry_override_url_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_override_url"
style="@style/EntryFieldHeader"
/>
<TextView
android:id="@+id/entry_override_url"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:autoLink="all"
android:textIsSelectable="true"
style="@style/EntryItem" />
<ImageView
android:id="@+id/username_vdots"
android:layout_width="wrap_content"
android:layout_height="15dp"
android:src="@drawable/vdots"
android:gravity="right|bottom"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
/>
<TextView
android:id="@+id/entry_user_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/username_vdots"
style="@style/EntryItem" />
</RelativeLayout>
</LinearLayout>
<!-- Created -->
<TextView
android:id="@+id/entry_created_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_created"
style="@style/EntryFieldHeader"
<LinearLayout android:id="@+id/entryfield_container_url"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="vertical">
<!-- URL -->
<TextView
android:id="@+id/entry_url_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_url"
style="@style/EntryFieldHeader"
/>
<RelativeLayout android:id="@+id/url_container"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="horizontal"
android:clickable="true"
android:background="?android:attr/selectableItemBackground">
<ImageView
android:id="@+id/url_vdots"
android:layout_width="wrap_content"
android:layout_height="15dp"
android:src="@drawable/vdots"
android:gravity="right|bottom"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
/>
<TextView
android:id="@+id/entry_url"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:autoLink="all"
android:layout_toLeftOf="@id/url_vdots"
style="@style/EntryItem" />
</RelativeLayout>
</LinearLayout>
<LinearLayout android:id="@+id/entryfield_container_password"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="vertical">
<!-- Password -->
<TextView
android:id="@+id/entry_password_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_password"
style="@style/EntryFieldHeader"
/>
<TextView
android:id="@+id/entry_created"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="@style/EntryItem" />
<!-- Modified -->
<TextView
android:id="@+id/entry_modified_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_modified"
style="@style/EntryFieldHeader"
<RelativeLayout android:id="@+id/password_container"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="horizontal"
android:clickable="true"
android:background="?android:attr/selectableItemBackground">
<ImageView
android:id="@+id/password_vdots"
android:layout_width="wrap_content"
android:layout_height="15dp"
android:src="@drawable/vdots"
android:gravity="right|bottom"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
/>
<TextView
android:id="@+id/entry_password"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:password="true"
android:typeface="monospace"
android:layout_toLeftOf="@id/password_vdots"
style="@style/EntryItem" />
</RelativeLayout>
</LinearLayout>
<LinearLayout android:id="@+id/entryfield_container_comment"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="vertical">
<!-- Comment -->
<TextView
android:id="@+id/entry_comment_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_comment"
style="@style/EntryFieldHeader"
/>
<TextView
android:id="@+id/entry_modified"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="@style/EntryItem" />
<!-- Expires -->
<TextView
android:id="@+id/entry_expires_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_expires"
style="@style/EntryFieldHeader"
<TextView
android:id="@+id/entry_comment"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
style="@style/EntryItem" />
</LinearLayout>
<LinearLayout
android:id="@+id/extra_strings"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="vertical" />
<!-- file attachments -->
<TextView
android:id="@+id/entry_binaries_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_binaries"
style="@style/EntryFieldHeader"
/>
<TextView
android:id="@+id/entry_expires"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="@style/EntryItem" />
<!-- Property Change Conflict | id:@+id/entry_url_label -->
<LinearLayout
android:id="@+id/binaries"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<!--Tags -->
<LinearLayout android:id="@+id/entryfield_container_tags"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="vertical">
<TextView
android:id="@+id/entry_tags_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_tags"
style="@style/EntryFieldHeader"
/>
<TextView
android:id="@+id/entry_tags"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
style="@style/EntryItem" />
</LinearLayout>
<LinearLayout android:id="@+id/entryfield_container_overrideurl"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="vertical">
<!--Override URL-->
<TextView
android:id="@+id/entry_override_url_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_override_url"
style="@style/EntryFieldHeader"
/>
<TextView
android:id="@+id/entry_override_url"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:autoLink="all"
android:textIsSelectable="true"
style="@style/EntryItem" />
</LinearLayout>
<LinearLayout android:id="@+id/entryfield_container_created"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="vertical">
<!-- Created -->
<TextView
android:id="@+id/entry_created_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_created"
style="@style/EntryFieldHeader"
/>
<TextView
android:id="@+id/entry_created"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="@style/EntryItem" />
</LinearLayout>
<LinearLayout android:id="@+id/entryfield_container_modified"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="vertical">
<!-- Modified -->
<TextView
android:id="@+id/entry_modified_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_modified"
style="@style/EntryFieldHeader"
/>
<TextView
android:id="@+id/entry_modified"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="@style/EntryItem" />
</LinearLayout>
<LinearLayout android:id="@+id/entryfield_container_expires"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="vertical">
<!-- Expires -->
<TextView
android:id="@+id/entry_expires_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/entry_expires"
style="@style/EntryFieldHeader"
/>
<TextView
android:id="@+id/entry_expires"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="@style/EntryItem" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Keepass2Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_donate"
android:icon="@android:drawable/ic_menu_share"
android:title="@string/menu_donate"
/>
<item android:id="@+id/menu_toggle_pass"
android:icon="@android:drawable/ic_menu_view"
android:title="@string/show_password"
/>
<item android:id="@+id/menu_goto_url"
android:icon="@android:drawable/ic_menu_upload"
android:title="@string/menu_url"
/>
<!--<item android:id="@+id/menu_copy_user"
android:icon="@android:drawable/ic_menu_set_as"
android:title="@string/menu_copy_user"
/>
<item android:id="@+id/menu_copy_pass"
android:icon="@android:drawable/ic_menu_agenda"
android:title="@string/menu_copy_pass"
/>-->
<item android:id="@+id/menu_lock"
android:icon="@android:drawable/ic_lock_lock"
android:title="@string/menu_lock"
/>
<item android:id="@+id/menu_suggest_improvements"
android:icon="@android:drawable/ic_menu_directions"
android:title="@string/suggest_improvements"
/>
<item android:id="@+id/menu_rate"
android:icon="@android:drawable/star_off"
android:title="@string/rate_app"
/>
<item android:id="@+id/menu_translate"
android:title="@string/translate_app"
/>
</menu>

View File

@ -54,7 +54,7 @@
<item name="android:layout_marginRight">12dip</item>
<item name="android:paddingLeft">4dp</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
<item name="android:textSize">18sp</item>
<item name="android:textSize">16sp</item>
</style>
<style name="EntryFieldHeader">
@ -63,11 +63,11 @@
<item name="android:layout_marginLeft">12dip</item>
<item name="android:layout_marginRight">12dip</item>
<item name="android:layout_marginBottom">3dp</item>
<item name="android:layout_marginTop">8dp</item>
<item name="android:layout_marginTop">14dp</item>
<item name="android:paddingLeft">4dp</item>
<item name="android:textAllCaps">true</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textSize">18sp</item>
<item name="android:textSize">16sp</item>
<item name="android:textStyle">bold</item>
</style>

View File

@ -8,6 +8,6 @@
<string name="SCOPE_DATABASE_ACTIONS_explanation">Plugin will be notified when a database is opened, closed or saved.</string>
<string name="SCOPE_CURRENT_ENTRY_title">Current entry data</string>
<string name="SCOPE_CURRENT_ENTRY_explanation">Plugin will receive all data about the current database entry and will be allowed to offer actions on it.</string>
<string name="SCOPE_CURRENT_ENTRY_explanation">Plugin will receive all data about the current database entry and will be allowed to offer actions and modify the display of it.</string>
</resources>

View File

@ -0,0 +1,7 @@
namespace keepass2android
{
public enum SprCompileFlags
{
All
}
}

View File

@ -0,0 +1,13 @@
using System;
using KeePassLib;
namespace keepass2android
{
public class SprContext
{
public SprContext(PwEntry entry, PwDatabase kpDatabase, object all)
{
}
}
}

View File

@ -0,0 +1,11 @@
namespace keepass2android
{
public class SprEngine
{
public static string Compile(string value, SprContext sprContext)
{
return value;
}
}
}

View File

@ -0,0 +1,56 @@
using System;
using Android.Content;
using Android.Graphics;
using Android.Graphics.Drawables;
namespace keepass2android
{
/// <summary>
/// Shows text as a drawable.
/// </summary>
/// Based on http://stackoverflow.com/questions/3972445/how-to-put-text-in-a-drawable
public class TextDrawable: Drawable {
private readonly String _text;
private readonly Paint _paint;
private static Typeface _iconFont;
public TextDrawable(String text, Context ctx) {
_text = text;
if (_iconFont == null)
_iconFont = Typeface.CreateFromAsset(ctx.Assets, "fontawesome-webfont.ttf");
_paint = new Paint {Color = (Color.White), TextSize = 22f, AntiAlias = true};
//_paint.SetTypeface(_iconFont);
_paint.SetShadowLayer(6f, 0, 0, Color.Black);
_paint.SetStyle(Paint.Style.Fill);
_paint.TextAlign = Paint.Align.Left;
}
public override void Draw(Canvas canvas) {
canvas.DrawText("x"+_text, 0, 0, _paint);
}
public override void SetAlpha(int alpha) {
_paint.Alpha = alpha;
}
public override void SetColorFilter(ColorFilter cf)
{
_paint.SetColorFilter(cf);
}
public override int Opacity
{
get { return -3; /*translucent*/ }
}
}
}

View File

@ -1,5 +1,7 @@
package keepass2android.pluginsdk;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import org.json.JSONArray;
@ -10,10 +12,15 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.PopupMenu;
public class AccessManager
{
private static final String _tag = "Kp2aPluginSDK";
private static final String PREF_KEY_SCOPE = "scope";
private static final String PREF_KEY_TOKEN = "token";
@ -49,19 +56,40 @@ public class AccessManager
public static void storeAccessToken(Context ctx, String hostPackage, String accessToken, ArrayList<String> scopes)
{
SharedPreferences prefs = getPrefsForHost(ctx, hostPackage);
//
if (accessToken.equals(prefs.getString(PREF_KEY_TOKEN, "")))
{
//token already available
return;
}
Editor edit = prefs.edit();
edit.putString(PREF_KEY_TOKEN, accessToken);
edit.putString(PREF_KEY_SCOPE, stringArrayToString(scopes));
String scopesString = stringArrayToString(scopes);
edit.putString(PREF_KEY_SCOPE, scopesString);
edit.commit();
Log.d(_tag, "stored access token " + accessToken.substring(0, 4)+"... for "+scopes.size()+" scopes ("+scopesString+").");
}
public static void preparePopup(Object popupMenu)
{
try
{
Field[] fields = popupMenu.getClass().getDeclaredFields();
for (Field field : fields) {
if ("mPopup".equals(field.getName())) {
field.setAccessible(true);
Object menuPopupHelper = field.get(popupMenu);
Class<?> classPopupHelper = Class.forName(menuPopupHelper
.getClass().getName());
Method setForceIcons = classPopupHelper.getMethod(
"setForceShowIcon", boolean.class);
setForceIcons.invoke(menuPopupHelper, true);
break;
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
private static SharedPreferences getPrefsForHost(Context ctx,
@ -72,14 +100,22 @@ public class AccessManager
public static String tryGetAccessToken(Context ctx, String hostPackage, ArrayList<String> scopes) {
if (TextUtils.isEmpty(hostPackage))
{
Log.d(_tag, "hostPackage is empty!");
return null;
}
SharedPreferences prefs = getPrefsForHost(ctx, hostPackage);
ArrayList<String> currentScope = stringToStringArray(prefs.getString(PREF_KEY_SCOPE, ""));
String scopesString = prefs.getString(PREF_KEY_SCOPE, "");
Log.d(_tag, "scopes: "+ scopesString);
ArrayList<String> currentScope = stringToStringArray(scopesString);
if (isSubset(scopes, currentScope))
{
return prefs.getString(PREF_KEY_TOKEN, null);
}
else
{
Log.d(_tag, "looks like scope changed. Access token invalid.");
return null;
}
}
@ -88,7 +124,10 @@ public class AccessManager
ArrayList<String> availableScopes) {
for (String r: requiredScopes){
if (availableScopes.indexOf(r)<0)
{
Log.d(_tag, "Scope "+r+" not available. "+availableScopes.size());
return false;
}
}
return true;
}
@ -106,4 +145,15 @@ public class AccessManager
}
}
/**
* Returns a valid access token or throws PluginAccessException
*/
public static String getAccessToken(Context context, String hostPackage,
ArrayList<String> scopes) throws PluginAccessException {
String accessToken = tryGetAccessToken(context, hostPackage, scopes);
if (accessToken == null)
throw new PluginAccessException(hostPackage, scopes);
return accessToken;
}
}

View File

@ -34,6 +34,8 @@ public abstract class PluginAccessBroadcastReceiver extends BroadcastReceiver {
public void onReceive(Context ctx, Intent intent) {
String action = intent.getAction();
android.util.Log.d("KP2A.pluginsdk", "received broadcast with action="+action);
if (action == null)
return;
if (action.equals(Strings.ACTION_TRIGGER_REQUEST_ACCESS))
{
requestAccess(ctx, intent);

View File

@ -1,19 +1,114 @@
package keepass2android.pluginsdk;
public class Strings {
/**
* Plugin is notified about actions like open/close/update a database.
*/
public static final String SCOPE_DATABASE_ACTIONS = "keepass2android.SCOPE_DATABASE_ACTIONS";
/**
* Plugin is notified when an entry is opened.
*/
public static final String SCOPE_CURRENT_ENTRY = "keepass2android.SCOPE_CURRENT_ENTRY";
/**
* Extra key to transfer a (json serialized) list of scopes
*/
public static final String EXTRA_SCOPES = "keepass2android.EXTRA_SCOPES";
/**
* Extra key for sending the package name of the sender of a broadcast.
* Should be set in every broadcast.
*/
public static final String EXTRA_SENDER = "keepass2android.EXTRA_SENDER";
/**
* Extra key for sending a request token. The request token is passed from
* KP2A to the plugin. It's used in the authorization process.
*/
public static final String EXTRA_REQUEST_TOKEN = "keepass2android.EXTRA_REQUEST_TOKEN";
/**
* Action sent from KP2A to the plugin to indicate that the plugin should request
* access (sending it's scopes)
*/
public static final String ACTION_TRIGGER_REQUEST_ACCESS = "keepass2android.ACTION_TRIGGER_REQUEST_ACCESS";
/**
* Action sent from the plugin to KP2A including the scopes.
*/
public static final String ACTION_REQUEST_ACCESS = "keepass2android.ACTION_REQUEST_ACCESS";
/**
* Action sent from the KP2A to the plugin when the user grants access.
* Will contain an access token.
*/
public static final String ACTION_RECEIVE_ACCESS = "keepass2android.ACTION_RECEIVE_ACCESS";
/**
* Action sent from KP2A to the plugin to indicate that access is not or no longer valid.
*/
public static final String ACTION_REVOKE_ACCESS = "keepass2android.ACTION_REVOKE_ACCESS";
public static final String EXTRA_ACCESS_TOKEN = "EXTRA_ACCESS_TOKEN";
//static final String SCOPE_DATABASE_ACTIONS = "keepass2android.SCOPE_DATABASE_ACTIONS";
/**
* Action sent from KP2A to the plugin to indicate that an entry was opened.
* The Intent contains the full entry data.
*/
public static final String ACTION_OPEN_ENTRY= "keepass2android.ACTION_OPEN_ENTRY";
/**
* Action sent from KP2A to the plugin to indicate that an entry activity was closed.
*/
public static final String ACTION_CLOSE_ENTRY_VIEW= "keepass2android.ACTION_CLOSE_ENTRY_VIEW";
/**
* Extra key for a string containing the GUID of the entry.
*/
public static final String EXTRA_ENTRY_ID= "keepass2android.EXTRA_ENTRY_DATA";
/**
* Json serialized data of the PwEntry (C# class) representing the opened entry.
* currently not implemented.
*/
//public static final String EXTRA_ENTRY_DATA = "keepass2android.EXTRA_ENTRY_DATA";
/**
* Json serialized list of fields, compiled using the database context (i.e. placeholders are replaced already)
*/
public static final String EXTRA_COMPILED_ENTRY_DATA = "keepass2android.EXTRA_COMPILED_ENTRY_DATA";
/**
* Extra key for passing the access token (both ways)
*/
public static final String EXTRA_ACCESS_TOKEN = "keepass2android.EXTRA_ACCESS_TOKEN";
/**
* Action for an intent from the plugin to KP2A to add menu options regarding the currently open entry.
* Requires SCOPE_CURRENT_ENTRY.
*/
public static final String ACTION_ADD_ENTRY_ACTION = "keepass2android.ACTION_ADD_ENTRY_ACTION";
public static final String EXTRA_ACTION_DISPLAY_TEXT = "keepass2android.EXTRA_ACTION_DISPLAY_TEXT";
public static final String EXTRA_ACTION_ICON_RES_ID = "keepass2android.EXTRA_ACTION_ICON_RES_ID";
public static final String EXTRA_FIELD_ID = "keepass2android.EXTRA_FIELD_ID";
/** Extra for ACTION_ADD_ENTRY_ACTION and ACTION_ENTRY_ACTION_SELECTED to pass data specifying the action parameters.*/
public static final String EXTRA_ACTION_DATA = "keepass2android.EXTRA_ACTION_DATA";
/**
* Action for an intent from KP2A to the plugin when an action added with ACTION_ADD_ENTRY_ACTION was selected by the user.
*
*/
public static final String ACTION_ENTRY_ACTION_SELECTED = "keepass2android.ACTION_ENTRY_ACTION_SELECTED";
/**
* Action for an intent from the plugin to KP2A to set (i.e. add or update) a field in the entry.
* May be used to update existing or add new fields at any time while the entry is opened.
*/
public static final String ACTION_SET_ENTRY_FIELD = "keepass2android.ACTION_SET_ENTRY_FIELD";
public static final String EXTRA_FIELD_VALUE = "keepass2android.EXTRA_FIELD_VALUE";
public static final String EXTRA_FIELD_PROTECTED = "keepass2android.EXTRA_FIELD_PROTECTED";
public static final String PREFIX_STRING = "STRING_";
public static final String PREFIX_BINARY = "BINARY_";
}