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 | 
 Philipp Crocoll
					Philipp Crocoll