Merge branch 'pluginhosttest'
Conflicts: .gitignore
4
.gitignore
vendored
@ -238,3 +238,7 @@ Thumbs.db
|
||||
/src/java/JavaFileStorageTest/gen/com/google/android/gms/R.java
|
||||
/src/java/JavaFileStorageTest/gen/group/pals/android/lib/ui/filechooser/R.java
|
||||
/src/java/JavaFileStorageTest/gen/keepass2android/javafilestorage/R.java
|
||||
/src/java/PluginQR/bin
|
||||
/src/PluginHostTest/bin/Debug
|
||||
|
||||
/src/java/Keepass2AndroidPluginSDK/bin
|
||||
|
@ -37,7 +37,8 @@ namespace ArtTestApp
|
||||
FindViewById<Button>(Resource.Id.MyButton2).Click += (sender, args) => StartActivityForResult(typeof(Activity2),1);
|
||||
|
||||
FindViewById<Button>(Resource.Id.MyButton3).Click += (sender, args) => StartActivityForResult(typeof(PrefActivity), 1);
|
||||
|
||||
StartActivity(typeof(Activity2));
|
||||
Finish();
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -17,14 +17,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TwofishCipher", "TwofishCip
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JavaFileStorageBindings", "JavaFileStorageBindings\JavaFileStorageBindings.csproj", "{48574278-4779-4B3A-A9E4-9CF1BC285D0B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppCompatV7Binding", "AppCompatV7Binding\AppCompatV7Binding.csproj", "{23233A28-D74F-4BF8-B4D8-834060840BD7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AndroidFileChooserBinding", "AndroidFileChooserBinding\AndroidFileChooserBinding.csproj", "{3C0F7FE5-639F-4422-A087-8B26CF862D1B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KP2AKdbLibraryBinding", "KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj", "{70D3844A-D9FA-4A64-B205-A84C6A822196}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArtTestApp", "ArtTestApp\ArtTestApp.csproj", "{1FF6C335-A627-43C9-AAA7-CBAC2E74CD18}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginHostTest", "PluginHostTest\PluginHostTest.csproj", "{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginSdkBinding", "PluginSdkBinding\PluginSdkBinding.csproj", "{3DA3911E-36DE-465E-8F15-F1991B6437E5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -206,24 +208,6 @@ Global
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
{23233A28-D74F-4BF8-B4D8-834060840BD7}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
@ -284,6 +268,48 @@ Global
|
||||
{1FF6C335-A627-43C9-AAA7-CBAC2E74CD18}.ReleaseNoNet|Mixed Platforms.Deploy.0 = Release|Any CPU
|
||||
{1FF6C335-A627-43C9-AAA7-CBAC2E74CD18}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
{1FF6C335-A627-43C9-AAA7-CBAC2E74CD18}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|Mixed Platforms.Deploy.0 = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
{C9F4AE81-0996-4E17-B3F2-D0F652F6AC50}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -20,7 +20,7 @@
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>False</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;INCLUDE_KEYBOARD;INCLUDE_FILECHOOSER;INCLUDE_JAVAFILESTORAGE;INCLUDE_KEYTRANSFORM</DefineConstants>
|
||||
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_FILECHOOSER;EXCLUDE_JAVAFILESTORAGE;EXCLUDE_KEYTRANSFORM</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>False</ConsolePause>
|
||||
|
@ -2,7 +2,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// Dieser Code wurde von einem Tool generiert.
|
||||
// Laufzeitversion:4.0.30319.34011
|
||||
// Laufzeitversion:4.0.30319.34014
|
||||
//
|
||||
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
|
||||
// der Code erneut generiert wird.
|
||||
|
@ -20,7 +20,7 @@
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG;EXCLUDE_TWOFISH;INCLUDE_KEYBOARD;INCLUDE_FILECHOOSER;INCLUDE_JAVAFILESTORAGE;INCLUDE_KEYTRANSFORM</DefineConstants>
|
||||
<DefineConstants>TRACE;DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_FILECHOOSER;EXCLUDE_JAVAFILESTORAGE;EXCLUDE_KEYTRANSFORM</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
@ -57,6 +57,7 @@
|
||||
<Compile Include="database\edit\MoveElement.cs" />
|
||||
<Compile Include="database\KdbDatabaseLoader.cs" />
|
||||
<Compile Include="database\KdbxDatabaseLoader.cs" />
|
||||
<Compile Include="database\PwEntryOutput.cs" />
|
||||
<Compile Include="database\SynchronizeCachedDatabase.cs" />
|
||||
<Compile Include="DataExchange\FileFormatProvider.cs" />
|
||||
<Compile Include="DataExchange\Formats\KeePassCsv1x.cs" />
|
||||
|
@ -45,6 +45,11 @@ namespace keepass2android
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Information about the last opened entry. Includes the entry but also transformed fields.
|
||||
/// </summary>
|
||||
public PwEntryOutput LastOpenedEntry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// if an OTP key was used, this property tells the location of the OTP auxiliary file.
|
||||
/// Must be set after loading.
|
||||
|
60
src/Kp2aBusinessLogic/database/PwEntryOutput.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using KeePass.Util.Spr;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Collections;
|
||||
using KeePassLib.Security;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the strings which are output from a PwEntry.
|
||||
/// </summary>
|
||||
/// In contrast to the original PwEntry, this means that placeholders are replaced. Also, plugins may modify
|
||||
/// or add fields.
|
||||
public class PwEntryOutput
|
||||
{
|
||||
private readonly PwEntry _entry;
|
||||
private readonly PwDatabase _db;
|
||||
private readonly ProtectedStringDictionary _outputStrings = new ProtectedStringDictionary();
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the PwEntryOutput by replacing the placeholders
|
||||
/// </summary>
|
||||
public PwEntryOutput(PwEntry entry, PwDatabase db)
|
||||
{
|
||||
_entry = entry;
|
||||
_db = db;
|
||||
|
||||
foreach (var pair in entry.Strings)
|
||||
{
|
||||
_outputStrings.Set(pair.Key, new ProtectedString(entry.Strings.Get(pair.Key).IsProtected, GetStringAndReplacePlaceholders(pair.Key)));
|
||||
}
|
||||
}
|
||||
|
||||
string GetStringAndReplacePlaceholders(string key)
|
||||
{
|
||||
String value = Entry.Strings.ReadSafe(key);
|
||||
value = SprEngine.Compile(value, new SprContext(Entry, _db, SprCompileFlags.All));
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the ID of the entry
|
||||
/// </summary>
|
||||
public PwUuid Uuid
|
||||
{
|
||||
get { return Entry.Uuid; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The output strings for the represented entry
|
||||
/// </summary>
|
||||
public ProtectedStringDictionary OutputStrings { get { return _outputStrings; } }
|
||||
|
||||
public PwEntry Entry
|
||||
{
|
||||
get { return _entry; }
|
||||
}
|
||||
}
|
||||
}
|
@ -67,6 +67,7 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="TestIntentsAndBundles.cs" />
|
||||
<Compile Include="ProgressDialogStub.cs" />
|
||||
<Compile Include="TestBase.cs" />
|
||||
<Compile Include="TestCacheSupervisor.cs" />
|
||||
@ -85,6 +86,7 @@
|
||||
<Compile Include="TestSynchronizeCachedDatabase.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="ClassDiagram1.cd" />
|
||||
<None Include="Resources\AboutResources.txt" />
|
||||
<None Include="Assets\AboutAssets.txt" />
|
||||
</ItemGroup>
|
||||
|
@ -18,7 +18,8 @@ namespace Kp2aUnitTests
|
||||
{
|
||||
TestRunner runner = new TestRunner();
|
||||
// Run all tests from this assembly
|
||||
runner.AddTests(Assembly.GetExecutingAssembly());
|
||||
//runner.AddTests(Assembly.GetExecutingAssembly());
|
||||
runner.AddTests(new List<Type> { typeof(TestIntentsAndBundles) });
|
||||
//runner.AddTests(new List<Type> { typeof(TestSynchronizeCachedDatabase)});
|
||||
//runner.AddTests(typeof(TestLoadDb).GetMethod("LoadErrorWithCertificateTrustFailure"));
|
||||
//runner.AddTests(typeof(TestLoadDb).GetMethod("LoadWithAcceptedCertificateTrustFailure"));
|
||||
|
2
src/Kp2aUnitTests/Resources/Resource.Designer.cs
generated
@ -2,7 +2,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// Dieser Code wurde von einem Tool generiert.
|
||||
// Laufzeitversion:4.0.30319.34011
|
||||
// Laufzeitversion:4.0.30319.34014
|
||||
//
|
||||
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
|
||||
// der Code erneut generiert wird.
|
||||
|
34
src/Kp2aUnitTests/TestIntentsAndBundles.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Java.IO;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Interfaces;
|
||||
using KeePassLib.Keys;
|
||||
using KeePassLib.Serialization;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using keepass2android;
|
||||
|
||||
namespace Kp2aUnitTests
|
||||
{
|
||||
[TestClass]
|
||||
internal class TestIntentsAndBundles
|
||||
{
|
||||
[TestMethod]
|
||||
public void StringArray()
|
||||
{
|
||||
string[] dataIn = new string[] { "a","bcd"};
|
||||
Intent i= new Intent();
|
||||
i.PutExtra("key", dataIn);
|
||||
|
||||
Bundle extras = i.Extras;
|
||||
var dataOut = extras.GetStringArray("key");
|
||||
Assert.AreEqual(dataIn.Length, dataOut.Length);
|
||||
Assert.AreEqual(dataIn[0], dataOut[0]);
|
||||
Assert.AreEqual(dataIn[1], dataOut[1]);
|
||||
}
|
||||
}
|
||||
}
|
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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
40
src/PluginHostTest/App.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using KeePassLib;
|
||||
using KeePassLib.Keys;
|
||||
using KeePassLib.Serialization;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
public class App
|
||||
{
|
||||
|
||||
public class Kp2A
|
||||
{
|
||||
private static Db _mDb;
|
||||
|
||||
public class Db
|
||||
{
|
||||
public PwEntryOutput LastOpenedEntry { get; set; }
|
||||
|
||||
public void SetEntry(PwEntry e)
|
||||
{
|
||||
KpDatabase = new PwDatabase();
|
||||
KpDatabase.New(new IOConnectionInfo(), new CompositeKey());
|
||||
|
||||
KpDatabase.RootGroup.AddEntry(e, true);
|
||||
}
|
||||
|
||||
public PwDatabase KpDatabase
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
public static Db GetDb()
|
||||
{
|
||||
if (_mDb == null)
|
||||
_mDb = new Db();
|
||||
return _mDb;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
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);
|
||||
}
|
||||
}
|
||||
|
35
src/PluginHostTest/CopyToClipboardService.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Widget;
|
||||
using KeePassLib.Security;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
[Service]
|
||||
public class CopyToClipboardService: Service
|
||||
{
|
||||
public CopyToClipboardService()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public CopyToClipboardService(IntPtr javaReference, JniHandleOwnership transfer)
|
||||
: base(javaReference, transfer)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public static void CopyValueToClipboardWithTimeout(Context ctx, string text)
|
||||
{
|
||||
Toast.MakeText(ctx, text, ToastLength.Short).Show();
|
||||
}
|
||||
|
||||
public override IBinder OnBind(Intent intent)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
905
src/PluginHostTest/EntryActivity.cs
Normal file
@ -0,0 +1,905 @@
|
||||
/*
|
||||
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 System.Threading;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Text;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Android.Preferences;
|
||||
using Android.Text.Method;
|
||||
using System.Globalization;
|
||||
using Android.Content.PM;
|
||||
using Android.Webkit;
|
||||
using Android.Graphics;
|
||||
using Java.IO;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Security;
|
||||
using Keepass2android.Pluginsdk;
|
||||
using PluginHostTest;
|
||||
using Uri = Android.Net.Uri;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
|
||||
[Activity(Label = "@string/app_name", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
|
||||
Theme = "@style/NoTitleBar")]
|
||||
public class EntryActivity : Activity
|
||||
{
|
||||
public const String KeyEntry = "entry";
|
||||
public const String KeyRefreshPos = "refresh_pos";
|
||||
public const String KeyCloseAfterCreate = "close_after_create";
|
||||
|
||||
protected PwEntry Entry = new PwEntry(true, true);
|
||||
|
||||
private static Typeface _passwordFont;
|
||||
|
||||
internal bool _showPassword;
|
||||
private int _pos;
|
||||
|
||||
private List<TextView> _protectedTextViews;
|
||||
|
||||
private readonly Dictionary<string, List<IPopupMenuItem>> _popupMenuItems =
|
||||
new Dictionary<string, List<IPopupMenuItem>>();
|
||||
|
||||
private readonly Dictionary<string, IStringView> _stringViews = new Dictionary<string, IStringView>();
|
||||
private readonly List<PluginMenuOption> _pendingMenuOptions = new List<PluginMenuOption>();
|
||||
private IMenu _menu;
|
||||
|
||||
protected void SetEntryView()
|
||||
{
|
||||
SetContentView(Resource.Layout.entry_view);
|
||||
}
|
||||
|
||||
protected void SetupEditButtons()
|
||||
{
|
||||
View edit = FindViewById(Resource.Id.entry_edit);
|
||||
if (true)
|
||||
{
|
||||
edit.Visibility = ViewStates.Visible;
|
||||
edit.Click += (sender, e) =>
|
||||
{
|
||||
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
edit.Visibility = ViewStates.Gone;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class PluginActionReceiver : BroadcastReceiver
|
||||
{
|
||||
private readonly EntryActivity _activity;
|
||||
|
||||
public PluginActionReceiver(EntryActivity activity)
|
||||
{
|
||||
_activity = activity;
|
||||
}
|
||||
|
||||
public override void OnReceive(Context context, Intent intent)
|
||||
{
|
||||
var pluginPackage = intent.GetStringExtra(Strings.ExtraSender);
|
||||
if (new PluginDatabase(context).IsValidAccessToken(pluginPackage,
|
||||
intent.GetStringExtra(Strings.ExtraAccessToken),
|
||||
Strings.ScopeCurrentEntry))
|
||||
{
|
||||
if (intent.GetStringExtra(Strings.ExtraEntryId) != _activity.Entry.Uuid.ToHexString())
|
||||
{
|
||||
Kp2aLog.Log("received action for wrong entry " + intent.GetStringExtra(Strings.ExtraEntryId));
|
||||
return;
|
||||
}
|
||||
_activity.AddPluginAction(pluginPackage,
|
||||
intent.GetStringExtra(Strings.ExtraFieldId),
|
||||
intent.GetStringExtra(Strings.ExtraActionId),
|
||||
intent.GetStringExtra(Strings.ExtraActionDisplayText),
|
||||
intent.GetIntExtra(Strings.ExtraActionIconResId, -1),
|
||||
intent.GetBundleExtra(Strings.ExtraActionData));
|
||||
}
|
||||
else
|
||||
{
|
||||
Kp2aLog.Log("received invalid request. Plugin not authorized.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PluginFieldReceiver : BroadcastReceiver
|
||||
{
|
||||
private readonly EntryActivity _activity;
|
||||
|
||||
public PluginFieldReceiver(EntryActivity activity)
|
||||
{
|
||||
_activity = activity;
|
||||
}
|
||||
|
||||
public override void OnReceive(Context context, Intent intent)
|
||||
{
|
||||
if (intent.GetStringExtra(Strings.ExtraEntryId) != _activity.Entry.Uuid.ToHexString())
|
||||
{
|
||||
Kp2aLog.Log("received field for wrong entry " + intent.GetStringExtra(Strings.ExtraEntryId));
|
||||
return;
|
||||
}
|
||||
if (!new PluginDatabase(context).IsValidAccessToken(intent.GetStringExtra(Strings.ExtraSender),
|
||||
intent.GetStringExtra(Strings.ExtraAccessToken),
|
||||
Strings.ScopeCurrentEntry))
|
||||
{
|
||||
Kp2aLog.Log("received field with invalid access token from " + intent.GetStringExtra(Strings.ExtraSender));
|
||||
return;
|
||||
}
|
||||
string key = intent.GetStringExtra(Strings.ExtraFieldId);
|
||||
string value = intent.GetStringExtra(Strings.ExtraFieldValue);
|
||||
bool isProtected = intent.GetBooleanExtra(Strings.ExtraFieldProtected, false);
|
||||
_activity.SetPluginField(key, value, isProtected);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPluginField(string key, string value, bool isProtected)
|
||||
{
|
||||
//update or add the string view:
|
||||
IStringView existingField;
|
||||
if (_stringViews.TryGetValue(key, out existingField))
|
||||
{
|
||||
existingField.Text = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewGroup extraGroup = (ViewGroup) FindViewById(Resource.Id.extra_strings);
|
||||
var view = CreateExtraSection(key, value, isProtected);
|
||||
extraGroup.AddView(view.View);
|
||||
}
|
||||
|
||||
//update the Entry output in the App database and notify the CopyToClipboard service
|
||||
App.Kp2A.GetDb().LastOpenedEntry.OutputStrings.Set(key, new ProtectedString(isProtected, value));
|
||||
Intent updateKeyboardIntent = new Intent(this, typeof(CopyToClipboardService));
|
||||
Intent.SetAction(Intents.UpdateKeyboard);
|
||||
updateKeyboardIntent.PutExtra(KeyEntry, Entry.Uuid.ToHexString());
|
||||
StartService(updateKeyboardIntent);
|
||||
|
||||
//notify plugins
|
||||
NotifyPluginsOnModification(Strings.PrefixString+key);
|
||||
}
|
||||
|
||||
private void AddPluginAction(string pluginPackage, string fieldId, string popupItemId, string displayText, int iconId, Bundle bundleExtra)
|
||||
{
|
||||
if (fieldId != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
//create a new popup item for the plugin action:
|
||||
var newPopup = new PluginPopupMenuItem(this, pluginPackage, fieldId, popupItemId, displayText, iconId, bundleExtra);
|
||||
//see if we already have a popup item for this field with the same item id
|
||||
var popupsForField = _popupMenuItems[fieldId];
|
||||
var popupItemPos = popupsForField.FindIndex(0,
|
||||
item =>
|
||||
(item is PluginPopupMenuItem) &&
|
||||
((PluginPopupMenuItem)item).PopupItemId == popupItemId);
|
||||
|
||||
//replace existing or add
|
||||
if (popupItemPos >= 0)
|
||||
{
|
||||
popupsForField[popupItemPos] = newPopup;
|
||||
}
|
||||
else
|
||||
{
|
||||
popupsForField.Add(newPopup);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Kp2aLog.Log(e.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//we need to add an option to the menu.
|
||||
//As it is not sure that OnCreateOptionsMenu was called yet, we cannot access _menu without a check:
|
||||
|
||||
Intent i = new Intent(Strings.ActionEntryActionSelected);
|
||||
i.SetPackage(pluginPackage);
|
||||
i.PutExtra(Strings.ExtraActionData, bundleExtra);
|
||||
i.PutExtra(Strings.ExtraSender, PackageName);
|
||||
PluginHost.AddEntryToIntent(i, App.Kp2A.GetDb().LastOpenedEntry);
|
||||
|
||||
var menuOption = new PluginMenuOption()
|
||||
{
|
||||
DisplayText = displayText,
|
||||
Icon = PackageManager.GetResourcesForApplication(pluginPackage).GetDrawable(iconId),
|
||||
Intent = i
|
||||
};
|
||||
|
||||
if (_menu != null)
|
||||
{
|
||||
AddMenuOption(menuOption);
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (_pendingMenuOptions)
|
||||
{
|
||||
_pendingMenuOptions.Add(menuOption);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void AddMenuOption(PluginMenuOption menuOption)
|
||||
{
|
||||
var menuItem = _menu.Add(menuOption.DisplayText);
|
||||
menuItem.SetIcon(menuOption.Icon);
|
||||
menuItem.SetIntent(menuOption.Intent);
|
||||
}
|
||||
|
||||
public override bool OnCreateOptionsMenu(IMenu menu)
|
||||
{
|
||||
_menu = menu;
|
||||
base.OnCreateOptionsMenu(menu);
|
||||
|
||||
MenuInflater inflater = MenuInflater;
|
||||
inflater.Inflate(Resource.Menu.entry, menu);
|
||||
|
||||
lock (_pendingMenuOptions)
|
||||
{
|
||||
foreach (var option in _pendingMenuOptions)
|
||||
AddMenuOption(option);
|
||||
_pendingMenuOptions.Clear();
|
||||
}
|
||||
|
||||
|
||||
UpdateTogglePasswordMenu();
|
||||
|
||||
IMenuItem gotoUrl = menu.FindItem(Resource.Id.menu_goto_url);
|
||||
//Disabled IMenuItem copyUser = menu.FindItem(Resource.Id.menu_copy_user);
|
||||
//Disabled IMenuItem copyPass = menu.FindItem(Resource.Id.menu_copy_pass);
|
||||
|
||||
// In API >= 11 onCreateOptionsMenu may be called before onCreate completes
|
||||
// so _entry may not be set
|
||||
if (Entry == null)
|
||||
{
|
||||
gotoUrl.SetVisible(false);
|
||||
//Disabled copyUser.SetVisible(false);
|
||||
//Disabled copyPass.SetVisible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
String url = Entry.Strings.ReadSafe(PwDefs.UrlField);
|
||||
if (String.IsNullOrEmpty(url))
|
||||
{
|
||||
// disable button if url is not available
|
||||
gotoUrl.SetVisible(false);
|
||||
}
|
||||
if (String.IsNullOrEmpty(Entry.Strings.ReadSafe(PwDefs.UserNameField)))
|
||||
{
|
||||
// disable button if username is not available
|
||||
//Disabled copyUser.SetVisible(false);
|
||||
}
|
||||
if (String.IsNullOrEmpty(Entry.Strings.ReadSafe(PwDefs.PasswordField)))
|
||||
{
|
||||
// disable button if password is not available
|
||||
//Disabled copyPass.SetVisible(false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool OnOptionsItemSelected(IMenuItem item)
|
||||
{
|
||||
//check if this is a plugin action
|
||||
if ((item.Intent != null) && (item.Intent.Action == Strings.ActionEntryActionSelected))
|
||||
{
|
||||
//yes. let the plugin handle the click:
|
||||
SendBroadcast(item.Intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (item.ItemId)
|
||||
{
|
||||
case Resource.Id.menu_donate:
|
||||
try
|
||||
{
|
||||
// Util.GotoDonateUrl(this);
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.error_failed_to_launch_link, ToastLength.Long).Show();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
case Resource.Id.menu_toggle_pass:
|
||||
if (_showPassword)
|
||||
{
|
||||
item.SetTitle(Resource.String.show_password);
|
||||
_showPassword = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.SetTitle(Resource.String.menu_hide_password);
|
||||
_showPassword = true;
|
||||
}
|
||||
SetPasswordStyle();
|
||||
|
||||
return true;
|
||||
|
||||
case Resource.Id.menu_goto_url:
|
||||
string url = _stringViews[PwDefs.UrlField].Text;
|
||||
if (url == null) return false;
|
||||
|
||||
// Default http:// if no protocol specified
|
||||
if (!url.Contains("://"))
|
||||
{
|
||||
url = "http://" + url;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
|
||||
}
|
||||
return true;
|
||||
/* TODO: required?
|
||||
case Resource.Id.menu_copy_user:
|
||||
timeoutCopyToClipboard(_entry.Strings.ReadSafe (PwDefs.UserNameField));
|
||||
return true;
|
||||
|
||||
case Resource.Id.menu_copy_pass:
|
||||
timeoutCopyToClipboard(_entry.Strings.ReadSafe (PwDefs.UserNameField));
|
||||
return true;
|
||||
*/
|
||||
case Resource.Id.menu_rate:
|
||||
try
|
||||
{
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
|
||||
}
|
||||
return true;
|
||||
case Resource.Id.menu_suggest_improvements:
|
||||
try
|
||||
{
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
|
||||
}
|
||||
return true;
|
||||
case Resource.Id.menu_lock:
|
||||
return true;
|
||||
case Resource.Id.menu_translate:
|
||||
try
|
||||
{
|
||||
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Toast.MakeText(this, Resource.String.no_url_handler, ToastLength.Long).Show();
|
||||
}
|
||||
return true;
|
||||
case Android.Resource.Id.Home:
|
||||
//Currently the action bar only displays the home button when we come from a previous activity.
|
||||
//So we can simply Finish. See this page for information on how to do this in more general (future?) cases:
|
||||
//http://developer.android.com/training/implementing-navigation/ancestral.html
|
||||
Finish();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return base.OnOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
|
||||
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
|
||||
|
||||
long usageCount = prefs.GetLong(GetString(Resource.String.UsageCount_key), 0);
|
||||
|
||||
ISharedPreferencesEditor edit = prefs.Edit();
|
||||
edit.PutLong(GetString(Resource.String.UsageCount_key), usageCount + 1);
|
||||
edit.Commit();
|
||||
|
||||
_showPassword =
|
||||
!prefs.GetBoolean(GetString(Resource.String.maskpass_key), Resources.GetBoolean(Resource.Boolean.maskpass_default));
|
||||
|
||||
Entry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "philipp "));
|
||||
Entry.Strings.Set(PwDefs.PasswordField, new ProtectedString(true, "password value"));
|
||||
Entry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "https://www.google.com"));
|
||||
Entry.Strings.Set("field header", new ProtectedString(true, "protected field value"));
|
||||
Entry.Strings.Set("public field header", new ProtectedString(false, "public field value"));
|
||||
|
||||
base.OnCreate(savedInstanceState);
|
||||
SetEntryView();
|
||||
|
||||
FillData();
|
||||
|
||||
SetupEditButtons();
|
||||
|
||||
App.Kp2A.GetDb().LastOpenedEntry = new PwEntryOutput(Entry, App.Kp2A.GetDb().KpDatabase);
|
||||
|
||||
RegisterReceiver(new PluginActionReceiver(this), new IntentFilter(Strings.ActionAddEntryAction));
|
||||
RegisterReceiver(new PluginFieldReceiver(this), new IntentFilter(Strings.ActionSetEntryField));
|
||||
|
||||
new Thread(NotifyPluginsOnOpen).Start();
|
||||
}
|
||||
|
||||
private void NotifyPluginsOnOpen()
|
||||
{
|
||||
App.Kp2A.GetDb().SetEntry(Entry);
|
||||
|
||||
Intent i = new Intent(Strings.ActionOpenEntry);
|
||||
i.PutExtra(Strings.ExtraSender, PackageName);
|
||||
AddEntryToIntent(i);
|
||||
|
||||
|
||||
foreach (var plugin in new PluginDatabase(this).GetPluginsWithAcceptedScope(Strings.ScopeCurrentEntry))
|
||||
{
|
||||
i.SetPackage(plugin);
|
||||
SendBroadcast(i);
|
||||
}
|
||||
}
|
||||
private void NotifyPluginsOnModification(string fieldId)
|
||||
{
|
||||
Intent i = new Intent(Strings.ActionEntryOutputModified);
|
||||
i.PutExtra(Strings.ExtraSender, PackageName);
|
||||
i.PutExtra(Strings.ExtraFieldId, fieldId);
|
||||
AddEntryToIntent(i);
|
||||
|
||||
|
||||
foreach (var plugin in new PluginDatabase(this).GetPluginsWithAcceptedScope(Strings.ScopeCurrentEntry))
|
||||
{
|
||||
i.SetPackage(plugin);
|
||||
SendBroadcast(i);
|
||||
}
|
||||
}
|
||||
|
||||
public void CompleteOnCreate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
private String getDateTime(DateTime dt)
|
||||
{
|
||||
return dt.ToString("g", CultureInfo.CurrentUICulture);
|
||||
}
|
||||
|
||||
private 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();
|
||||
}
|
||||
|
||||
private void PopulateExtraStrings()
|
||||
{
|
||||
ViewGroup extraGroup = (ViewGroup) FindViewById(Resource.Id.extra_strings);
|
||||
foreach (var pair in Entry.Strings.Where(pair => !PwDefs.IsStandardField(pair.Key)).OrderBy(pair => pair.Key))
|
||||
{
|
||||
var stringView = CreateExtraSection(pair.Key, pair.Value.ReadString(), pair.Value.IsProtected);
|
||||
extraGroup.AddView(stringView.View);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private ExtraStringView CreateExtraSection(string key, string value, bool isProtected)
|
||||
{
|
||||
LinearLayout layout = new LinearLayout(this, null) {Orientation = Orientation.Vertical};
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FillParent,
|
||||
ViewGroup.LayoutParams.WrapContent);
|
||||
|
||||
layout.LayoutParameters = layoutParams;
|
||||
View viewInflated = LayoutInflater.Inflate(Resource.Layout.entry_extrastring_title, null);
|
||||
TextView keyView = viewInflated.FindViewById<TextView>(Resource.Id.entry_title);
|
||||
if (key != null)
|
||||
keyView.Text = key;
|
||||
|
||||
layout.AddView(viewInflated);
|
||||
RelativeLayout valueViewContainer =
|
||||
(RelativeLayout) LayoutInflater.Inflate(Resource.Layout.entry_extrastring_value, null);
|
||||
var valueView = valueViewContainer.FindViewById<TextView>(Resource.Id.entry_extra);
|
||||
if (value != null)
|
||||
valueView.Text = value;
|
||||
SetPasswordTypeface(valueView);
|
||||
if (isProtected)
|
||||
{
|
||||
RegisterProtectedTextView(valueView);
|
||||
valueView.TransformationMethod = PasswordTransformationMethod.Instance;
|
||||
}
|
||||
|
||||
layout.AddView(valueViewContainer);
|
||||
var stringView = new ExtraStringView(layout, valueView, keyView);
|
||||
|
||||
_stringViews.Add(key, stringView);
|
||||
RegisterTextPopup(valueViewContainer, valueViewContainer.FindViewById(Resource.Id.extra_vdots), key, isProtected);
|
||||
|
||||
return stringView;
|
||||
|
||||
}
|
||||
|
||||
private List<IPopupMenuItem> RegisterPopup(string popupKey, View clickView, View anchorView)
|
||||
{
|
||||
clickView.Click += (sender, args) =>
|
||||
{
|
||||
ShowPopup(anchorView, popupKey);
|
||||
};
|
||||
_popupMenuItems[popupKey] = new List<IPopupMenuItem>();
|
||||
return _popupMenuItems[popupKey];
|
||||
}
|
||||
|
||||
|
||||
private void RegisterProtectedTextView(TextView protectedTextView)
|
||||
{
|
||||
_protectedTextViews.Add(protectedTextView);
|
||||
}
|
||||
|
||||
|
||||
private void PopulateBinaries()
|
||||
{
|
||||
ViewGroup binariesGroup = (ViewGroup) FindViewById(Resource.Id.binaries);
|
||||
foreach (KeyValuePair<string, string> pair in new Dictionary<string, string>())
|
||||
{
|
||||
String key = pair.Key;
|
||||
|
||||
|
||||
RelativeLayout valueViewContainer =
|
||||
(RelativeLayout) LayoutInflater.Inflate(Resource.Layout.entry_extrastring_value, null);
|
||||
var valueView = valueViewContainer.FindViewById<TextView>(Resource.Id.entry_extra);
|
||||
if (key != null)
|
||||
valueView.Text = key;
|
||||
|
||||
string popupKey = Strings.PrefixBinary + key;
|
||||
|
||||
var itemList = RegisterPopup(popupKey, valueViewContainer, valueViewContainer.FindViewById(Resource.Id.extra_vdots));
|
||||
itemList.Add(new WriteBinaryToFilePopupItem(key, this));
|
||||
itemList.Add(new OpenBinaryPopupItem(key, this));
|
||||
|
||||
|
||||
|
||||
|
||||
binariesGroup.AddView(valueViewContainer);
|
||||
/*
|
||||
Button binaryButton = new Button(this);
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
|
||||
binaryButton.Text = key;
|
||||
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()
|
||||
{
|
||||
_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);
|
||||
|
||||
|
||||
|
||||
PopulateStandardText(Resource.Id.entry_user_name, Resource.Id.entryfield_container_username, PwDefs.UserNameField);
|
||||
PopulateStandardText(Resource.Id.entry_url, Resource.Id.entryfield_container_url, PwDefs.UrlField);
|
||||
PopulateStandardText(Resource.Id.entry_password, Resource.Id.entryfield_container_password, PwDefs.PasswordField);
|
||||
RegisterProtectedTextView(FindViewById<TextView>(Resource.Id.entry_password));
|
||||
SetPasswordTypeface(FindViewById<TextView>(Resource.Id.entry_password));
|
||||
|
||||
RegisterTextPopup(FindViewById<RelativeLayout>(Resource.Id.username_container),
|
||||
FindViewById(Resource.Id.username_vdots), PwDefs.UserNameField);
|
||||
RegisterTextPopup(FindViewById<RelativeLayout>(Resource.Id.url_container),
|
||||
FindViewById(Resource.Id.url_vdots), PwDefs.UrlField)
|
||||
.Add(new GotoUrlMenuItem(this));
|
||||
RegisterTextPopup(FindViewById<RelativeLayout>(Resource.Id.password_container),
|
||||
FindViewById(Resource.Id.password_vdots), PwDefs.PasswordField);
|
||||
|
||||
|
||||
PopulateText(Resource.Id.entry_created, Resource.Id.entryfield_container_created, getDateTime(Entry.CreationTime));
|
||||
PopulateText(Resource.Id.entry_modified, Resource.Id.entryfield_container_modified, getDateTime(Entry.LastModificationTime));
|
||||
|
||||
if (Entry.Expires)
|
||||
{
|
||||
PopulateText(Resource.Id.entry_expires, Resource.Id.entryfield_container_expires, getDateTime(Entry.ExpiryTime));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
PopulateText(Resource.Id.entry_expires, Resource.Id.entryfield_container_expires, null);
|
||||
}
|
||||
PopulateStandardText(Resource.Id.entry_comment, Resource.Id.entryfield_container_comment, PwDefs.NotesField);
|
||||
PopulateText(Resource.Id.entry_tags, Resource.Id.entryfield_container_tags, concatTags(Entry.Tags));
|
||||
PopulateText(Resource.Id.entry_override_url, Resource.Id.entryfield_container_overrideurl, Entry.OverrideUrl);
|
||||
|
||||
PopulateExtraStrings();
|
||||
|
||||
PopulateBinaries();
|
||||
|
||||
SetPasswordStyle();
|
||||
}
|
||||
|
||||
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
|
||||
{
|
||||
base.OnActivityResult(requestCode, resultCode, data);
|
||||
if (resultCode == /*TODO*/ 0)
|
||||
{
|
||||
if (resultCode == /*TODO*/ 0)
|
||||
{
|
||||
RequiresRefresh();
|
||||
}
|
||||
Recreate();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
NotifyPluginsOnClose();
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
private void NotifyPluginsOnClose()
|
||||
{
|
||||
Intent i = new Intent(Strings.ActionCloseEntryView);
|
||||
i.PutExtra(Strings.ExtraSender, PackageName);
|
||||
foreach (var plugin in new PluginDatabase(this).GetPluginsWithAcceptedScope(Strings.ScopeCurrentEntry))
|
||||
{
|
||||
i.SetPackage(plugin);
|
||||
SendBroadcast(i);
|
||||
}
|
||||
}
|
||||
private List<IPopupMenuItem> RegisterTextPopup(View container, View anchor, string fieldKey)
|
||||
{
|
||||
return RegisterTextPopup(container, anchor, fieldKey, Entry.Strings.GetSafe(fieldKey).IsProtected);
|
||||
}
|
||||
|
||||
private List<IPopupMenuItem> RegisterTextPopup(View container, View anchor, string fieldKey, bool isProtected)
|
||||
{
|
||||
string popupKey = Strings.PrefixString + fieldKey;
|
||||
var popupItems = RegisterPopup(
|
||||
popupKey,
|
||||
container,
|
||||
anchor);
|
||||
popupItems.Add(new CopyToClipboardPopupMenuIcon(this, _stringViews[fieldKey]));
|
||||
if (isProtected)
|
||||
popupItems.Add(new ToggleVisibilityPopupMenuItem(this));
|
||||
return popupItems;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void ShowPopup(View anchor, string popupKey)
|
||||
{
|
||||
//PopupMenu popupMenu = new PopupMenu(this, FindViewById(Resource.Id.entry_user_name));
|
||||
PopupMenu popupMenu = new PopupMenu(this, anchor);
|
||||
|
||||
AccessManager.PreparePopup(popupMenu);
|
||||
int itemId = 0;
|
||||
foreach (IPopupMenuItem popupItem in _popupMenuItems[popupKey])
|
||||
{
|
||||
popupMenu.Menu.Add(0, itemId, 0, popupItem.Text)
|
||||
.SetIcon(popupItem.Icon);
|
||||
itemId++;
|
||||
}
|
||||
|
||||
popupMenu.MenuItemClick += delegate(object sender, PopupMenu.MenuItemClickEventArgs args)
|
||||
{
|
||||
_popupMenuItems[popupKey][args.Item.ItemId].HandleClick();
|
||||
};
|
||||
popupMenu.Show();
|
||||
}
|
||||
|
||||
private void ShowPopup(int resAnchor, string popupKey)
|
||||
{
|
||||
ShowPopup(FindViewById(resAnchor), popupKey);
|
||||
}
|
||||
|
||||
private void SetPasswordTypeface(TextView textView)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void PopulateText(int viewId, int containerViewId, int resId)
|
||||
{
|
||||
View header = FindViewById(containerViewId);
|
||||
TextView tv = (TextView) FindViewById(viewId);
|
||||
|
||||
header.Visibility = tv.Visibility = ViewStates.Visible;
|
||||
tv.SetText(resId);
|
||||
}
|
||||
|
||||
private void PopulateText(int viewId, int containerViewId, String text)
|
||||
{
|
||||
View container = FindViewById(containerViewId);
|
||||
TextView tv = (TextView) FindViewById(viewId);
|
||||
if (String.IsNullOrEmpty(text))
|
||||
{
|
||||
container.Visibility = tv.Visibility = ViewStates.Gone;
|
||||
}
|
||||
else
|
||||
{
|
||||
container.Visibility = tv.Visibility = ViewStates.Visible;
|
||||
tv.Text = text;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateStandardText(int viewId, int containerViewId, String key)
|
||||
{
|
||||
PopulateText(viewId, containerViewId, Entry.Strings.ReadSafe(key));
|
||||
_stringViews.Add(key, new StandardStringView(viewId, containerViewId, this));
|
||||
}
|
||||
|
||||
private 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();
|
||||
|
||||
}
|
||||
|
||||
public void ToggleVisibility()
|
||||
{
|
||||
_showPassword = !_showPassword;
|
||||
SetPasswordStyle();
|
||||
UpdateTogglePasswordMenu();
|
||||
}
|
||||
|
||||
public Android.Net.Uri WriteBinaryToFile(string key, bool writeToCacheDirectory)
|
||||
{
|
||||
return Android.Net.Uri.Empty;
|
||||
//TODO
|
||||
}
|
||||
|
||||
private void UpdateTogglePasswordMenu()
|
||||
{
|
||||
//todo use real method
|
||||
}
|
||||
|
||||
public void GotoUrl()
|
||||
{
|
||||
//TODO
|
||||
|
||||
}
|
||||
|
||||
public void OpenBinaryFile(Uri newUri)
|
||||
{
|
||||
Toast.MakeText(this, "opening file TODO", ToastLength.Short).Show();
|
||||
}
|
||||
|
||||
public void AddEntryToIntent(Intent intent)
|
||||
{
|
||||
PluginHost.AddEntryToIntent(intent, App.Kp2A.GetDb().LastOpenedEntry);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
using Android.Content;
|
||||
using Android.Graphics.Drawables;
|
||||
using PluginHostTest;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
/// <summary>
|
||||
/// Reperesents the popup menu item in EntryActivity to copy a string to clipboard
|
||||
/// </summary>
|
||||
class CopyToClipboardPopupMenuIcon : IPopupMenuItem
|
||||
{
|
||||
private readonly Context _context;
|
||||
private readonly IStringView _stringView;
|
||||
|
||||
public CopyToClipboardPopupMenuIcon(Context context, IStringView stringView)
|
||||
{
|
||||
_context = context;
|
||||
_stringView = stringView;
|
||||
|
||||
}
|
||||
|
||||
public Drawable Icon
|
||||
{
|
||||
get
|
||||
{
|
||||
return _context.Resources.GetDrawable(Resource.Drawable.ic_menu_copy_holo_light);
|
||||
}
|
||||
}
|
||||
public string Text
|
||||
{
|
||||
//TODO localize
|
||||
get { return "Copy to clipboard"; }
|
||||
}
|
||||
|
||||
|
||||
public void HandleClick()
|
||||
{
|
||||
CopyToClipboardService.CopyValueToClipboardWithTimeout(_context, _stringView.Text);
|
||||
}
|
||||
}
|
||||
}
|
44
src/PluginHostTest/EntryActivityClasses/ExtraStringView.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
internal class ExtraStringView : IStringView
|
||||
{
|
||||
private readonly View _container;
|
||||
private readonly TextView _valueView;
|
||||
private readonly TextView _keyView;
|
||||
|
||||
public ExtraStringView(LinearLayout container, TextView valueView, TextView keyView)
|
||||
{
|
||||
_container = container;
|
||||
_valueView = valueView;
|
||||
_keyView = keyView;
|
||||
}
|
||||
|
||||
public View View
|
||||
{
|
||||
get { return _container; }
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return _valueView.Text; }
|
||||
set
|
||||
{
|
||||
if (String.IsNullOrEmpty(value))
|
||||
{
|
||||
_valueView.Visibility = ViewStates.Gone;
|
||||
_keyView.Visibility = ViewStates.Gone;
|
||||
}
|
||||
else
|
||||
{
|
||||
_valueView.Visibility = ViewStates.Visible;
|
||||
_keyView.Visibility = ViewStates.Visible;
|
||||
_valueView.Text = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
34
src/PluginHostTest/EntryActivityClasses/GotoUrlMenuItem.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using Android.Graphics.Drawables;
|
||||
using PluginHostTest;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
/// <summary>
|
||||
/// Reperesents the popup menu item in EntryActivity to go to the URL in the field
|
||||
/// </summary>
|
||||
class GotoUrlMenuItem : IPopupMenuItem
|
||||
{
|
||||
private readonly EntryActivity _ctx;
|
||||
|
||||
public GotoUrlMenuItem(EntryActivity ctx)
|
||||
{
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
public Drawable Icon
|
||||
{
|
||||
get { return _ctx.Resources.GetDrawable(Android.Resource.Drawable.IcMenuUpload); }
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return _ctx.Resources.GetString(Resource.String.menu_url); }
|
||||
}
|
||||
|
||||
public void HandleClick()
|
||||
{
|
||||
//TODO
|
||||
_ctx.GotoUrl();
|
||||
}
|
||||
}
|
||||
}
|
17
src/PluginHostTest/EntryActivityClasses/IPopupMenuItem.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using Android.Graphics.Drawables;
|
||||
using KeePassLib;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for popup menu items in EntryActivity
|
||||
/// </summary>
|
||||
internal interface IPopupMenuItem
|
||||
{
|
||||
Drawable Icon { get; }
|
||||
String Text { get; }
|
||||
|
||||
void HandleClick();
|
||||
}
|
||||
}
|
7
src/PluginHostTest/EntryActivityClasses/IStringView.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace keepass2android
|
||||
{
|
||||
internal interface IStringView
|
||||
{
|
||||
string Text { set; get; }
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
using Android.Graphics.Drawables;
|
||||
using PluginHostTest;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the popup menu item in EntryActivity to open the associated attachment
|
||||
/// </summary>
|
||||
internal class OpenBinaryPopupItem : IPopupMenuItem
|
||||
{
|
||||
private readonly string _key;
|
||||
private readonly EntryActivity _entryActivity;
|
||||
|
||||
public OpenBinaryPopupItem(string key, EntryActivity entryActivity)
|
||||
{
|
||||
_key = key;
|
||||
_entryActivity = entryActivity;
|
||||
}
|
||||
|
||||
public Drawable Icon
|
||||
{
|
||||
get { return _entryActivity.Resources.GetDrawable(Android.Resource.Drawable.IcMenuShare); }
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return _entryActivity.Resources.GetString(Resource.String.SaveAttachmentDialog_open); }
|
||||
}
|
||||
|
||||
public void HandleClick()
|
||||
{
|
||||
Android.Net.Uri newUri = _entryActivity.WriteBinaryToFile(_key, true);
|
||||
if (newUri != null)
|
||||
{
|
||||
_entryActivity.OpenBinaryFile(newUri);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
14
src/PluginHostTest/EntryActivityClasses/PluginMenuOption.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using Android.Content;
|
||||
using Android.Graphics.Drawables;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
class PluginMenuOption
|
||||
{
|
||||
public string DisplayText { get; set; }
|
||||
|
||||
public Drawable Icon { get; set; }
|
||||
|
||||
public Intent Intent { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
using Android.Content;
|
||||
using Android.Graphics.Drawables;
|
||||
using Android.OS;
|
||||
using Keepass2android.Pluginsdk;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a popup menu item in EntryActivity which was added by a plugin. The click will therefore broadcast to the plugin.
|
||||
/// </summary>
|
||||
class PluginPopupMenuItem : IPopupMenuItem
|
||||
{
|
||||
private readonly EntryActivity _activity;
|
||||
private readonly string _pluginPackage;
|
||||
private readonly string _fieldId;
|
||||
private readonly string _popupItemId;
|
||||
private readonly string _displayText;
|
||||
private readonly int _iconId;
|
||||
private readonly Bundle _bundleExtra;
|
||||
|
||||
public PluginPopupMenuItem(EntryActivity activity, string pluginPackage, string fieldId, string popupItemId, string displayText, int iconId, Bundle bundleExtra)
|
||||
{
|
||||
_activity = activity;
|
||||
_pluginPackage = pluginPackage;
|
||||
_fieldId = fieldId;
|
||||
_popupItemId = popupItemId;
|
||||
_displayText = displayText;
|
||||
_iconId = iconId;
|
||||
_bundleExtra = bundleExtra;
|
||||
}
|
||||
|
||||
public Drawable Icon
|
||||
{
|
||||
get { return _activity.PackageManager.GetResourcesForApplication(_pluginPackage).GetDrawable(_iconId); }
|
||||
}
|
||||
public string Text
|
||||
{
|
||||
get { return _displayText; }
|
||||
}
|
||||
|
||||
public string PopupItemId
|
||||
{
|
||||
get { return _popupItemId; }
|
||||
}
|
||||
|
||||
public void HandleClick()
|
||||
{
|
||||
Intent i = new Intent(Strings.ActionEntryActionSelected);
|
||||
i.SetPackage(_pluginPackage);
|
||||
i.PutExtra(Strings.ExtraActionData, _bundleExtra);
|
||||
i.PutExtra(Strings.ExtraFieldId, _fieldId);
|
||||
i.PutExtra(Strings.ExtraSender, _activity.PackageName);
|
||||
|
||||
_activity.AddEntryToIntent(i);
|
||||
|
||||
_activity.SendBroadcast(i);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using Android.App;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
internal class StandardStringView : IStringView
|
||||
{
|
||||
private readonly int _viewId;
|
||||
private readonly int _containerViewId;
|
||||
private readonly Activity _activity;
|
||||
|
||||
public StandardStringView(int viewId, int containerViewId, Activity activity)
|
||||
{
|
||||
_viewId = viewId;
|
||||
_containerViewId = containerViewId;
|
||||
_activity = activity;
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
set
|
||||
{
|
||||
View container = _activity.FindViewById(_containerViewId);
|
||||
TextView tv = (TextView) _activity.FindViewById(_viewId);
|
||||
if (String.IsNullOrEmpty(value))
|
||||
{
|
||||
container.Visibility = tv.Visibility = ViewStates.Gone;
|
||||
}
|
||||
else
|
||||
{
|
||||
container.Visibility = tv.Visibility = ViewStates.Visible;
|
||||
tv.Text = value;
|
||||
}
|
||||
}
|
||||
get
|
||||
{
|
||||
TextView tv = (TextView) _activity.FindViewById(_viewId);
|
||||
return tv.Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
using Android.Graphics.Drawables;
|
||||
using PluginHostTest;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
/// <summary>
|
||||
/// Reperesents the popup menu item in EntryActivity to toggle visibility of all protected strings (e.g. Password)
|
||||
/// </summary>
|
||||
class ToggleVisibilityPopupMenuItem : IPopupMenuItem
|
||||
{
|
||||
private readonly EntryActivity _activity;
|
||||
|
||||
|
||||
public ToggleVisibilityPopupMenuItem(EntryActivity activity)
|
||||
{
|
||||
_activity = activity;
|
||||
|
||||
}
|
||||
|
||||
public Drawable Icon
|
||||
{
|
||||
get
|
||||
{
|
||||
//return new TextDrawable("\uF06E", _activity);
|
||||
return _activity.Resources.GetDrawable(Resource.Drawable.ic_action_eye_open);
|
||||
|
||||
}
|
||||
}
|
||||
public string Text
|
||||
{
|
||||
get
|
||||
{
|
||||
return _activity.Resources.GetString(
|
||||
_activity._showPassword ?
|
||||
Resource.String.menu_hide_password
|
||||
: Resource.String.show_password);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void HandleClick()
|
||||
{
|
||||
_activity.ToggleVisibility();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
using Android.Graphics.Drawables;
|
||||
using PluginHostTest;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the popup menu item in EntryActivity to store the binary attachment on SD card
|
||||
/// </summary>
|
||||
internal class WriteBinaryToFilePopupItem : IPopupMenuItem
|
||||
{
|
||||
private readonly string _key;
|
||||
private readonly EntryActivity _activity;
|
||||
|
||||
public WriteBinaryToFilePopupItem(string key, EntryActivity activity)
|
||||
{
|
||||
_key = key;
|
||||
_activity = activity;
|
||||
}
|
||||
|
||||
public Drawable Icon
|
||||
{
|
||||
get { return _activity.Resources.GetDrawable(Android.Resource.Drawable.IcMenuSave); }
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return _activity.Resources.GetString(Resource.String.SaveAttachmentDialog_save); }
|
||||
}
|
||||
|
||||
public void HandleClick()
|
||||
{
|
||||
_activity.WriteBinaryToFile(_key, false);
|
||||
}
|
||||
}
|
||||
}
|
@ -15,41 +15,49 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Android.Content;
|
||||
using Android.Runtime;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Android.Util;
|
||||
|
||||
using System;
|
||||
using Android.Runtime;
|
||||
using PluginHostTest;
|
||||
|
||||
|
||||
namespace keepass2android.view
|
||||
{
|
||||
|
||||
public class GroupHeaderView : RelativeLayout {
|
||||
public class EntryContentsView : LinearLayout {
|
||||
|
||||
public GroupHeaderView (IntPtr javaReference, JniHandleOwnership transfer)
|
||||
public EntryContentsView (IntPtr javaReference, JniHandleOwnership transfer)
|
||||
: base(javaReference, transfer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public GroupHeaderView(Context context) :base(context) {
|
||||
|
||||
public EntryContentsView(Context context):base(context, null) {
|
||||
InflateView();
|
||||
}
|
||||
|
||||
public GroupHeaderView(Context context, IAttributeSet attrs):base(context,attrs) {
|
||||
public EntryContentsView(Context context, IAttributeSet attrs): base(context, attrs) {
|
||||
|
||||
InflateView();
|
||||
}
|
||||
|
||||
private void InflateView() {
|
||||
LayoutInflater inflater = (LayoutInflater) Context.GetSystemService(Context.LayoutInflaterService);
|
||||
inflater.Inflate(Resource.Layout.group_header, this);
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
126
src/PluginHostTest/PluginArrayAdapter.cs
Normal file
@ -0,0 +1,126 @@
|
||||
using Android.Content.PM;
|
||||
using Android.Content.Res;
|
||||
using Android.Graphics.Drawables;
|
||||
using Android.Widget;
|
||||
using Android.Content;
|
||||
using Android.Views;
|
||||
using System.Collections.Generic;
|
||||
using Android.App;
|
||||
using PluginHostTest;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents information about a plugin for display in the plugin list activity
|
||||
/// </summary>
|
||||
public class PluginItem
|
||||
{
|
||||
private readonly string _package;
|
||||
private readonly Context _ctx;
|
||||
private readonly Resources _pluginRes;
|
||||
|
||||
public PluginItem(string package, string enabledStatus, Context ctx)
|
||||
{
|
||||
_package = package;
|
||||
_ctx = ctx;
|
||||
EnabledStatus = enabledStatus;
|
||||
_pluginRes = _ctx.PackageManager.GetResourcesForApplication(_package);
|
||||
}
|
||||
|
||||
public string Label
|
||||
{
|
||||
get
|
||||
{
|
||||
return PluginDetailsActivity.GetStringFromPlugin(_pluginRes, _package, "kp2aplugin_title");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public string Version
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ctx.PackageManager.GetPackageInfo(_package, 0).VersionName;
|
||||
}
|
||||
}
|
||||
|
||||
public string EnabledStatus
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Drawable Icon
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ctx.PackageManager.GetApplicationIcon(_package);
|
||||
}
|
||||
}
|
||||
|
||||
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.SetImageDrawable(item.Icon);
|
||||
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
190
src/PluginHostTest/PluginDatabase.cs
Normal file
@ -0,0 +1,190 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
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>()).Where(IsPackageInstalled);
|
||||
}
|
||||
|
||||
public bool IsPackageInstalled(string targetPackage)
|
||||
{
|
||||
try
|
||||
{
|
||||
PackageInfo info = _ctx.PackageManager.GetPackageInfo(targetPackage, PackageInfoFlags.MetaData);
|
||||
}
|
||||
catch (PackageManager.NameNotFoundException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsEnabled(string pluginPackage)
|
||||
{
|
||||
return GetPreferencesForPlugin(pluginPackage).GetString(_accessToken, null) != null;
|
||||
}
|
||||
|
||||
|
||||
public void StorePlugin(string pluginPackage, string accessToken, IList<string> requestedScopes)
|
||||
{
|
||||
ISharedPreferences pluginPrefs = GetPreferencesForPlugin(pluginPackage);
|
||||
|
||||
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)
|
||||
{
|
||||
if (pluginPackage == null)
|
||||
{
|
||||
Log.Warn(_tag, "No pluginPackage specified!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (accessToken == null)
|
||||
{
|
||||
Log.Warn(_tag, "No accessToken specified!");
|
||||
return false;
|
||||
}
|
||||
|
||||
var prefs = GetPreferencesForPlugin(pluginPackage);
|
||||
if (prefs.GetString(_accessToken, null) != accessToken)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
152
src/PluginHostTest/PluginDetailsActivity.cs
Normal file
@ -0,0 +1,152 @@
|
||||
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")]
|
||||
[IntentFilter(new[] { Strings.ActionEditPluginSettings},
|
||||
Label = AppNames.AppName,
|
||||
Categories = new[] { Intent.CategoryDefault })]
|
||||
public class PluginDetailsActivity : Activity
|
||||
{
|
||||
private CheckBox _checkbox;
|
||||
private string _pluginPackageName;
|
||||
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
base.OnCreate(bundle);
|
||||
|
||||
_pluginPackageName = Intent.GetStringExtra(Strings.ExtraPluginPackage);
|
||||
|
||||
var pluginRes = PackageManager.GetResourcesForApplication(_pluginPackageName);
|
||||
var title = GetStringFromPlugin(pluginRes, _pluginPackageName, "kp2aplugin_title");
|
||||
var author = GetStringFromPlugin(pluginRes, _pluginPackageName, "kp2aplugin_author");
|
||||
var shortDesc = GetStringFromPlugin(pluginRes, _pluginPackageName, "kp2aplugin_shortdesc");
|
||||
var version = PackageManager.GetPackageInfo(_pluginPackageName, 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);
|
||||
|
||||
_checkbox = FindViewById<CheckBox>(Resource.Id.cb_enabled);
|
||||
_checkbox.CheckedChange += delegate {
|
||||
new PluginDatabase(this).SetEnabled(_pluginPackageName, _checkbox.Checked);
|
||||
};
|
||||
|
||||
Drawable d = PackageManager.GetApplicationIcon(_pluginPackageName);
|
||||
FindViewById<ImageView>(Resource.Id.imgIcon).SetImageDrawable(d);
|
||||
|
||||
FindViewById<TextView>(Resource.Id.txtVersion).Text = version;
|
||||
|
||||
//cannot be wrong to update the view when we received an update
|
||||
PluginHost.OnReceivedRequest += OnPluginHostOnOnReceivedRequest;
|
||||
|
||||
if (Intent.Action == Strings.ActionEditPluginSettings)
|
||||
{
|
||||
//this action can be triggered by external apps so we don't know if anybody has ever triggered
|
||||
//the plugin to request access. Do this now, don't set the view right now
|
||||
PluginHost.TriggerRequest(this, _pluginPackageName, new PluginDatabase(this));
|
||||
//show the buttons instead of the checkbox
|
||||
_checkbox.Visibility = ViewStates.Invisible;
|
||||
FindViewById(Resource.Id.accept_button).Visibility = ViewStates.Visible;
|
||||
FindViewById(Resource.Id.deny_button).Visibility = ViewStates.Visible;
|
||||
|
||||
FindViewById(Resource.Id.accept_button).Click += delegate(object sender, EventArgs args)
|
||||
{
|
||||
new PluginDatabase(this).SetEnabled(_pluginPackageName, true);
|
||||
SetResult(Result.Ok);
|
||||
Finish();
|
||||
};
|
||||
|
||||
FindViewById(Resource.Id.deny_button).Click += delegate(object sender, EventArgs args)
|
||||
{
|
||||
new PluginDatabase(this).SetEnabled(_pluginPackageName, false);
|
||||
SetResult(Result.Canceled);
|
||||
Finish();
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateView();
|
||||
_checkbox.Visibility = ViewStates.Visible;
|
||||
FindViewById(Resource.Id.accept_button).Visibility = ViewStates.Gone;
|
||||
FindViewById(Resource.Id.deny_button).Visibility = ViewStates.Gone;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPluginHostOnOnReceivedRequest(object sender, PluginHost.PluginHostEventArgs args)
|
||||
{
|
||||
if (args.Package == _pluginPackageName)
|
||||
{
|
||||
UpdateView();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
PluginHost.OnReceivedRequest -= OnPluginHostOnOnReceivedRequest;
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
private void UpdateView()
|
||||
{
|
||||
var scopesContainer = FindViewById<LinearLayout>(Resource.Id.scopes_list);
|
||||
//scopesContainer.RemoveAllViews();
|
||||
|
||||
var pluginDb = new PluginDatabase(this);
|
||||
_checkbox.Checked = pluginDb.IsEnabled(_pluginPackageName);
|
||||
foreach (string scope in pluginDb.GetPluginScopes(_pluginPackageName))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
}
|
||||
}
|
174
src/PluginHostTest/PluginHost.cs
Normal file
@ -0,0 +1,174 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.Util;
|
||||
using KeePassLib.Utility;
|
||||
using Keepass2android.Pluginsdk;
|
||||
using Org.Json;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
/// <summary>
|
||||
/// Class which manages plugins inside the app
|
||||
/// </summary>
|
||||
[BroadcastReceiver()]
|
||||
[IntentFilter(new[] { Strings.ActionRequestAccess})]
|
||||
public class PluginHost: BroadcastReceiver
|
||||
{
|
||||
|
||||
private const string _tag = "KP2A_PluginHost";
|
||||
|
||||
|
||||
private static readonly string[] _validScopes = { Strings.ScopeDatabaseActions, Strings.ScopeCurrentEntry };
|
||||
|
||||
/// <summary>
|
||||
/// Sends a broadcast to all potential plugins prompting them to request access to our app.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
TriggerRequest(ctx, pkgName, pluginDatabase);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void TriggerRequest(Context ctx, string pkgName, PluginDatabase pluginDatabase)
|
||||
{
|
||||
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.");
|
||||
}
|
||||
|
||||
}
|
||||
if (OnReceivedRequest != null)
|
||||
OnReceivedRequest(this, new PluginHostEventArgs() { Package = senderPackage});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// adds the entry output data to the intent to be sent to a plugin
|
||||
/// </summary>
|
||||
public static void AddEntryToIntent(Intent intent, PwEntryOutput entry)
|
||||
{
|
||||
/*//add the entry XML
|
||||
not yet implemented. What to do with attachments?
|
||||
MemoryStream memStream = new MemoryStream();
|
||||
KdbxFile.WriteEntries(memStream, new[] {entry});
|
||||
string entryData = StrUtil.Utf8.GetString(memStream.ToArray());
|
||||
intent.PutExtra(Strings.ExtraEntryData, entryData);
|
||||
*/
|
||||
//add the 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());
|
||||
|
||||
//add field values as JSON ({ "key":"value", ... } form)
|
||||
JSONObject json = new JSONObject(outputFields);
|
||||
var jsonStr = json.ToString();
|
||||
intent.PutExtra(Strings.ExtraEntryOutputData, jsonStr);
|
||||
|
||||
//add list of which fields are protected (StringArrayExtra)
|
||||
string[] protectedFieldsList = entry.OutputStrings.Where(s=>s.Value.IsProtected).Select(s => s.Key).ToArray();
|
||||
intent.PutExtra(Strings.ExtraProtectedFieldsList, protectedFieldsList);
|
||||
|
||||
intent.PutExtra(Strings.ExtraEntryId, entry.Uuid.ToHexString());
|
||||
|
||||
}
|
||||
|
||||
public class PluginHostEventArgs
|
||||
{
|
||||
public string Package { get; set; }
|
||||
}
|
||||
public static event EventHandler<PluginHostEventArgs> OnReceivedRequest;
|
||||
}
|
||||
}
|
712
src/PluginHostTest/PluginHostTest.csproj
Normal file
@ -0,0 +1,712 @@
|
||||
<?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.2</TargetFrameworkVersion>
|
||||
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</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="App.cs" />
|
||||
<Compile Include="AppNames.cs" />
|
||||
<Compile Include="ClickView.cs" />
|
||||
<Compile Include="CopyToClipboardService.cs" />
|
||||
<Compile Include="EntryActivity.cs" />
|
||||
<Compile Include="EntryActivityClasses\CopyToClipboardPopupMenuIcon.cs" />
|
||||
<Compile Include="EntryActivityClasses\GotoUrlMenuItem.cs" />
|
||||
<Compile Include="EntryActivityClasses\ToggleVisibilityPopupMenuItem.cs" />
|
||||
<Compile Include="EntryContentsView.cs" />
|
||||
<Compile Include="EntrySection.cs" />
|
||||
<Compile Include="EntryActivityClasses\ExtraStringView.cs" />
|
||||
<Compile Include="EntryActivityClasses\IPopupMenuItem.cs" />
|
||||
<Compile Include="EntryActivityClasses\IStringView.cs" />
|
||||
<Compile Include="Intents.cs" />
|
||||
<Compile Include="Kp2aShortHelpView.cs" />
|
||||
<Compile Include="EntryActivityClasses\OpenBinaryPopupItem.cs" />
|
||||
<Compile Include="PluginDatabase.cs" />
|
||||
<Compile Include="PluginDetailsActivity.cs" />
|
||||
<Compile Include="PluginArrayAdapter.cs" />
|
||||
<Compile Include="PluginListActivity.cs" />
|
||||
<Compile Include="PluginHost.cs" />
|
||||
<Compile Include="EntryActivityClasses\PluginMenuOption.cs" />
|
||||
<Compile Include="EntryActivityClasses\PluginPopupMenuItem.cs" />
|
||||
<Compile Include="PwEntryOutput.cs" />
|
||||
<Compile Include="Resources\Resource.Designer.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SprCompileFlags.cs" />
|
||||
<Compile Include="SprContext.cs" />
|
||||
<Compile Include="SprEngine.cs" />
|
||||
<Compile Include="EntryActivityClasses\StandardStringView.cs" />
|
||||
<Compile Include="TextDrawable.cs" />
|
||||
<Compile Include="TextViewSelect.cs" />
|
||||
<Compile Include="TextWithHelp.cs" />
|
||||
<Compile Include="EntryActivityClasses\WriteBinaryToFilePopupItem.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidAsset Include="Assets\DejaVuSansMono.ttf" />
|
||||
<AndroidAsset Include="Assets\fontawesome-webfont.ttf" />
|
||||
<None Include="Resources\AboutResources.txt" />
|
||||
<None Include="Assets\AboutAssets.txt" />
|
||||
<AndroidResource Include="Resources\Layout\plugin_list.xml">
|
||||
<SubType>AndroidResource</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\Layout\sftp_credentials.axml" />
|
||||
</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="..\KeePassLib2Android\KeePassLib2Android.csproj">
|
||||
<Project>{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}</Project>
|
||||
<Name>KeePassLib2Android</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\PluginSdkBinding\PluginSdkBinding.csproj">
|
||||
<Project>{3da3911e-36de-465e-8f15-f1991b6437e5}</Project>
|
||||
<Name>PluginSdkBinding</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\ListViewPluginRow.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\plugin_details.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</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\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>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\vdots_holodark.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\vdots.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Drawable\ic_menu_copy_holo_light.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Menu\entry.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\about.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\create_database.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\database_settings.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\donate.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\donate_bday.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\donate_bdaymissed.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\edit_extra_string_dialog.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\file_row.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\file_selection.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\file_selection_buttons.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\file_selection_filename.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\file_selection_no_recent.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\file_storage_setup.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\filestorage_selection.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\filestorage_selection_listitem.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\filestorage_selection_listitem_kp2a.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\generate_password.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\group_add_entry.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\group_edit.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\group_header.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\group_list_entry.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\icon.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\icon_picker.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\InViewButton.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\password.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\QuickUnlock.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\QuickUnlock_Unused.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\SaveButton.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\search.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\searchurlresults.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\searchurlresults_empty.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\set_password.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\sftpcredentials.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\StartScreenButtons.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Layout\url_credentials.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Values\styles_dark.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\Values\styles_light.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>
|
58
src/PluginHostTest/PluginListActivity.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Android.Widget;
|
||||
using Keepass2android.Pluginsdk;
|
||||
using PluginHostTest;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
//TODO theme?
|
||||
[Activity (Label = "Plugins (TODO)", ConfigurationChanges=ConfigChanges.Orientation|ConfigChanges.KeyboardHidden )]
|
||||
public class PluginListActivity : ListActivity
|
||||
{
|
||||
private PluginArrayAdapter _pluginArrayAdapter;
|
||||
private List<PluginItem> _items;
|
||||
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
base.OnCreate(bundle);
|
||||
|
||||
//TODO _design.ApplyTheme();
|
||||
|
||||
SetContentView(Resource.Layout.plugin_list);
|
||||
|
||||
ListView listView = FindViewById<ListView>(Android.Resource.Id.List);
|
||||
listView.ItemClick +=
|
||||
(sender, args) =>
|
||||
{
|
||||
Intent i = new Intent(this, typeof(PluginDetailsActivity));
|
||||
i.PutExtra(Strings.ExtraPluginPackage, _items[args.Position].Package);
|
||||
StartActivity(i);
|
||||
};
|
||||
|
||||
// Create your application here
|
||||
}
|
||||
protected override void OnResume()
|
||||
{
|
||||
base.OnResume();
|
||||
PluginDatabase pluginDb = new PluginDatabase(this);
|
||||
|
||||
_items = (from pluginPackage in pluginDb.GetAllPluginPackages()
|
||||
let version = PackageManager.GetPackageInfo(pluginPackage, 0).VersionName
|
||||
let enabledStatus = pluginDb.IsEnabled(pluginPackage) ? GetString(Resource.String.plugin_enabled) : GetString(Resource.String.plugin_disabled)
|
||||
select new PluginItem(pluginPackage, enabledStatus, this)).ToList();
|
||||
/*
|
||||
{
|
||||
new PluginItem("PluginA", Resource.Drawable.Icon, "keepass2android.plugina", "connected"),
|
||||
new PluginItem("KeepassNFC", Resource.Drawable.Icon, "com.bla.blubb.plugina", "disconnected")
|
||||
};
|
||||
* */
|
||||
_pluginArrayAdapter = new PluginArrayAdapter(this, Resource.Layout.ListViewPluginRow, _items);
|
||||
ListAdapter = _pluginArrayAdapter;
|
||||
}
|
||||
}
|
||||
}
|
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="14" android:targetSdkVersion="17" />
|
||||
<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>
|
||||
|
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 |