PluginSDK: Add Action moved to base class. rename classes for simpler binding

This commit is contained in:
Philipp Crocoll
2014-05-30 20:51:07 +02:00
parent 85fb4ba9f8
commit bc99d6e04f
12 changed files with 236 additions and 148 deletions

View File

@@ -25,6 +25,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArtTestApp", "ArtTestApp\Ar
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginSdkBinding", "PluginSdkBinding\PluginSdkBinding.csproj", "{3DA3911E-36DE-465E-8F15-F1991B6437E5}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginSdkBinding", "PluginSdkBinding\PluginSdkBinding.csproj", "{3DA3911E-36DE-465E-8F15-F1991B6437E5}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginTOTP", "PluginTOTP\PluginTOTP.csproj", "{F46F101D-4076-4E0A-9781-105598E2F42A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginHostTest", "PluginHostTest\PluginHostTest.csproj", "{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -284,6 +288,54 @@ Global
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Mixed Platforms.Build.0 = 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|Win32.ActiveCfg = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU {3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Debug|Win32.ActiveCfg = Debug|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Debug|x64.ActiveCfg = Debug|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Release|Any CPU.Build.0 = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Release|Any CPU.Deploy.0 = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Release|Win32.ActiveCfg = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.Release|x64.ActiveCfg = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.ReleaseNoNet|Any CPU.Deploy.0 = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.ReleaseNoNet|Mixed Platforms.Deploy.0 = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{F46F101D-4076-4E0A-9781-105598E2F42A}.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
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -16,7 +16,6 @@ namespace keepass2android
private const string _accessToken = "accessToken"; private const string _accessToken = "accessToken";
private const string _scopes = "scopes"; private const string _scopes = "scopes";
private const string _requesttoken = "requestToken"; private const string _requesttoken = "requestToken";
private const string _pluginlist = "pluginList";
@@ -34,14 +33,7 @@ namespace keepass2android
editor.PutString(_requesttoken, Guid.NewGuid().ToString()); editor.PutString(_requesttoken, Guid.NewGuid().ToString());
editor.Commit(); 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();
}
return prefs; return prefs;
} }
@@ -62,8 +54,7 @@ namespace keepass2android
public IEnumerable<String> GetAllPluginPackages() public IEnumerable<String> GetAllPluginPackages()
{ {
var hostPrefs = GetHostPrefs(); return PluginHost.GetAllPlugins(_ctx);
return hostPrefs.GetStringSet(_pluginlist, new List<string>()).Where(IsPackageInstalled);
} }
public bool IsPackageInstalled(string targetPackage) public bool IsPackageInstalled(string targetPackage)
@@ -88,16 +79,11 @@ namespace keepass2android
public void StorePlugin(string pluginPackage, string accessToken, IList<string> requestedScopes) public void StorePlugin(string pluginPackage, string accessToken, IList<string> requestedScopes)
{ {
ISharedPreferences pluginPrefs = GetPreferencesForPlugin(pluginPackage); ISharedPreferences pluginPrefs = GetPreferencesForPlugin(pluginPackage);
pluginPrefs.Edit()
.PutString(_scopes, AccessManager.StringArrayToString(requestedScopes))
.PutString(_accessToken, accessToken)
.Commit();
}
private ISharedPreferences GetHostPrefs() pluginPrefs.Edit()
{ .PutString(_scopes, AccessManager.StringArrayToString(requestedScopes))
return _ctx.GetSharedPreferences("plugins", FileCreationMode.Private); .PutString(_accessToken, accessToken)
.Commit();
} }
public void SetEnabled(string pluginPackage, bool enabled) public void SetEnabled(string pluginPackage, bool enabled)
@@ -114,7 +100,7 @@ namespace keepass2android
i.PutExtra(Strings.ExtraAccessToken, accessToken); i.PutExtra(Strings.ExtraAccessToken, accessToken);
_ctx.SendBroadcast(i); _ctx.SendBroadcast(i);
StorePlugin(pluginPackage, accessToken, GetPluginScopes( pluginPackage)); StorePlugin(pluginPackage, accessToken, GetPluginScopes(pluginPackage));
} }
else else
{ {
@@ -139,7 +125,7 @@ namespace keepass2android
{ {
Log.Warn(_tag, "No accessToken specified!"); Log.Warn(_tag, "No accessToken specified!");
return false; return false;
} }
var prefs = GetPreferencesForPlugin(pluginPackage); var prefs = GetPreferencesForPlugin(pluginPackage);
if (prefs.GetString(_accessToken, null) != accessToken) if (prefs.GetString(_accessToken, null) != accessToken)
@@ -166,19 +152,18 @@ namespace keepass2android
{ {
GetPreferencesForPlugin(plugin).Edit().Clear().Commit(); GetPreferencesForPlugin(plugin).Edit().Clear().Commit();
} }
GetHostPrefs().Edit().Clear().Commit();
} }
public IEnumerable<string> GetPluginsWithAcceptedScope(string scope) public IEnumerable<string> GetPluginsWithAcceptedScope(string scope)
{ {
return GetAllPluginPackages().Where(plugin => return GetAllPluginPackages().Where(plugin =>
{ {
var prefs = GetPreferencesForPlugin(plugin); var prefs = GetPreferencesForPlugin(plugin);
return (prefs.GetString(_accessToken, null) != null) return (prefs.GetString(_accessToken, null) != null)
&& AccessManager.StringToStringArray(prefs.GetString(_scopes, "")).Contains(scope); && AccessManager.StringToStringArray(prefs.GetString(_scopes, "")).Contains(scope);
}); });
} }
public void ClearPlugin(string plugin) public void ClearPlugin(string plugin)
@@ -186,5 +171,30 @@ namespace keepass2android
var prefs = _ctx.GetSharedPreferences("KP2A.Plugin." + plugin, FileCreationMode.Private); var prefs = _ctx.GetSharedPreferences("KP2A.Plugin." + plugin, FileCreationMode.Private);
prefs.Edit().Clear().Commit(); prefs.Edit().Clear().Commit();
} }
/// <summary>
/// Checks if the given pluginPackage has been granted the requiredScope
/// </summary>
public bool HasAcceptedScope(string pluginPackage, string requiredScope)
{
if (pluginPackage == null)
{
Log.Warn(_tag, "No pluginPackage specified!");
return false;
}
var prefs = GetPreferencesForPlugin(pluginPackage);
if (prefs.GetString(_accessToken, null) == null)
{
Log.Info(_tag, "No access token for " + pluginPackage);
return false;
}
if (!AccessManager.StringToStringArray(prefs.GetString(_scopes, "")).Contains(requiredScope))
{
Log.Info(_tag, "Scope " + requiredScope + " not granted for " + pluginPackage);
return false;
}
return true;
}
} }
} }

View File

@@ -15,14 +15,14 @@ using Android.Views;
using Android.Widget; using Android.Widget;
using Java.Util; using Java.Util;
using Keepass2android.Pluginsdk; using Keepass2android.Pluginsdk;
using keepass2android; using PluginHostTest;
using keepass2android.views; using keepass2android.views;
namespace PluginHostTest namespace keepass2android
{ {
[Activity(Label = "TODO Details")] [Activity(Label = "bla")]
[IntentFilter(new[] { Strings.ActionEditPluginSettings}, [IntentFilter(new[] { Strings.ActionEditPluginSettings },
Label = AppNames.AppName, Label = "bla",
Categories = new[] { Intent.CategoryDefault })] Categories = new[] { Intent.CategoryDefault })]
public class PluginDetailsActivity : Activity public class PluginDetailsActivity : Activity
{ {
@@ -32,7 +32,7 @@ namespace PluginHostTest
protected override void OnCreate(Bundle bundle) protected override void OnCreate(Bundle bundle)
{ {
base.OnCreate(bundle); base.OnCreate(bundle);
_pluginPackageName = Intent.GetStringExtra(Strings.ExtraPluginPackage); _pluginPackageName = Intent.GetStringExtra(Strings.ExtraPluginPackage);
var pluginRes = PackageManager.GetResourcesForApplication(_pluginPackageName); var pluginRes = PackageManager.GetResourcesForApplication(_pluginPackageName);
@@ -40,7 +40,7 @@ namespace PluginHostTest
var author = GetStringFromPlugin(pluginRes, _pluginPackageName, "kp2aplugin_author"); var author = GetStringFromPlugin(pluginRes, _pluginPackageName, "kp2aplugin_author");
var shortDesc = GetStringFromPlugin(pluginRes, _pluginPackageName, "kp2aplugin_shortdesc"); var shortDesc = GetStringFromPlugin(pluginRes, _pluginPackageName, "kp2aplugin_shortdesc");
var version = PackageManager.GetPackageInfo(_pluginPackageName, 0).VersionName; var version = PackageManager.GetPackageInfo(_pluginPackageName, 0).VersionName;
SetContentView(Resource.Layout.plugin_details); SetContentView(Resource.Layout.plugin_details);
if (title != null) if (title != null)
FindViewById<TextView>(Resource.Id.txtLabel).Text = title; FindViewById<TextView>(Resource.Id.txtLabel).Text = title;
@@ -49,13 +49,14 @@ namespace PluginHostTest
SetTextOrHide(Resource.Id.txtShortDesc, shortDesc); SetTextOrHide(Resource.Id.txtShortDesc, shortDesc);
_checkbox = FindViewById<CheckBox>(Resource.Id.cb_enabled); _checkbox = FindViewById<CheckBox>(Resource.Id.cb_enabled);
_checkbox.CheckedChange += delegate { _checkbox.CheckedChange += delegate
new PluginDatabase(this).SetEnabled(_pluginPackageName, _checkbox.Checked); {
}; new PluginDatabase(this).SetEnabled(_pluginPackageName, _checkbox.Checked);
};
Drawable d = PackageManager.GetApplicationIcon(_pluginPackageName); Drawable d = PackageManager.GetApplicationIcon(_pluginPackageName);
FindViewById<ImageView>(Resource.Id.imgIcon).SetImageDrawable(d); FindViewById<ImageView>(Resource.Id.imgIcon).SetImageDrawable(d);
FindViewById<TextView>(Resource.Id.txtVersion).Text = version; FindViewById<TextView>(Resource.Id.txtVersion).Text = version;
//cannot be wrong to update the view when we received an update //cannot be wrong to update the view when we received an update
@@ -72,11 +73,11 @@ namespace PluginHostTest
FindViewById(Resource.Id.deny_button).Visibility = ViewStates.Visible; FindViewById(Resource.Id.deny_button).Visibility = ViewStates.Visible;
FindViewById(Resource.Id.accept_button).Click += delegate(object sender, EventArgs args) FindViewById(Resource.Id.accept_button).Click += delegate(object sender, EventArgs args)
{ {
new PluginDatabase(this).SetEnabled(_pluginPackageName, true); new PluginDatabase(this).SetEnabled(_pluginPackageName, true);
SetResult(Result.Ok); SetResult(Result.Ok);
Finish(); Finish();
}; };
FindViewById(Resource.Id.deny_button).Click += delegate(object sender, EventArgs args) FindViewById(Resource.Id.deny_button).Click += delegate(object sender, EventArgs args)
{ {
@@ -120,10 +121,10 @@ namespace PluginHostTest
string scopeId = scope.Substring("keepass2android.".Length); string scopeId = scope.Substring("keepass2android.".Length);
TextWithHelp help = new TextWithHelp(this, TextWithHelp help = new TextWithHelp(this,
GetString(Resources.GetIdentifier(scopeId + "_title", "string", PackageName)), GetString(Resources.GetIdentifier(scopeId + "_title", "string", PackageName)),
GetString(Resources.GetIdentifier(scopeId + "_explanation", "string", PackageName))); GetString(Resources.GetIdentifier(scopeId + "_explanation", "string", PackageName)));
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FillParent, LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FillParent,
ViewGroup.LayoutParams.WrapContent); ViewGroup.LayoutParams.WrapContent);
help.LayoutParameters = layoutParams; help.LayoutParameters = layoutParams;
scopesContainer.AddView(help); scopesContainer.AddView(help);
} }
@@ -142,7 +143,7 @@ namespace PluginHostTest
public 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); int titleId = pluginRes.GetIdentifier(pluginPackage + ":string/" + stringId, null, null);
string title = null; string title = null;
if (titleId != 0) if (titleId != 0)
title = pluginRes.GetString(titleId); title = pluginRes.GetString(titleId);

View File

@@ -14,33 +14,38 @@ namespace keepass2android
/// <summary> /// <summary>
/// Class which manages plugins inside the app /// Class which manages plugins inside the app
/// </summary> /// </summary>
[BroadcastReceiver()] [BroadcastReceiver]
[IntentFilter(new[] { Strings.ActionRequestAccess})] [IntentFilter(new[] { Strings.ActionRequestAccess })]
public class PluginHost: BroadcastReceiver public class PluginHost : BroadcastReceiver
{ {
private const string _tag = "KP2A_PluginHost";
private static readonly string[] _validScopes = { Strings.ScopeDatabaseActions, Strings.ScopeCurrentEntry }; private const string _tag = "KP2A_PluginHost";
private static readonly string[] _validScopes = { Strings.ScopeDatabaseActions,
Strings.ScopeCurrentEntry,
Strings.ScopeQueryCredentials,
Strings.ScopeQueryCredentialsForOwnPackage};
public static IEnumerable<string> GetAllPlugins(Context ctx)
{
Intent accessIntent = new Intent(Strings.ActionTriggerRequestAccess);
PackageManager packageManager = ctx.PackageManager;
IList<ResolveInfo> dictPacks = packageManager.QueryBroadcastReceivers(
accessIntent, PackageInfoFlags.Receivers);
return dictPacks.Select(ri => ri.ActivityInfo.ApplicationInfo).Select(appInfo => appInfo.PackageName);
}
/// <summary> /// <summary>
/// Sends a broadcast to all potential plugins prompting them to request access to our app. /// Sends a broadcast to all potential plugins prompting them to request access to our app.
/// </summary> /// </summary>
public static void TriggerRequests(Context ctx) public static void TriggerRequests(Context ctx)
{ {
Intent accessIntent = new Intent(Strings.ActionTriggerRequestAccess);
PackageManager packageManager = ctx.PackageManager;
IList<ResolveInfo> dictPacks = packageManager.QueryBroadcastReceivers(
accessIntent, PackageInfoFlags.Receivers);
PluginDatabase pluginDatabase = new PluginDatabase(ctx); PluginDatabase pluginDatabase = new PluginDatabase(ctx);
foreach (ResolveInfo ri in dictPacks) foreach (string pkg in GetAllPlugins(ctx))
{ TriggerRequest(ctx, pkg, pluginDatabase);
ApplicationInfo appInfo = ri.ActivityInfo.ApplicationInfo;
String pkgName = appInfo.PackageName;
TriggerRequest(ctx, pkgName, pluginDatabase);
}
} }
@@ -69,7 +74,7 @@ namespace keepass2android
var senderPackage = intent.GetStringExtra(Strings.ExtraSender); var senderPackage = intent.GetStringExtra(Strings.ExtraSender);
var requestToken = intent.GetStringExtra(Strings.ExtraRequestToken); var requestToken = intent.GetStringExtra(Strings.ExtraRequestToken);
var requestedScopes = intent.GetStringArrayListExtra(Strings.ExtraScopes); var requestedScopes = intent.GetStringArrayListExtra(Strings.ExtraScopes);
if (!AreScopesValid(requestedScopes)) if (!AreScopesValid(requestedScopes))
{ {
@@ -82,9 +87,9 @@ namespace keepass2android
return; return;
} }
string currentAccessToken = pluginDb.GetAccessToken(senderPackage); string currentAccessToken = pluginDb.GetAccessToken(senderPackage);
if ((currentAccessToken != null) if ((currentAccessToken != null)
&& (AccessManager.IsSubset(requestedScopes, && (AccessManager.IsSubset(requestedScopes,
pluginDb.GetPluginScopes(senderPackage)))) pluginDb.GetPluginScopes(senderPackage))))
{ {
//permission already there. //permission already there.
var i = new Intent(Strings.ActionReceiveAccess); var i = new Intent(Strings.ActionReceiveAccess);
@@ -116,10 +121,10 @@ namespace keepass2android
context.SendBroadcast(i); context.SendBroadcast(i);
Log.Warn(_tag, "Access token of plugin " + senderPackage + " not (or no more) valid."); Log.Warn(_tag, "Access token of plugin " + senderPackage + " not (or no more) valid.");
} }
} }
if (OnReceivedRequest != null) if (OnReceivedRequest != null)
OnReceivedRequest(this, new PluginHostEventArgs() { Package = senderPackage}); OnReceivedRequest(this, new PluginHostEventArgs() { Package = senderPackage });
} }
} }
@@ -152,14 +157,16 @@ namespace keepass2android
//add the output string array (placeholders replaced taking into account the db context) //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()); Dictionary<string, string> outputFields = entry.OutputStrings.ToDictionary(pair => StrUtil.SafeXmlString(pair.Key), pair => pair.Value.ReadString());
//add field values as JSON ({ "key":"value", ... } form) JSONObject jsonOutput = new JSONObject(outputFields);
JSONObject json = new JSONObject(outputFields); var jsonOutputStr = jsonOutput.ToString();
var jsonStr = json.ToString(); intent.PutExtra(Strings.ExtraEntryOutputData, jsonOutputStr);
intent.PutExtra(Strings.ExtraEntryOutputData, jsonStr);
//add list of which fields are protected (StringArrayExtra) JSONArray jsonProtectedFields = new JSONArray(
string[] protectedFieldsList = entry.OutputStrings.Where(s=>s.Value.IsProtected).Select(s => s.Key).ToArray(); (System.Collections.ICollection)entry.OutputStrings
intent.PutExtra(Strings.ExtraProtectedFieldsList, protectedFieldsList); .Where(pair => pair.Value.IsProtected)
.Select(pair => pair.Key)
.ToArray());
intent.PutExtra(Strings.ExtraProtectedFieldsList, jsonProtectedFields.ToString());
intent.PutExtra(Strings.ExtraEntryId, entry.Uuid.ToHexString()); intent.PutExtra(Strings.ExtraEntryId, entry.Uuid.ToHexString());

View File

@@ -10,8 +10,8 @@ using PluginHostTest;
namespace keepass2android namespace keepass2android
{ {
//TODO theme? [Activity(Label = "@string/plugins", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden)]
[Activity (Label = "Plugins (TODO)", ConfigurationChanges=ConfigChanges.Orientation|ConfigChanges.KeyboardHidden )] [IntentFilter(new[] { "keepass2android.PluginListActivity" }, Categories = new[] { Intent.CategoryDefault })]
public class PluginListActivity : ListActivity public class PluginListActivity : ListActivity
{ {
private PluginArrayAdapter _pluginArrayAdapter; private PluginArrayAdapter _pluginArrayAdapter;
@@ -20,21 +20,21 @@ namespace keepass2android
protected override void OnCreate(Bundle bundle) protected override void OnCreate(Bundle bundle)
{ {
base.OnCreate(bundle); base.OnCreate(bundle);
//TODO _design.ApplyTheme();
SetContentView(Resource.Layout.plugin_list); SetContentView(Resource.Layout.plugin_list);
PluginHost.TriggerRequests(this);
ListView listView = FindViewById<ListView>(Android.Resource.Id.List); ListView listView = FindViewById<ListView>(Android.Resource.Id.List);
listView.ItemClick += listView.ItemClick +=
(sender, args) => (sender, args) =>
{ {
Intent i = new Intent(this, typeof(PluginDetailsActivity)); Intent i = new Intent(this, typeof(PluginDetailsActivity));
i.PutExtra(Strings.ExtraPluginPackage, _items[args.Position].Package); i.PutExtra(Strings.ExtraPluginPackage, _items[args.Position].Package);
StartActivity(i); StartActivity(i);
}; };
// Create your application here
} }
protected override void OnResume() protected override void OnResume()
{ {
@@ -42,9 +42,9 @@ namespace keepass2android
PluginDatabase pluginDb = new PluginDatabase(this); PluginDatabase pluginDb = new PluginDatabase(this);
_items = (from pluginPackage in pluginDb.GetAllPluginPackages() _items = (from pluginPackage in pluginDb.GetAllPluginPackages()
let version = PackageManager.GetPackageInfo(pluginPackage, 0).VersionName let version = PackageManager.GetPackageInfo(pluginPackage, 0).VersionName
let enabledStatus = pluginDb.IsEnabled(pluginPackage) ? GetString(Resource.String.plugin_enabled) : GetString(Resource.String.plugin_disabled) let enabledStatus = pluginDb.IsEnabled(pluginPackage) ? GetString(Resource.String.plugin_enabled) : GetString(Resource.String.plugin_disabled)
select new PluginItem(pluginPackage, enabledStatus, this)).ToList(); select new PluginItem(pluginPackage, enabledStatus, this)).ToList();
/* /*
{ {
new PluginItem("PluginA", Resource.Drawable.Icon, "keepass2android.plugina", "connected"), new PluginItem("PluginA", Resource.Drawable.Icon, "keepass2android.plugina", "connected"),

View File

@@ -25,7 +25,7 @@
android:layout_gravity="center" android:layout_gravity="center"
android:paddingRight="20dp" android:paddingRight="20dp"
android:drawablePadding="8dp" android:drawablePadding="8dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:text="@string/accept" /> android:text="@string/accept" />
</FrameLayout> </FrameLayout>
<FrameLayout <FrameLayout
@@ -40,7 +40,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:paddingRight="20dp" android:paddingRight="20dp"
android:drawableLeft="?attr/CancelDrawable"
android:drawablePadding="8dp" android:drawablePadding="8dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:text="@string/deny" /> android:text="@string/deny" />
@@ -50,8 +49,9 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/plugin_scroll" android:id="@+id/plugin_scroll"
android:layout_width="wrap_content" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="12dp"
android:scrollbarStyle="insideOverlay"> android:scrollbarStyle="insideOverlay">
<LinearLayout android:id="@+id/scopes_list" <LinearLayout android:id="@+id/scopes_list"
android:layout_width="fill_parent" android:layout_width="fill_parent"

View File

@@ -4,14 +4,15 @@
<LinearLayout <LinearLayout
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical"
<TextView android:padding="12dp">
android:layout_width="wrap_content" <Button
android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge" android:textAppearance="?android:attr/textAppearanceMedium"
android:text="plugin text" android:text="plugin_web"
style="@style/PaddedElement" style="@style/PaddedElement"
android:id="@+id/textView" android:id="@+id/btnPluginsOnline"
android:layout_gravity="left|center_vertical" /> android:layout_gravity="left|center_vertical" />
<ListView <ListView
android:id="@android:id/list" android:id="@android:id/list"

View File

@@ -68,16 +68,42 @@ public abstract class PluginActionBroadcastReceiver extends BroadcastReceiver {
{ {
return _intent.getStringArrayExtra(Strings.EXTRA_PROTECTED_FIELDS_LIST); return _intent.getStringArrayExtra(Strings.EXTRA_PROTECTED_FIELDS_LIST);
} }
public String getEntryId()
{
return _intent.getStringExtra(Strings.EXTRA_ENTRY_ID);
}
public void setEntryField(String fieldId, String fieldValue, boolean isProtected) throws PluginAccessException
{
Intent i = new Intent(Strings.ACTION_SET_ENTRY_FIELD);
ArrayList<String> scope = new ArrayList<String>();
scope.add(Strings.SCOPE_CURRENT_ENTRY);
i.putExtra(Strings.EXTRA_ACCESS_TOKEN, AccessManager.getAccessToken(_context, getHostPackage(), scope));
i.setPackage(getHostPackage());
i.putExtra(Strings.EXTRA_SENDER, _context.getPackageName());
i.putExtra(Strings.EXTRA_FIELD_VALUE, fieldValue);
i.putExtra(Strings.EXTRA_ENTRY_ID, getEntryId());
i.putExtra(Strings.EXTRA_FIELD_ID, fieldId);
i.putExtra(Strings.EXTRA_FIELD_PROTECTED, isProtected);
_context.sendBroadcast(i);
}
} }
protected class ActionSelected extends PluginEntryActionBase protected class ActionSelectedAction extends PluginEntryActionBase
{ {
public ActionSelected(Context ctx, Intent intent) { public ActionSelectedAction(Context ctx, Intent intent) {
super(ctx, intent); super(ctx, intent);
} }
/** /**
* *
* @return the Bundle associated with the action. This bundle can be set in OpenEntry.add(Entry)FieldAction * @return the Bundle associated with the action. This bundle can be set in OpenEntry.add(Entry)FieldAction
@@ -125,9 +151,9 @@ public abstract class PluginActionBroadcastReceiver extends BroadcastReceiver {
} }
} }
protected class CloseEntryView extends PluginEntryActionBase protected class CloseEntryViewAction extends PluginEntryActionBase
{ {
public CloseEntryView(Context context, Intent intent) { public CloseEntryViewAction(Context context, Intent intent) {
super(context, intent); super(context, intent);
} }
@@ -137,18 +163,14 @@ public abstract class PluginActionBroadcastReceiver extends BroadcastReceiver {
} }
} }
protected class OpenEntry extends PluginEntryActionBase protected class OpenEntryAction extends PluginEntryActionBase
{ {
public OpenEntry(Context context, Intent intent) public OpenEntryAction(Context context, Intent intent)
{ {
super(context, intent); super(context, intent);
} }
public String getEntryId()
{
return _intent.getStringExtra(Strings.EXTRA_ENTRY_ID);
}
public HashMap<String, String> getEntryFields() public HashMap<String, String> getEntryFields()
{ {
@@ -187,21 +209,7 @@ public abstract class PluginActionBroadcastReceiver extends BroadcastReceiver {
_context.sendBroadcast(i); _context.sendBroadcast(i);
} }
public void setEntryField(String fieldId, String fieldValue, boolean isProtected) throws PluginAccessException
{
Intent i = new Intent(Strings.ACTION_SET_ENTRY_FIELD);
ArrayList<String> scope = new ArrayList<String>();
scope.add(Strings.SCOPE_CURRENT_ENTRY);
i.putExtra(Strings.EXTRA_ACCESS_TOKEN, AccessManager.getAccessToken(_context, getHostPackage(), scope));
i.setPackage(getHostPackage());
i.putExtra(Strings.EXTRA_SENDER, _context.getPackageName());
i.putExtra(Strings.EXTRA_FIELD_VALUE, fieldValue);
i.putExtra(Strings.EXTRA_ENTRY_ID, getEntryId());
i.putExtra(Strings.EXTRA_FIELD_ID, fieldId);
i.putExtra(Strings.EXTRA_FIELD_PROTECTED, isProtected);
_context.sendBroadcast(i);
}
} }
@@ -231,10 +239,10 @@ public abstract class PluginActionBroadcastReceiver extends BroadcastReceiver {
} }
//EntryOutputModified is very similar to OpenEntry because it receives the same //EntryOutputModified is very similar to OpenEntry because it receives the same
//data (+ the field id which was modified) //data (+ the field id which was modified)
protected class EntryOutputModified extends OpenEntry protected class EntryOutputModifiedAction extends OpenEntryAction
{ {
public EntryOutputModified(Context context, Intent intent) public EntryOutputModifiedAction(Context context, Intent intent)
{ {
super(context, intent); super(context, intent);
} }
@@ -253,26 +261,26 @@ public abstract class PluginActionBroadcastReceiver extends BroadcastReceiver {
return; return;
if (action.equals(Strings.ACTION_OPEN_ENTRY)) if (action.equals(Strings.ACTION_OPEN_ENTRY))
{ {
openEntry(new OpenEntry(ctx, intent)); openEntry(new OpenEntryAction(ctx, intent));
} }
else if (action.equals(Strings.ACTION_CLOSE_ENTRY_VIEW)) else if (action.equals(Strings.ACTION_CLOSE_ENTRY_VIEW))
{ {
closeEntryView(new CloseEntryView(ctx, intent)); closeEntryView(new CloseEntryViewAction(ctx, intent));
} }
else if (action.equals(Strings.ACTION_ENTRY_ACTION_SELECTED)) else if (action.equals(Strings.ACTION_ENTRY_ACTION_SELECTED))
{ {
actionSelected(new ActionSelected(ctx, intent)); actionSelected(new ActionSelectedAction(ctx, intent));
} }
else if (action.equals(Strings.ACTION_ENTRY_OUTPUT_MODIFIED)) else if (action.equals(Strings.ACTION_ENTRY_OUTPUT_MODIFIED))
{ {
entryOutputModified(new EntryOutputModified(ctx, intent)); entryOutputModified(new EntryOutputModifiedAction(ctx, intent));
} }
else if (action.equals(Strings.ACTION_LOCK_DATABASE) else if (action.equals(Strings.ACTION_LOCK_DATABASE)
|| action.equals(Strings.ACTION_UNLOCK_DATABASE) || action.equals(Strings.ACTION_UNLOCK_DATABASE)
|| action.equals(Strings.ACTION_OPEN_DATABASE) || action.equals(Strings.ACTION_OPEN_DATABASE)
|| action.equals(Strings.ACTION_CLOSE_DATABASE)) || action.equals(Strings.ACTION_CLOSE_DATABASE))
{ {
databaseAction(new DatabaseAction(ctx, intent)); dbAction(new DatabaseAction(ctx, intent));
} }
else else
{ {
@@ -282,14 +290,14 @@ public abstract class PluginActionBroadcastReceiver extends BroadcastReceiver {
} }
protected void closeEntryView(CloseEntryView closeEntryView) {} protected void closeEntryView(CloseEntryViewAction closeEntryView) {}
protected void actionSelected(ActionSelected actionSelected) {} protected void actionSelected(ActionSelectedAction actionSelected) {}
protected void openEntry(OpenEntry oe) {} protected void openEntry(OpenEntryAction oe) {}
protected void entryOutputModified(EntryOutputModified eom) {} protected void entryOutputModified(EntryOutputModifiedAction eom) {}
protected void databaseAction(DatabaseAction db) {} protected void dbAction(DatabaseAction db) {}
} }

View File

@@ -10,7 +10,7 @@ import keepass2android.pluginsdk.Strings;
public class ActionReceiver extends PluginActionBroadcastReceiver{ public class ActionReceiver extends PluginActionBroadcastReceiver{
@Override @Override
protected void openEntry(OpenEntry oe) { protected void openEntry(OpenEntryAction oe) {
try { try {
oe.addEntryAction(oe.getContext().getString(R.string.action_show_qr), oe.addEntryAction(oe.getContext().getString(R.string.action_show_qr),
R.drawable.qrcode, null); R.drawable.qrcode, null);
@@ -26,7 +26,7 @@ public class ActionReceiver extends PluginActionBroadcastReceiver{
} }
@Override @Override
protected void actionSelected(ActionSelected actionSelected) { protected void actionSelected(ActionSelectedAction actionSelected) {
Intent i = new Intent(actionSelected.getContext(), QRActivity.class); Intent i = new Intent(actionSelected.getContext(), QRActivity.class);
i.putExtra(Strings.EXTRA_ENTRY_OUTPUT_DATA, new JSONObject(actionSelected.getEntryFields()).toString()); i.putExtra(Strings.EXTRA_ENTRY_OUTPUT_DATA, new JSONObject(actionSelected.getEntryFields()).toString());
i.putExtra(Strings.EXTRA_FIELD_ID, actionSelected.getFieldId()); i.putExtra(Strings.EXTRA_FIELD_ID, actionSelected.getFieldId());
@@ -36,7 +36,7 @@ public class ActionReceiver extends PluginActionBroadcastReceiver{
} }
@Override @Override
protected void entryOutputModified(EntryOutputModified eom) { protected void entryOutputModified(EntryOutputModifiedAction eom) {
try { try {
eom.addEntryFieldAction("keepass2android.plugin.qr.show", eom.getModifiedFieldId(), eom.getContext().getString(R.string.action_show_qr), eom.addEntryFieldAction("keepass2android.plugin.qr.show", eom.getModifiedFieldId(), eom.getContext().getString(R.string.action_show_qr),
R.drawable.qrcode, null); R.drawable.qrcode, null);

View File

@@ -99,6 +99,8 @@ namespace keepass2android
//make sure _timer doesn't go out of scope: //make sure _timer doesn't go out of scope:
private Timer _timer; private Timer _timer;
private PluginActionReceiver _pluginActionReceiver;
private PluginFieldReceiver _pluginFieldReceiver;
protected void SetEntryView() protected void SetEntryView()
@@ -375,8 +377,10 @@ namespace keepass2android
App.Kp2a.GetDb().LastOpenedEntry = new PwEntryOutput(Entry, App.Kp2a.GetDb().KpDatabase); App.Kp2a.GetDb().LastOpenedEntry = new PwEntryOutput(Entry, App.Kp2a.GetDb().KpDatabase);
RegisterReceiver(new PluginActionReceiver(this), new IntentFilter(Strings.ActionAddEntryAction)); _pluginActionReceiver = new PluginActionReceiver(this);
RegisterReceiver(new PluginFieldReceiver(this), new IntentFilter(Strings.ActionSetEntryField)); RegisterReceiver(_pluginActionReceiver, new IntentFilter(Strings.ActionAddEntryAction));
_pluginFieldReceiver = new PluginFieldReceiver(this);
RegisterReceiver(_pluginFieldReceiver, new IntentFilter(Strings.ActionSetEntryField));
new Thread(NotifyPluginsOnOpen).Start(); new Thread(NotifyPluginsOnOpen).Start();
@@ -742,6 +746,10 @@ namespace keepass2android
protected override void OnDestroy() protected override void OnDestroy()
{ {
NotifyPluginsOnClose(); NotifyPluginsOnClose();
if (_pluginActionReceiver != null)
UnregisterReceiver(_pluginActionReceiver);
if (_pluginFieldReceiver != null)
UnregisterReceiver(_pluginFieldReceiver);
base.OnDestroy(); base.OnDestroy();
} }

View File

@@ -56,8 +56,9 @@ namespace keepass2android
Intent triggerIntent = new Intent(Strings.ActionTriggerRequestAccess); Intent triggerIntent = new Intent(Strings.ActionTriggerRequestAccess);
triggerIntent.SetPackage(pkgName); triggerIntent.SetPackage(pkgName);
triggerIntent.PutExtra(Strings.ExtraSender, ctx.PackageName); triggerIntent.PutExtra(Strings.ExtraSender, ctx.PackageName);
string requestToken = pluginDatabase.GetRequestToken(pkgName);
triggerIntent.PutExtra(Strings.ExtraRequestToken, pluginDatabase.GetRequestToken(pkgName)); triggerIntent.PutExtra(Strings.ExtraRequestToken, requestToken);
Android.Util.Log.Debug(_tag, "Request token: " + requestToken);
ctx.SendBroadcast(triggerIntent); ctx.SendBroadcast(triggerIntent);
} }
catch (Exception e) catch (Exception e)