+ pluginhost test project
173
src/PluginHostTest/Activity1.cs
Normal file
@ -0,0 +1,173 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Android.OS;
|
||||
using Keepass2android.Pluginsdk;
|
||||
using keepass2android;
|
||||
|
||||
namespace PluginHostTest
|
||||
{
|
||||
[Activity(Label = "PluginHostTest", MainLauncher = true, Icon = "@drawable/icon")]
|
||||
public class Activity1 : Activity
|
||||
{
|
||||
int count = 1;
|
||||
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
base.OnCreate(bundle);
|
||||
|
||||
// Set our view from the "main" layout resource
|
||||
SetContentView(Resource.Layout.Main);
|
||||
|
||||
// Get our button from the layout resource,
|
||||
// and attach an event to it
|
||||
Button button = FindViewById<Button>(Resource.Id.MyButton);
|
||||
|
||||
button.Click += delegate { PluginHost.TriggerRequests(this); };
|
||||
|
||||
FindViewById<Button>(Resource.Id.managePluginsButton).Click += delegate(object sender, EventArgs args)
|
||||
{
|
||||
StartActivity(new Intent(this, typeof(PluginListActivity)));
|
||||
};
|
||||
FindViewById<Button>(Resource.Id.entryviewButton).Click += delegate
|
||||
{
|
||||
StartActivity(new Intent(this, typeof(EntryActivity)));
|
||||
};
|
||||
|
||||
FindViewById<Button>(Resource.Id.testDbButton).Click += delegate
|
||||
{
|
||||
string message = "ok. ";
|
||||
try
|
||||
{
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
PluginDatabase db = new PluginDatabase(this);
|
||||
db.Clear();
|
||||
|
||||
|
||||
if (db.GetAllPluginPackages().Count() != 0)
|
||||
throw new Exception("db not empty!");
|
||||
|
||||
const string testPackageA = "test.package.a";
|
||||
const string testPackageB = "test.package.b";
|
||||
db.ClearPlugin(testPackageA);
|
||||
db.ClearPlugin(testPackageB);
|
||||
EnsurePackageDataIsEmpty(db, testPackageA);
|
||||
EnsurePackageDataIsEmpty(db, testPackageB);
|
||||
|
||||
string[] requestedScopes = {
|
||||
Strings.ScopeDatabaseActions
|
||||
};
|
||||
db.StorePlugin(testPackageA, null, requestedScopes);
|
||||
EnsurePackageDataIsEmpty(db, testPackageB);
|
||||
EnsurePackageDataIsEmpty(new PluginDatabase(this), testPackageB);
|
||||
db.StorePlugin(testPackageB, null, requestedScopes);
|
||||
EnsurePackageHasUnacceptedScope(db, testPackageA, Strings.ScopeDatabaseActions);
|
||||
EnsurePackageHasUnacceptedScope(db, testPackageB, Strings.ScopeDatabaseActions);
|
||||
EnsurePackageHasUnacceptedScope(new PluginDatabase(this), testPackageA, Strings.ScopeDatabaseActions);
|
||||
|
||||
if (db.GetAllPluginPackages().Count() != 2)
|
||||
throw new Exception("wrong count of plugins");
|
||||
if (db.GetPluginsWithAcceptedScope(Strings.ScopeDatabaseActions).Any())
|
||||
{
|
||||
throw new Exception("wrong count of accepted plugins");
|
||||
}
|
||||
if (new PluginDatabase(this).GetPluginsWithAcceptedScope(Strings.ScopeDatabaseActions).Any())
|
||||
{
|
||||
throw new Exception("wrong count of accepted plugins");
|
||||
}
|
||||
|
||||
db.SetEnabled(testPackageA, true);
|
||||
if (db.GetPluginsWithAcceptedScope(Strings.ScopeDatabaseActions).Single() != testPackageA)
|
||||
{
|
||||
throw new Exception("wrong plugin");
|
||||
}
|
||||
if (new PluginDatabase(this).GetPluginsWithAcceptedScope(Strings.ScopeDatabaseActions).Single() != testPackageA)
|
||||
{
|
||||
throw new Exception("wrong plugin");
|
||||
}
|
||||
if (db.GetPluginsWithAcceptedScope("somescope").Any())
|
||||
{
|
||||
throw new Exception("wrong count of accepted plugins");
|
||||
}
|
||||
var accessTokenA = db.GetAccessToken(testPackageA);
|
||||
if (String.IsNullOrEmpty(accessTokenA))
|
||||
throw new Exception("expected access token!");
|
||||
if (!db.IsEnabled(testPackageA))
|
||||
throw new Exception("plugin not enabled!");
|
||||
if (db.IsEnabled(testPackageB))
|
||||
throw new Exception("plugin enabled!");
|
||||
if (!db.IsValidAccessToken(testPackageA, accessTokenA, Strings.ScopeDatabaseActions))
|
||||
throw new Exception("invalid token!");
|
||||
db.SetEnabled(testPackageA, false);
|
||||
if (db.IsValidAccessToken(testPackageA, accessTokenA, Strings.ScopeDatabaseActions))
|
||||
throw new Exception("valid token?!");
|
||||
if (db.GetPluginsWithAcceptedScope(Strings.ScopeDatabaseActions).Any())
|
||||
throw new Exception("unexpected!");
|
||||
|
||||
|
||||
new PluginDatabase(this).SetEnabled(testPackageB, true);
|
||||
if (!db.IsEnabled(testPackageB))
|
||||
throw new Exception("plugin not enabled!");
|
||||
|
||||
db.SetEnabled(testPackageA, true);
|
||||
accessTokenA = db.GetAccessToken(testPackageA);
|
||||
|
||||
message += sw.ElapsedMilliseconds + "ms";
|
||||
|
||||
Stopwatch swQuery = new Stopwatch();
|
||||
swQuery.Start();
|
||||
int n = 3;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if (db.GetPluginsWithAcceptedScope(Strings.ScopeDatabaseActions).Count() != 2)
|
||||
{
|
||||
throw new Exception("wrong plugin");
|
||||
}
|
||||
if (!db.IsValidAccessToken(testPackageA, accessTokenA, Strings.ScopeDatabaseActions))
|
||||
throw new Exception("invalid token");
|
||||
}
|
||||
message += "/ " + swQuery.ElapsedMilliseconds/(double)n/2.0 + "ms for query";
|
||||
|
||||
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
message = exception.ToString();
|
||||
}
|
||||
Toast.MakeText(this, message, ToastLength.Long).Show();
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void EnsurePackageHasUnacceptedScope(PluginDatabase db, string plugin, string scope)
|
||||
{
|
||||
if (String.IsNullOrEmpty(db.GetRequestToken(plugin)))
|
||||
throw new Exception("invalid request token");
|
||||
if (db.GetAccessToken(plugin) != null)
|
||||
throw new Exception("invalid access token!");
|
||||
if (db.GetPluginScopes(plugin).Count != 1)
|
||||
throw new Exception("Unexpected scopes!");
|
||||
if (db.GetPluginScopes(plugin).First() != scope)
|
||||
throw new Exception("Unexpected scope in db!");
|
||||
}
|
||||
|
||||
private static void EnsurePackageDataIsEmpty(PluginDatabase db, string testPackageA)
|
||||
{
|
||||
if (String.IsNullOrEmpty(db.GetRequestToken(testPackageA)))
|
||||
throw new Exception("invalid request token");
|
||||
if (db.GetAccessToken(testPackageA) != null)
|
||||
throw new Exception("invalid access token!");
|
||||
if (db.GetPluginScopes(testPackageA).Count > 0)
|
||||
throw new Exception("Unexpected scopes!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
19
src/PluginHostTest/Assets/AboutAssets.txt
Normal file
@ -0,0 +1,19 @@
|
||||
Any raw assets you want to be deployed with your application can be placed in
|
||||
this directory (and child directories) and given a Build Action of "AndroidAsset".
|
||||
|
||||
These files will be deployed with you package and will be accessible using Android's
|
||||
AssetManager, like this:
|
||||
|
||||
public class ReadAsset : Activity
|
||||
{
|
||||
protected override void OnCreate (Bundle bundle)
|
||||
{
|
||||
base.OnCreate (bundle);
|
||||
|
||||
InputStream input = Assets.Open ("my_asset.txt");
|
||||
}
|
||||
}
|
||||
|
||||
Additionally, some Android functions will automatically load asset files:
|
||||
|
||||
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");
|
BIN
src/PluginHostTest/Assets/DejaVuSansMono.ttf
Normal file
BIN
src/PluginHostTest/Assets/fontawesome-webfont.ttf
Normal file
44
src/PluginHostTest/ClickView.cs
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
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 3 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/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Android.Content;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace keepass2android.view
|
||||
{
|
||||
public abstract class ClickView : LinearLayout {
|
||||
protected ClickView (IntPtr javaReference, JniHandleOwnership transfer)
|
||||
: base(javaReference, transfer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected ClickView(Context context) :base(context)
|
||||
{
|
||||
}
|
||||
|
||||
abstract public void OnClick();
|
||||
|
||||
abstract public void OnCreateMenu(IContextMenu menu, IContextMenuContextMenuInfo menuInfo);
|
||||
|
||||
abstract public bool OnContextItemSelected(IMenuItem item);
|
||||
}
|
||||
}
|
||||
|
367
src/PluginHostTest/EntryActivity.cs
Normal file
@ -0,0 +1,367 @@
|
||||
/*
|
||||
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 3 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/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Android.Preferences;
|
||||
using Android.Text.Method;
|
||||
using System.Globalization;
|
||||
using Android.Content.PM;
|
||||
using Android.Webkit;
|
||||
using Android.Graphics;
|
||||
using Java.IO;
|
||||
using PluginHostTest;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
[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";
|
||||
|
||||
private static Typeface _passwordFont;
|
||||
|
||||
private bool _showPassword;
|
||||
private int _pos;
|
||||
|
||||
private List<TextView> _protectedTextViews;
|
||||
|
||||
|
||||
protected void SetEntryView() {
|
||||
SetContentView(Resource.Layout.entry_view);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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.Commit();
|
||||
|
||||
_showPassword = ! prefs.GetBoolean(GetString(Resource.String.maskpass_key), Resources.GetBoolean(Resource.Boolean.maskpass_default));
|
||||
|
||||
base.OnCreate(savedInstanceState);
|
||||
SetEntryView();
|
||||
|
||||
FillData(false);
|
||||
|
||||
SetupEditButtons();
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void CompleteOnCreate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
private String getDateTime(DateTime dt) {
|
||||
return dt.ToString ("g", CultureInfo.CurrentUICulture);
|
||||
}
|
||||
|
||||
String concatTags(List<string> tags)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (string tag in tags)
|
||||
{
|
||||
sb.Append(tag);
|
||||
sb.Append(", ");
|
||||
}
|
||||
if (tags.Count > 0)
|
||||
sb.Remove(sb.Length-2,2);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
void PopulateExtraStrings(bool trimList)
|
||||
{
|
||||
ViewGroup extraGroup = (ViewGroup)FindViewById(Resource.Id.extra_strings);
|
||||
if (trimList)
|
||||
{
|
||||
extraGroup.RemoveAllViews();
|
||||
}
|
||||
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)
|
||||
{
|
||||
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);
|
||||
layout.LayoutParameters = layoutParams;
|
||||
View viewInflated = LayoutInflater.Inflate(Resource.Layout.entry_extrastring_title,null);
|
||||
TextView keyView = (TextView)viewInflated;
|
||||
if (key != null)
|
||||
keyView.Text = key;
|
||||
|
||||
layout.AddView(keyView);
|
||||
TextView valueView = (TextView)LayoutInflater.Inflate(Resource.Layout.entry_extrastring_value, null);
|
||||
if (value != null)
|
||||
valueView.Text = value;
|
||||
SetPasswordTypeface(valueView);
|
||||
if (isProtected)
|
||||
RegisterProtectedTextView(valueView);
|
||||
|
||||
|
||||
if ((int)Build.VERSION.SdkInt >= 11)
|
||||
valueView.SetTextIsSelectable(true);
|
||||
layout.AddView(valueView);
|
||||
return layout;
|
||||
}
|
||||
|
||||
private void RegisterProtectedTextView(TextView protectedTextView)
|
||||
{
|
||||
_protectedTextViews.Add(protectedTextView);
|
||||
}
|
||||
|
||||
|
||||
void PopulateBinaries(bool trimList)
|
||||
{
|
||||
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"} })
|
||||
{
|
||||
String key = pair.Key;
|
||||
Button binaryButton = new Button(this);
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
|
||||
binaryButton.Text = key;
|
||||
binaryButton.SetCompoundDrawablesWithIntrinsicBounds( Resources.GetDrawable(Android.Resource.Drawable.IcMenuSave),null, null, null);
|
||||
binaryButton.Click += (sender, e) =>
|
||||
{
|
||||
Button btnSender = (Button)(sender);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.SetTitle(GetString(Resource.String.SaveAttachmentDialog_title));
|
||||
|
||||
builder.SetMessage(GetString(Resource.String.SaveAttachmentDialog_text));
|
||||
|
||||
builder.SetPositiveButton(GetString(Resource.String.SaveAttachmentDialog_save), (dlgSender, dlgEvt) =>
|
||||
{
|
||||
|
||||
});
|
||||
|
||||
builder.SetNegativeButton(GetString(Resource.String.SaveAttachmentDialog_open), (dlgSender, dlgEvt) =>
|
||||
{
|
||||
|
||||
});
|
||||
|
||||
Dialog dialog = builder.Create();
|
||||
dialog.Show();
|
||||
|
||||
|
||||
};
|
||||
binariesGroup.AddView(binaryButton,layoutParams);
|
||||
|
||||
|
||||
}
|
||||
FindViewById(Resource.Id.entry_binaries_label).Visibility = true ? ViewStates.Visible : ViewStates.Gone;
|
||||
}
|
||||
|
||||
// url = file path or whatever suitable URL you want.
|
||||
public static String GetMimeType(String url)
|
||||
{
|
||||
String type = null;
|
||||
String extension = MimeTypeMap.GetFileExtensionFromUrl(url);
|
||||
if (extension != null) {
|
||||
MimeTypeMap mime = MimeTypeMap.Singleton;
|
||||
type = mime.GetMimeTypeFromExtension(extension);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public override void OnBackPressed()
|
||||
{
|
||||
base.OnBackPressed();
|
||||
}
|
||||
|
||||
protected void FillData(bool trimList)
|
||||
{
|
||||
_protectedTextViews = new List<TextView>();
|
||||
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");
|
||||
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));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
FindViewById(Resource.Id.entry_expires).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.entry_expires_label).Visibility = ViewStates.Gone;
|
||||
}
|
||||
PopulateText(Resource.Id.entry_comment, Resource.Id.entry_comment_label, "some text about this entry");
|
||||
|
||||
PopulateText(Resource.Id.entry_tags, Resource.Id.entry_tags_label, "bla; blubb; blablubb");
|
||||
|
||||
PopulateExtraStrings(trimList);
|
||||
|
||||
PopulateBinaries(trimList);
|
||||
|
||||
SetPasswordStyle();
|
||||
}
|
||||
|
||||
private void SetPasswordTypeface(TextView textView)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void PopulateText(int viewId, int headerViewId,int resId) {
|
||||
View header = FindViewById(headerViewId);
|
||||
TextView tv = (TextView)FindViewById(viewId);
|
||||
|
||||
header.Visibility = tv.Visibility = ViewStates.Visible;
|
||||
tv.SetText (resId);
|
||||
}
|
||||
|
||||
private void PopulateText(int viewId, int headerViewId, String text)
|
||||
{
|
||||
View header = FindViewById(headerViewId);
|
||||
TextView tv = (TextView)FindViewById(viewId);
|
||||
if (String.IsNullOrEmpty(text))
|
||||
{
|
||||
header.Visibility = tv.Visibility = ViewStates.Gone;
|
||||
}
|
||||
else
|
||||
{
|
||||
header.Visibility = tv.Visibility = ViewStates.Visible;
|
||||
tv.Text = text;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void RequiresRefresh ()
|
||||
{
|
||||
Intent ret = new Intent ();
|
||||
ret.PutExtra (KeyRefreshPos, _pos);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void SetPasswordStyle() {
|
||||
foreach (TextView password in _protectedTextViews)
|
||||
{
|
||||
|
||||
if (_showPassword)
|
||||
{
|
||||
password.TransformationMethod = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
password.TransformationMethod = PasswordTransformationMethod.Instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
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>
|
||||
public void AskAddUrlThenCompleteCreate(string url)
|
||||
{
|
||||
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.SetPositiveButton(GetString(Resource.String.yes), (dlgSender, dlgEvt) =>
|
||||
{
|
||||
|
||||
});
|
||||
|
||||
builder.SetNegativeButton(GetString(Resource.String.no), (dlgSender, dlgEvt) =>
|
||||
{
|
||||
CompleteOnCreate();
|
||||
});
|
||||
|
||||
Dialog dialog = builder.Create();
|
||||
dialog.Show();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
64
src/PluginHostTest/EntryContentsView.cs
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
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 3 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/>.
|
||||
*/
|
||||
|
||||
using Android.Content;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
using System;
|
||||
using Android.Runtime;
|
||||
using PluginHostTest;
|
||||
|
||||
|
||||
namespace keepass2android.view
|
||||
{
|
||||
|
||||
public class EntryContentsView : LinearLayout {
|
||||
|
||||
public EntryContentsView (IntPtr javaReference, JniHandleOwnership transfer)
|
||||
: base(javaReference, transfer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public EntryContentsView(Context context):base(context, null) {
|
||||
InflateView();
|
||||
}
|
||||
|
||||
public EntryContentsView(Context context, IAttributeSet attrs): base(context, attrs) {
|
||||
|
||||
InflateView();
|
||||
}
|
||||
|
||||
private void InflateView() {
|
||||
LayoutInflater inflater = (LayoutInflater) Context.GetSystemService(Context.LayoutInflaterService);
|
||||
inflater.Inflate(Resource.Layout.entry_view_contents, this);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* doesn't compile with mono for android
|
||||
*
|
||||
protected override LayoutParams GenerateDefaultLayoutParams() {
|
||||
return new LayoutParams(LayoutParams.FillParent, LayoutParams.WrapContent);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
|
75
src/PluginHostTest/EntrySection.cs
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
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 3 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/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Android.Content;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Android.Util;
|
||||
using PluginHostTest;
|
||||
|
||||
namespace keepass2android.view
|
||||
{
|
||||
|
||||
public class EntrySection : LinearLayout {
|
||||
|
||||
public EntrySection(Context context): base(context, null) {
|
||||
InflateView (null, null);
|
||||
}
|
||||
|
||||
public EntrySection(Context context, IAttributeSet attrs): base(context, attrs) {
|
||||
InflateView (null, null);
|
||||
}
|
||||
|
||||
public EntrySection(Context context, IAttributeSet attrs, String title, String value): base(context, attrs) {
|
||||
|
||||
InflateView(title, value);
|
||||
}
|
||||
|
||||
public EntrySection (IntPtr javaReference, JniHandleOwnership transfer)
|
||||
: base(javaReference, transfer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void InflateView(String title, String value) {
|
||||
LayoutInflater inflater = (LayoutInflater) Context.GetSystemService(Context.LayoutInflaterService);
|
||||
inflater.Inflate(Resource.Layout.entry_section, this);
|
||||
|
||||
SetText(Resource.Id.title, title);
|
||||
|
||||
FindViewById<TextView>(Resource.Id.value).Invalidate();
|
||||
SetText(Resource.Id.value, value);
|
||||
//TODO: this seems to cause a bug when rotating the device (and the activity gets destroyed)
|
||||
//After recreating the activity, the value fields all have the same content.
|
||||
if ((int)Android.OS.Build.VERSION.SdkInt >= 11)
|
||||
FindViewById<TextView>(Resource.Id.value).SetTextIsSelectable(true);
|
||||
}
|
||||
|
||||
private void SetText(int resId, String str) {
|
||||
if (str != null) {
|
||||
TextView tvTitle = (TextView) FindViewById(resId);
|
||||
tvTitle.Text = str;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
88
src/PluginHostTest/Kp2aShortHelpView.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.Res;
|
||||
using Android.Graphics;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Text;
|
||||
using Android.Text.Method;
|
||||
using Android.Text.Style;
|
||||
using Android.Text.Util;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using PluginHostTest;
|
||||
|
||||
namespace keepass2android.views
|
||||
{
|
||||
public class Kp2aShortHelpView: TextView
|
||||
{
|
||||
private string _helpText;
|
||||
private static Typeface _iconFont;
|
||||
|
||||
protected Kp2aShortHelpView(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
|
||||
{
|
||||
}
|
||||
|
||||
public Kp2aShortHelpView(Context context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
public Kp2aShortHelpView(Context context, IAttributeSet attrs) : base(context, attrs)
|
||||
{
|
||||
Initialize(attrs);
|
||||
}
|
||||
|
||||
public Kp2aShortHelpView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
|
||||
{
|
||||
Initialize(attrs);
|
||||
}
|
||||
|
||||
public string HelpText
|
||||
{
|
||||
get { return _helpText; }
|
||||
set { _helpText = value;
|
||||
UpdateView();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateView()
|
||||
{
|
||||
if (!String.IsNullOrEmpty(_helpText))
|
||||
{
|
||||
Text = "i";
|
||||
Clickable = true;
|
||||
|
||||
|
||||
MovementMethod = LinkMovementMethod.Instance;
|
||||
Click += (sender, args) =>
|
||||
{
|
||||
new AlertDialog.Builder(Context)
|
||||
.SetTitle("PluginHostTest")
|
||||
.SetMessage(_helpText)
|
||||
.SetPositiveButton(Android.Resource.String.Ok, (o, eventArgs) => { })
|
||||
.Show();
|
||||
};
|
||||
Visibility = ViewStates.Visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
Visibility = ViewStates.Gone;
|
||||
}
|
||||
}
|
||||
|
||||
void Initialize(IAttributeSet attrs)
|
||||
{
|
||||
TypedArray a = Context.ObtainStyledAttributes(
|
||||
attrs,
|
||||
Resource.Styleable.Kp2aShortHelpView);
|
||||
HelpText = a.GetString(Resource.Styleable.Kp2aShortHelpView_help_text);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
113
src/PluginHostTest/PluginArrayAdapter.cs
Normal file
@ -0,0 +1,113 @@
|
||||
using Android.Widget;
|
||||
using Android.Content;
|
||||
using Android.Views;
|
||||
using System.Collections.Generic;
|
||||
using Android.App;
|
||||
using PluginHostTest;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
|
||||
public class PluginItem
|
||||
{
|
||||
private readonly string _package;
|
||||
|
||||
public PluginItem(string package, string _label, int _icon, string _version, string _enabledStatus)
|
||||
{
|
||||
_package = package;
|
||||
Label = _label;
|
||||
Icon = _icon;
|
||||
Version = _version;
|
||||
EnabledStatus = _enabledStatus;
|
||||
}
|
||||
|
||||
public string Label
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string Version
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string EnabledStatus
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int Icon
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string Package
|
||||
{
|
||||
get { return _package; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class PluginArrayAdapter : ArrayAdapter<PluginItem>
|
||||
{
|
||||
|
||||
class PluginViewHolder : Java.Lang.Object
|
||||
{
|
||||
public ImageView imgIcon;
|
||||
public TextView txtTitle;
|
||||
public TextView txtVersion;
|
||||
|
||||
public TextView txtEnabledStatus;
|
||||
}
|
||||
|
||||
Context context;
|
||||
int layoutResourceId;
|
||||
IList<PluginItem> data = null;
|
||||
|
||||
public PluginArrayAdapter(Context context, int layoutResourceId, IList<PluginItem> data) :
|
||||
base(context, layoutResourceId, data)
|
||||
{
|
||||
|
||||
this.layoutResourceId = layoutResourceId;
|
||||
this.context = context;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public override View GetView(int position, View convertView, ViewGroup parent)
|
||||
{
|
||||
View row = convertView;
|
||||
PluginViewHolder holder = null;
|
||||
|
||||
if (row == null)
|
||||
{
|
||||
LayoutInflater inflater = ((Activity)context).LayoutInflater;
|
||||
row = inflater.Inflate(layoutResourceId, parent, false);
|
||||
|
||||
holder = new PluginViewHolder();
|
||||
holder.imgIcon = (ImageView)row.FindViewById(Resource.Id.imgIcon);
|
||||
holder.txtTitle = (TextView)row.FindViewById(Resource.Id.txtLabel);
|
||||
holder.txtVersion = (TextView)row.FindViewById(Resource.Id.txtVersion);
|
||||
holder.txtEnabledStatus = (TextView)row.FindViewById(Resource.Id.txtStatus);
|
||||
|
||||
row.Tag = holder;
|
||||
}
|
||||
else
|
||||
{
|
||||
holder = (PluginViewHolder)row.Tag;
|
||||
}
|
||||
|
||||
var item = data[position];
|
||||
holder.txtTitle.Text = item.Label;
|
||||
holder.txtVersion.Text = item.Version;
|
||||
holder.txtEnabledStatus.Text = item.EnabledStatus;
|
||||
holder.imgIcon.SetImageResource(item.Icon);
|
||||
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
174
src/PluginHostTest/PluginDatabase.cs
Normal file
@ -0,0 +1,174 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Android.Content;
|
||||
using Android.Util;
|
||||
using Keepass2android.Pluginsdk;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
public class PluginDatabase
|
||||
{
|
||||
private const string _tag = "KP2A_PluginDatabase";
|
||||
private readonly Context _ctx;
|
||||
private const string _accessToken = "accessToken";
|
||||
private const string _scopes = "scopes";
|
||||
private const string _requesttoken = "requestToken";
|
||||
private const string _pluginlist = "pluginList";
|
||||
|
||||
|
||||
|
||||
public PluginDatabase(Context ctx)
|
||||
{
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
private ISharedPreferences GetPreferencesForPlugin(string packageName)
|
||||
{
|
||||
var prefs = _ctx.GetSharedPreferences("KP2A.Plugin." + packageName, FileCreationMode.Private);
|
||||
if (prefs.GetString(_requesttoken, null) == null)
|
||||
{
|
||||
var editor = prefs.Edit();
|
||||
editor.PutString(_requesttoken, Guid.NewGuid().ToString());
|
||||
editor.Commit();
|
||||
|
||||
var hostPrefs = GetHostPrefs();
|
||||
var plugins = hostPrefs.GetStringSet(_pluginlist, new List<string>());
|
||||
if (!plugins.Contains(packageName))
|
||||
{
|
||||
plugins.Add(packageName);
|
||||
hostPrefs.Edit().PutStringSet(_pluginlist, plugins).Commit();
|
||||
}
|
||||
|
||||
}
|
||||
return prefs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the request token for the plugin. Request token is created of not yet available.
|
||||
/// </summary>
|
||||
/// <returns>Request token. Never null or empty.</returns>
|
||||
public string GetRequestToken(string pkgName)
|
||||
{
|
||||
return GetPreferencesForPlugin(pkgName).GetString(_requesttoken, null);
|
||||
}
|
||||
|
||||
public IList<string> GetPluginScopes(string pluginPackage)
|
||||
{
|
||||
var prefs = GetPreferencesForPlugin(pluginPackage);
|
||||
return AccessManager.StringToStringArray(prefs.GetString(_scopes, ""));
|
||||
}
|
||||
|
||||
public IEnumerable<String> GetAllPluginPackages()
|
||||
{
|
||||
var hostPrefs = GetHostPrefs();
|
||||
return hostPrefs.GetStringSet(_pluginlist, new List<string>());
|
||||
}
|
||||
|
||||
public bool IsEnabled(string pluginPackage)
|
||||
{
|
||||
return GetPreferencesForPlugin(pluginPackage).GetString(_accessToken, null) != null;
|
||||
}
|
||||
|
||||
|
||||
public void StorePlugin(string pluginPackage, string accessToken, IList<string> requestedScopes)
|
||||
{
|
||||
ISharedPreferences hostPrefs = GetHostPrefs();
|
||||
ISharedPreferences pluginPrefs = GetPreferencesForPlugin(pluginPackage);
|
||||
var stringSet = hostPrefs.GetStringSet(_pluginlist, new Collection<string>());
|
||||
if (!stringSet.Contains(pluginPackage))
|
||||
{
|
||||
stringSet.Add(pluginPackage);
|
||||
hostPrefs.Edit()
|
||||
.PutStringSet(_pluginlist, stringSet)
|
||||
.Commit();
|
||||
}
|
||||
|
||||
pluginPrefs.Edit()
|
||||
.PutString(_scopes, AccessManager.StringArrayToString(requestedScopes))
|
||||
.PutString(_accessToken, accessToken)
|
||||
.Commit();
|
||||
}
|
||||
|
||||
private ISharedPreferences GetHostPrefs()
|
||||
{
|
||||
return _ctx.GetSharedPreferences("plugins", FileCreationMode.Private);
|
||||
}
|
||||
|
||||
public void SetEnabled(string pluginPackage, bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
string accessToken = Guid.NewGuid().ToString();
|
||||
|
||||
Intent i = new Intent(Strings.ActionReceiveAccess);
|
||||
i.SetPackage(pluginPackage);
|
||||
|
||||
i.PutExtra(Strings.ExtraSender, _ctx.PackageName);
|
||||
i.PutExtra(Strings.ExtraRequestToken, GetPreferencesForPlugin(pluginPackage).GetString(_requesttoken, null));
|
||||
i.PutExtra(Strings.ExtraAccessToken, accessToken);
|
||||
_ctx.SendBroadcast(i);
|
||||
|
||||
StorePlugin(pluginPackage, accessToken, GetPluginScopes( pluginPackage));
|
||||
}
|
||||
else
|
||||
{
|
||||
Intent i = new Intent(Strings.ActionRevokeAccess);
|
||||
i.SetPackage(pluginPackage);
|
||||
i.PutExtra(Strings.ExtraSender, _ctx.PackageName);
|
||||
i.PutExtra(Strings.ExtraRequestToken, GetPreferencesForPlugin(pluginPackage).GetString(_requesttoken, null));
|
||||
_ctx.SendBroadcast(i);
|
||||
StorePlugin(pluginPackage, null, GetPluginScopes(pluginPackage));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsValidAccessToken(string pluginPackage, string accessToken, string scope)
|
||||
{
|
||||
var prefs = GetPreferencesForPlugin(pluginPackage);
|
||||
if (prefs.GetString(_accessToken, null) != accessToken)
|
||||
{
|
||||
Log.Warn(_tag, "Invalid access token for " + pluginPackage);
|
||||
return false;
|
||||
}
|
||||
if (!AccessManager.StringToStringArray(prefs.GetString(_scopes, "")).Contains(scope))
|
||||
{
|
||||
Log.Warn(_tag, "Scope " + scope + " not granted for " + pluginPackage);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public string GetAccessToken(string pluginPackage)
|
||||
{
|
||||
return GetPreferencesForPlugin(pluginPackage).GetString(_accessToken, null);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
foreach (string plugin in GetAllPluginPackages())
|
||||
{
|
||||
GetPreferencesForPlugin(plugin).Edit().Clear().Commit();
|
||||
}
|
||||
GetHostPrefs().Edit().Clear().Commit();
|
||||
}
|
||||
|
||||
|
||||
public IEnumerable<string> GetPluginsWithAcceptedScope(string scope)
|
||||
{
|
||||
return GetAllPluginPackages().Where(plugin =>
|
||||
{
|
||||
var prefs = GetPreferencesForPlugin(plugin);
|
||||
return (prefs.GetString(_accessToken, null) != null)
|
||||
&& AccessManager.StringToStringArray(prefs.GetString(_scopes, "")).Contains(scope);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public void ClearPlugin(string plugin)
|
||||
{
|
||||
var prefs = _ctx.GetSharedPreferences("KP2A.Plugin." + plugin, FileCreationMode.Private);
|
||||
prefs.Edit().Clear().Commit();
|
||||
}
|
||||
}
|
||||
}
|
108
src/PluginHostTest/PluginDetailsActivity.cs
Normal file
@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.Content.Res;
|
||||
using Android.Graphics.Drawables;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Java.Util;
|
||||
using Keepass2android.Pluginsdk;
|
||||
using keepass2android;
|
||||
using keepass2android.views;
|
||||
|
||||
namespace PluginHostTest
|
||||
{
|
||||
[Activity(Label = "TODO Details")]
|
||||
public class PluginDetailsActivity : Activity
|
||||
{
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
base.OnCreate(bundle);
|
||||
|
||||
string pluginPackage = Intent.GetStringExtra("PluginPackage");
|
||||
|
||||
Intent mainIntent = new Intent(Intent.ActionMain, null);
|
||||
mainIntent.AddCategory(Intent.CategoryLauncher);
|
||||
|
||||
IList<ResolveInfo> appList = PackageManager.QueryIntentActivities(mainIntent, 0);
|
||||
//Collections.Sort(appList, new ResolveInfo.DisplayNameComparator(PackageManager));
|
||||
|
||||
foreach (ResolveInfo temp in appList)
|
||||
{
|
||||
|
||||
Log.Verbose("my logs", "package and activity name = "
|
||||
+ temp.ActivityInfo.PackageName + " "
|
||||
+ temp.ActivityInfo.Name + " " + temp.ActivityInfo.IconResource);
|
||||
|
||||
}
|
||||
var pluginRes = PackageManager.GetResourcesForApplication(pluginPackage);
|
||||
var title = GetStringFromPlugin(pluginRes, pluginPackage, "kp2aplugin_title");
|
||||
var author = GetStringFromPlugin(pluginRes, pluginPackage, "kp2aplugin_author");
|
||||
var shortDesc = GetStringFromPlugin(pluginRes, pluginPackage, "kp2aplugin_shortdesc");
|
||||
|
||||
var version = PackageManager.GetPackageInfo(pluginPackage, 0).VersionName;
|
||||
|
||||
SetContentView(Resource.Layout.plugin_details);
|
||||
if (title != null)
|
||||
FindViewById<TextView>(Resource.Id.txtLabel).Text = title;
|
||||
FindViewById<TextView>(Resource.Id.txtVersion).Text = version;
|
||||
SetTextOrHide(Resource.Id.txtAuthor, author);
|
||||
SetTextOrHide(Resource.Id.txtShortDesc, shortDesc);
|
||||
|
||||
var checkbox = FindViewById<CheckBox>(Resource.Id.cb_enabled);
|
||||
PluginDatabase pluginDb = new PluginDatabase(this);
|
||||
checkbox.Checked = pluginDb.IsEnabled(pluginPackage);
|
||||
checkbox.CheckedChange += delegate(object sender, CompoundButton.CheckedChangeEventArgs args)
|
||||
{
|
||||
pluginDb.SetEnabled(pluginPackage, checkbox.Checked);
|
||||
};
|
||||
|
||||
Drawable d = PackageManager.GetApplicationIcon(pluginPackage);
|
||||
FindViewById<ImageView>(Resource.Id.imgIcon).SetImageDrawable(d);
|
||||
|
||||
FindViewById<TextView>(Resource.Id.txtVersion).Text = version;
|
||||
|
||||
var scopesContainer = FindViewById<LinearLayout>(Resource.Id.scopes_list);
|
||||
|
||||
foreach (string scope in pluginDb.GetPluginScopes(pluginPackage))
|
||||
{
|
||||
string scopeId = scope.Substring("keepass2android.".Length);
|
||||
|
||||
TextWithHelp help = new TextWithHelp(this, GetString(Resources.GetIdentifier(scopeId + "_title", "string", PackageName)), GetString(Resources.GetIdentifier(scopeId + "_explanation", "string", PackageName)));
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
|
||||
help.LayoutParameters = layoutParams;
|
||||
scopesContainer.AddView(help);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void SetTextOrHide(int resourceId, string text)
|
||||
{
|
||||
if (text != null)
|
||||
{
|
||||
FindViewById<TextView>(resourceId).Text = text;
|
||||
FindViewById<TextView>(resourceId).Visibility = ViewStates.Visible;
|
||||
}
|
||||
else
|
||||
FindViewById<TextView>(resourceId).Visibility = ViewStates.Gone;
|
||||
}
|
||||
|
||||
private static string GetStringFromPlugin(Resources pluginRes, string pluginPackage, string stringId)
|
||||
{
|
||||
int titleId = pluginRes.GetIdentifier(pluginPackage + ":string/"+stringId, null, null);
|
||||
string title = null;
|
||||
if (titleId != 0)
|
||||
title = pluginRes.GetString(titleId);
|
||||
return title;
|
||||
}
|
||||
}
|
||||
}
|
143
src/PluginHostTest/PluginHost.cs
Normal file
@ -0,0 +1,143 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
using Keepass2android;
|
||||
using Keepass2android.Pluginsdk;
|
||||
using PluginHostTest;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
[BroadcastReceiver()]
|
||||
[IntentFilter(new[] { Strings.ActionRequestAccess})]
|
||||
public class PluginHost: BroadcastReceiver
|
||||
{
|
||||
|
||||
private const string _tag = "KP2A_PluginHost";
|
||||
|
||||
|
||||
private static readonly string[] _validScopes = { Strings.ScopeDatabaseActions };
|
||||
|
||||
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);
|
||||
foreach (ResolveInfo ri in dictPacks)
|
||||
{
|
||||
ApplicationInfo appInfo = ri.ActivityInfo.ApplicationInfo;
|
||||
String pkgName = appInfo.PackageName;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
Intent triggerIntent = new Intent(Strings.ActionTriggerRequestAccess);
|
||||
triggerIntent.SetPackage(pkgName);
|
||||
triggerIntent.PutExtra(Strings.ExtraSender, ctx.PackageName);
|
||||
|
||||
triggerIntent.PutExtra(Strings.ExtraRequestToken, pluginDatabase.GetRequestToken(pkgName));
|
||||
ctx.SendBroadcast(triggerIntent);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public override void OnReceive(Context context, Intent intent)
|
||||
{
|
||||
PluginDatabase pluginDb = new PluginDatabase(context);
|
||||
if (intent.Action == Strings.ActionRequestAccess)
|
||||
{
|
||||
var senderPackage = intent.GetStringExtra(Strings.ExtraSender);
|
||||
var requestToken = intent.GetStringExtra(Strings.ExtraRequestToken);
|
||||
|
||||
var requestedScopes = intent.GetStringArrayListExtra(Strings.ExtraScopes);
|
||||
|
||||
if (!AreScopesValid(requestedScopes))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (pluginDb.GetRequestToken(senderPackage) != requestToken)
|
||||
{
|
||||
Log.Warn(_tag, "Invalid requestToken!");
|
||||
return;
|
||||
}
|
||||
string currentAccessToken = pluginDb.GetAccessToken(senderPackage);
|
||||
if ((currentAccessToken != null)
|
||||
&& (AccessManager.IsSubset(requestedScopes,
|
||||
pluginDb.GetPluginScopes(senderPackage))))
|
||||
{
|
||||
//permission already there.
|
||||
var i = new Intent(Strings.ActionReceiveAccess);
|
||||
i.PutExtra(Strings.ExtraSender, context.PackageName);
|
||||
i.PutExtra(Strings.ExtraAccessToken, currentAccessToken);
|
||||
//TODO: Plugin should verify requestToken to make sure it doesn't receive accessTokens from malicious apps
|
||||
i.PutExtra(Strings.ExtraRequestToken, requestToken);
|
||||
i.SetPackage(senderPackage);
|
||||
context.SendBroadcast(i);
|
||||
|
||||
Log.Debug(_tag, "Plugin " + senderPackage + " enabled.");
|
||||
}
|
||||
else
|
||||
{
|
||||
//store that scope was requested but not yet approved (=> accessToken = null)
|
||||
pluginDb.StorePlugin(senderPackage, null, requestedScopes);
|
||||
|
||||
Log.Debug(_tag, "Plugin " + senderPackage + " not enabled.");
|
||||
|
||||
//see if the plugin has an access token
|
||||
string accessToken = intent.GetStringExtra(Strings.ExtraAccessToken);
|
||||
if (accessToken != null)
|
||||
{
|
||||
//notify plugin that access token is no longer valid or sufficient
|
||||
Intent i = new Intent(Strings.ActionRevokeAccess);
|
||||
i.PutExtra(Strings.ExtraSender, context.PackageName);
|
||||
i.PutExtra(Strings.ExtraAccessToken, accessToken);
|
||||
i.SetPackage(senderPackage);
|
||||
context.SendBroadcast(i);
|
||||
Log.Warn(_tag, "Access token of plugin " + senderPackage + " not (or no more) valid.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private bool AreScopesValid(IList<string> requestedScopes)
|
||||
{
|
||||
foreach (string scope in requestedScopes)
|
||||
{
|
||||
if (!_validScopes.Contains(scope))
|
||||
{
|
||||
Log.Warn(_tag, "invalid scope: " + scope);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
578
src/PluginHostTest/PluginHostTest.csproj
Normal file
@ -0,0 +1,578 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}</ProjectGuid>
|
||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>PluginHostTest</RootNamespace>
|
||||
<AssemblyName>PluginHostTest</AssemblyName>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AndroidApplication>true</AndroidApplication>
|
||||
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
|
||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||
<AndroidUseLatestPlatformSdk />
|
||||
<TargetFrameworkVersion>v4.4</TargetFrameworkVersion>
|
||||
<AndroidSupportedAbis>armeabi-v7a</AndroidSupportedAbis>
|
||||
<AndroidStoreUncompressedFileExtensions />
|
||||
<MandroidI18n />
|
||||
<JavaMaximumHeapSize />
|
||||
<JavaOptions />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
|
||||
<AndroidLinkMode>None</AndroidLinkMode>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
||||
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Mono.Android" />
|
||||
<Reference Include="Mono.Android.Support.v4" />
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Activity1.cs" />
|
||||
<Compile Include="ClickView.cs" />
|
||||
<Compile Include="EntryActivity.cs" />
|
||||
<Compile Include="EntryContentsView.cs" />
|
||||
<Compile Include="EntrySection.cs" />
|
||||
<Compile Include="Kp2aShortHelpView.cs" />
|
||||
<Compile Include="PluginDatabase.cs" />
|
||||
<Compile Include="PluginDetailsActivity.cs" />
|
||||
<Compile Include="PluginArrayAdapter.cs" />
|
||||
<Compile Include="PluginListActivity.cs" />
|
||||
<Compile Include="PluginHost.cs" />
|
||||
<Compile Include="Resources\Resource.Designer.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TextViewSelect.cs" />
|
||||
<Compile Include="TextWithHelp.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<TransformFile Include="Assets\DejaVuSansMono.ttf" />
|
||||
<TransformFile Include="Assets\fontawesome-webfont.ttf" />
|
||||
<None Include="Resources\AboutResources.txt" />
|
||||
<None Include="Assets\AboutAssets.txt" />
|
||||
<AndroidResource Include="Resources\Layout\plugin_list.xml">
|
||||
<SubType>AndroidResource</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\Main.axml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Values\Strings2.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\Icon.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<TransformFile Include="Properties\AndroidManifest.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PluginSdkBinding\PluginSdkBinding.csproj">
|
||||
<Project>{3da3911e-36de-465e-8f15-f1991b6437e5}</Project>
|
||||
<Name>PluginSdkBinding</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\ListViewPluginRow.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\plugin_details.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\text_with_help.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Values\config2.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Values\attrs.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Values\colors.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Values\config.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Values\dimens.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Values\styles.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Values\strings.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\BlueButton.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Values-v14\styles_light.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Values-v14\colors.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Values-v14\styles.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Values-v14\styles_dark.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\btn_new_group.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\btn_new_group_dark.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\btn_new_group_holodark.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\collections_collection.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\collections_new_label.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\device_access_new_account.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\device_access_new_account_dark.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\device_access_not_secure.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\donate_cake.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\EntryFieldHeaderBackground.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\extra_string_header.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\GreenButton.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\HeaderButtonBackground.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_action_eye_open.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_action_search.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_action_search_holodark.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_keepass2android.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_keepass2android_nonet.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_launcher.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_launcher_folder_small.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_launcher_gray.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_launcher_gray_bday.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_launcher_offline.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_launcher_red.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_menu_add_field_holo_light.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_menu_add_field_holodark.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_menu_remove_field_holo_light.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_menu_remove_field_holodark.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_menu_view.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_storage_androidget.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_storage_androidsend.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_storage_dropbox.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_storage_dropboxKP2A.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_storage_file.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_storage_ftp.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_storage_gdrive.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_storage_http.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_storage_https.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_storage_sftp.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_storage_skydrive.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_unlocked_gray.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic00.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic01.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic02.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic03.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic04.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic05.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic06.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic07.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic08.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic09.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic10.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic11.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic12.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic13.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic14.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic15.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic16.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic17.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic18.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic19.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic20.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic21.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic22.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic23.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic24.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic25.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic26.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic27.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic28.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic29.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic30.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic31.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic32.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic33.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic34.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic35.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic36.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic37.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic38.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic39.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic40.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic41.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic42.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic43.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic44.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic45.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic46.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic47.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic48.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic49.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic50.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic51.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic52.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic53.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic54.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic55.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic56.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic57.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic58.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic59.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic60.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic61.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic62.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic63.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic64.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic65.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic66.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic67.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic68.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic99_blank.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\location_web_site.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\navigation_accept.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\navigation_accept_dark.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\navigation_cancel.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\navigation_cancel_holodark.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\navigation_previous_item.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\navigation_previous_item_dark.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\notify.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\notify_keyboard.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\oktoberfest.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\RedButton.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\section_header.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\transparent.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\YellowButton.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\collections_collection_holodark.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\collections_new_label_holodark.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\device_access_new_account_holodark.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\location_web_site_holodark.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<TransformFile Include="Resources\Layout\entry_edit.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\entry_edit_section.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\entry_extrastring_title.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\entry_extrastring_value.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\entry_list_entry.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\entry_section.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\entry_view_test.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\entry_view_contents.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\entry_view.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.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
52
src/PluginHostTest/PluginListActivity.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Android.Widget;
|
||||
using PluginHostTest;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
//TODO theme?
|
||||
[Activity (Label = "Plugins (TODO)", ConfigurationChanges=ConfigChanges.Orientation|ConfigChanges.KeyboardHidden )]
|
||||
public class PluginListActivity : ListActivity
|
||||
{
|
||||
private PluginArrayAdapter _pluginArrayAdapter;
|
||||
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
base.OnCreate(bundle);
|
||||
|
||||
//TODO _design.ApplyTheme();
|
||||
|
||||
SetContentView(Resource.Layout.plugin_list);
|
||||
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();
|
||||
/*
|
||||
{
|
||||
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);
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
5
src/PluginHostTest/Properties/AndroidManifest.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-sdk android:minSdkVersion="12" android:targetSdkVersion="19" />
|
||||
<application></application>
|
||||
</manifest>
|
34
src/PluginHostTest/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Android.App;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("PluginHostTest")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("PluginHostTest")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
|
||||
// Add some common permissions, these can be removed if not needed
|
||||
[assembly: UsesPermission(Android.Manifest.Permission.Internet)]
|
||||
[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)]
|
50
src/PluginHostTest/Resources/AboutResources.txt
Normal file
@ -0,0 +1,50 @@
|
||||
Images, layout descriptions, binary blobs and string dictionaries can be included
|
||||
in your application as resource files. Various Android APIs are designed to
|
||||
operate on the resource IDs instead of dealing with images, strings or binary blobs
|
||||
directly.
|
||||
|
||||
For example, a sample Android app that contains a user interface layout (main.xml),
|
||||
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
|
||||
would keep its resources in the "Resources" directory of the application:
|
||||
|
||||
Resources/
|
||||
drawable-hdpi/
|
||||
icon.png
|
||||
|
||||
drawable-ldpi/
|
||||
icon.png
|
||||
|
||||
drawable-mdpi/
|
||||
icon.png
|
||||
|
||||
layout/
|
||||
main.xml
|
||||
|
||||
values/
|
||||
strings.xml
|
||||
|
||||
In order to get the build system to recognize Android resources, set the build action to
|
||||
"AndroidResource". The native Android APIs do not operate directly with filenames, but
|
||||
instead operate on resource IDs. When you compile an Android application that uses resources,
|
||||
the build system will package the resources for distribution and generate a class called
|
||||
"Resource" that contains the tokens for each one of the resources included. For example,
|
||||
for the above Resources layout, this is what the Resource class would expose:
|
||||
|
||||
public class Resource {
|
||||
public class drawable {
|
||||
public const int icon = 0x123;
|
||||
}
|
||||
|
||||
public class layout {
|
||||
public const int main = 0x456;
|
||||
}
|
||||
|
||||
public class strings {
|
||||
public const int first_string = 0xabc;
|
||||
public const int second_string = 0xbcd;
|
||||
}
|
||||
}
|
||||
|
||||
You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main
|
||||
to reference the layout/main.xml file, or Resource.strings.first_string to reference the first
|
||||
string in the dictionary file values/strings.xml.
|
BIN
src/PluginHostTest/Resources/Drawable/2_action_about.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
38
src/PluginHostTest/Resources/Drawable/BlueButton.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" >
|
||||
<shape>
|
||||
<solid
|
||||
android:color="#449def" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#2f6699" />
|
||||
<corners
|
||||
android:radius="3dp" />
|
||||
<padding
|
||||
android:left="10dp"
|
||||
android:top="10dp"
|
||||
android:right="10dp"
|
||||
android:bottom="10dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<gradient
|
||||
android:startColor="#449def"
|
||||
android:endColor="#2f6699"
|
||||
android:angle="270" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#2f6699" />
|
||||
<corners
|
||||
android:radius="4dp" />
|
||||
<padding
|
||||
android:left="10dp"
|
||||
android:top="10dp"
|
||||
android:right="10dp"
|
||||
android:bottom="10dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape>
|
||||
<gradient
|
||||
android:angle="45"
|
||||
android:startColor="#FF000030"
|
||||
android:endColor="#FF000000"
|
||||
android:type="linear" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
38
src/PluginHostTest/Resources/Drawable/GreenButton.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" >
|
||||
<shape>
|
||||
<solid
|
||||
android:color="#70c656" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#53933f" />
|
||||
<corners
|
||||
android:radius="3dp" />
|
||||
<padding
|
||||
android:left="10dp"
|
||||
android:top="10dp"
|
||||
android:right="10dp"
|
||||
android:bottom="10dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<gradient
|
||||
android:startColor="#70c656"
|
||||
android:endColor="#53933f"
|
||||
android:angle="270" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#53933f" />
|
||||
<corners
|
||||
android:radius="4dp" />
|
||||
<padding
|
||||
android:left="10dp"
|
||||
android:top="10dp"
|
||||
android:right="10dp"
|
||||
android:bottom="10dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:drawable="@color/group_header_button_pressed"/> <!-- pressed -->
|
||||
<item android:state_focused="true" android:drawable="@color/group_header_button_pressed"/> <!-- focused -->
|
||||
<item android:drawable="@android:color/transparent"/> <!-- default -->
|
||||
</selector>
|
BIN
src/PluginHostTest/Resources/Drawable/Icon.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
37
src/PluginHostTest/Resources/Drawable/RedButton.xml
Normal file
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" >
|
||||
<shape>
|
||||
<solid
|
||||
android:color="#ef4444" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#992f2f" />
|
||||
<corners
|
||||
android:radius="3dp" />
|
||||
<padding
|
||||
android:left="10dp"
|
||||
android:top="10dp"
|
||||
android:right="10dp"
|
||||
android:bottom="10dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<gradient
|
||||
android:startColor="#ef4444"
|
||||
android:endColor="#992f2f"
|
||||
android:angle="270" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#992f2f" />
|
||||
<corners
|
||||
android:radius="3dp" />
|
||||
<padding
|
||||
android:left="10dp"
|
||||
android:top="10dp"
|
||||
android:right="10dp"
|
||||
android:bottom="10dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
38
src/PluginHostTest/Resources/Drawable/YellowButton.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:state_pressed="true">
|
||||
<shape>
|
||||
<solid
|
||||
android:color="#f3ae1b" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#bb6008" />
|
||||
<corners
|
||||
android:radius="3dp" />
|
||||
<padding
|
||||
android:left="10dp"
|
||||
android:top="10dp"
|
||||
android:right="10dp"
|
||||
android:bottom="10dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<gradient
|
||||
android:startColor="#f3ae1b"
|
||||
android:endColor="#bb6008"
|
||||
android:angle="270" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#bb6008" />
|
||||
<corners
|
||||
android:radius="4dp" />
|
||||
<padding
|
||||
android:left="10dp"
|
||||
android:top="10dp"
|
||||
android:right="10dp"
|
||||
android:bottom="10dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
BIN
src/PluginHostTest/Resources/Drawable/btn_new_group.png
Normal file
After Width: | Height: | Size: 877 B |
BIN
src/PluginHostTest/Resources/Drawable/btn_new_group_dark.png
Normal file
After Width: | Height: | Size: 713 B |
BIN
src/PluginHostTest/Resources/Drawable/btn_new_group_holodark.png
Normal file
After Width: | Height: | Size: 758 B |
BIN
src/PluginHostTest/Resources/Drawable/collections_collection.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.6 KiB |
BIN
src/PluginHostTest/Resources/Drawable/collections_new_label.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 924 B |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 785 B |
BIN
src/PluginHostTest/Resources/Drawable/donate_cake.png
Normal file
After Width: | Height: | Size: 134 KiB |
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<size android:height="2px" />
|
||||
<solid
|
||||
android:color="@color/emphasis2"/>
|
||||
</shape>
|
BIN
src/PluginHostTest/Resources/Drawable/ic00.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic01.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic02.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic03.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic04.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic05.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic06.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic07.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic08.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic09.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic10.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic11.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic12.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic13.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic14.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic15.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic16.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic17.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic18.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic19.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic20.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic21.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic22.png
Normal file
After Width: | Height: | Size: 1006 B |
BIN
src/PluginHostTest/Resources/Drawable/ic23.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic24.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic25.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic26.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic27.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic28.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic29.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic30.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic31.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic32.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic33.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic34.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic35.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic36.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic37.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic38.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic39.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic40.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic41.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic42.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic43.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic44.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic45.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic46.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic47.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic48.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic49.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic50.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic51.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic52.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic53.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic54.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic55.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic56.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic57.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic58.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic59.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
src/PluginHostTest/Resources/Drawable/ic60.png
Normal file
After Width: | Height: | Size: 2.6 KiB |