From f6d0778a467bda59d551e6c42d298e2cbbd49689 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Fri, 9 Oct 2015 13:08:11 +0200 Subject: [PATCH] First version of AutoFill Plugin --- src/AutoFillPlugin/AccessReceiver.cs | 37 ++++ src/AutoFillPlugin/Assets/AboutAssets.txt | 19 ++ src/AutoFillPlugin/AutoFillPlugin.csproj | 105 +++++++++ src/AutoFillPlugin/Credentials.cs | 21 ++ src/AutoFillPlugin/GettingStarted.Xamarin | 4 + .../Kp2aAccessibilityService.cs | 207 ++++++++++++++++++ .../LookupCredentialsActivity.cs | 56 +++++ src/AutoFillPlugin/MainActivity.cs | 25 +++ .../Properties/AndroidManifest.xml | 5 + src/AutoFillPlugin/Properties/AssemblyInfo.cs | 30 +++ .../Resources/AboutResources.txt | 50 +++++ .../drawable-xhdpi/ic_notify_keyboard.png | Bin 0 -> 15815 bytes .../Resources/drawable/Icon.png | Bin 0 -> 4147 bytes .../Resources/values/Strings.xml | 8 + .../Resources/xml/accserviceconfig.xml | 11 + src/KeePass.sln | 42 +++- 16 files changed, 618 insertions(+), 2 deletions(-) create mode 100644 src/AutoFillPlugin/AccessReceiver.cs create mode 100644 src/AutoFillPlugin/Assets/AboutAssets.txt create mode 100644 src/AutoFillPlugin/AutoFillPlugin.csproj create mode 100644 src/AutoFillPlugin/Credentials.cs create mode 100644 src/AutoFillPlugin/GettingStarted.Xamarin create mode 100644 src/AutoFillPlugin/Kp2aAccessibilityService.cs create mode 100644 src/AutoFillPlugin/LookupCredentialsActivity.cs create mode 100644 src/AutoFillPlugin/MainActivity.cs create mode 100644 src/AutoFillPlugin/Properties/AndroidManifest.xml create mode 100644 src/AutoFillPlugin/Properties/AssemblyInfo.cs create mode 100644 src/AutoFillPlugin/Resources/AboutResources.txt create mode 100644 src/AutoFillPlugin/Resources/drawable-xhdpi/ic_notify_keyboard.png create mode 100644 src/AutoFillPlugin/Resources/drawable/Icon.png create mode 100644 src/AutoFillPlugin/Resources/values/Strings.xml create mode 100644 src/AutoFillPlugin/Resources/xml/accserviceconfig.xml diff --git a/src/AutoFillPlugin/AccessReceiver.cs b/src/AutoFillPlugin/AccessReceiver.cs new file mode 100644 index 00000000..a8182f73 --- /dev/null +++ b/src/AutoFillPlugin/AccessReceiver.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Android.App; +using Android.Content; +using Android.OS; +using Android.Runtime; +using Android.Views; +using Android.Widget; +using Keepass2android.Pluginsdk; + +namespace keepass2android.AutoFillPlugin +{ + [BroadcastReceiver(Exported = true)] + [IntentFilter(new[] { Strings.ActionTriggerRequestAccess, Strings.ActionReceiveAccess, Strings.ActionRevokeAccess })] + public class AccessReceiver : PluginAccessBroadcastReceiver + { + public override void OnReceive(Context context, Intent intent) + { + Android.Util.Log.Debug("KP2AAS", intent.Action); + base.OnReceive(context, intent); + } + + public override IList Scopes + { + get + { + return new List + { + Strings.ScopeQueryCredentials + }; + } + } + } +} \ No newline at end of file diff --git a/src/AutoFillPlugin/Assets/AboutAssets.txt b/src/AutoFillPlugin/Assets/AboutAssets.txt new file mode 100644 index 00000000..ee398862 --- /dev/null +++ b/src/AutoFillPlugin/Assets/AboutAssets.txt @@ -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"); \ No newline at end of file diff --git a/src/AutoFillPlugin/AutoFillPlugin.csproj b/src/AutoFillPlugin/AutoFillPlugin.csproj new file mode 100644 index 00000000..93850d6e --- /dev/null +++ b/src/AutoFillPlugin/AutoFillPlugin.csproj @@ -0,0 +1,105 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {6FF440E6-E8FF-4E43-8221-9E3972F14812} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Properties + keepass2android.AutoFillPlugin + AutoFillPlugin + 512 + true + Resources\Resource.Designer.cs + Off + True + v5.0 + Properties\AndroidManifest.xml + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + True + None + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + False + SdkOnly + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + + + + + + {545b4a6b-8bba-4fbe-92fc-4ac060122a54} + KeePassLib2Android + + + {3da3911e-36de-465e-8f15-f1991b6437e5} + PluginSdkBinding + + + + + + + + \ No newline at end of file diff --git a/src/AutoFillPlugin/Credentials.cs b/src/AutoFillPlugin/Credentials.cs new file mode 100644 index 00000000..8f09f772 --- /dev/null +++ b/src/AutoFillPlugin/Credentials.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Android.App; +using Android.Content; +using Android.OS; +using Android.Runtime; +using Android.Views; +using Android.Widget; + +namespace keepass2android.AutoFillPlugin +{ + public class Credentials + { + public string User; + public string Password; + public string Url; + } +} \ No newline at end of file diff --git a/src/AutoFillPlugin/GettingStarted.Xamarin b/src/AutoFillPlugin/GettingStarted.Xamarin new file mode 100644 index 00000000..e9d4f6a4 --- /dev/null +++ b/src/AutoFillPlugin/GettingStarted.Xamarin @@ -0,0 +1,4 @@ + + GS\Android\CS\AndroidApp\GettingStarted.html + false + \ No newline at end of file diff --git a/src/AutoFillPlugin/Kp2aAccessibilityService.cs b/src/AutoFillPlugin/Kp2aAccessibilityService.cs new file mode 100644 index 00000000..52b981d9 --- /dev/null +++ b/src/AutoFillPlugin/Kp2aAccessibilityService.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Android.App; +using Android.Content; +using Android.OS; +using Android.Runtime; +using Android.Views; +using Android.Views.Accessibility; +using Android.Widget; + + +namespace keepass2android.AutoFillPlugin +{ + // + [Service(Enabled =true, Permission= "android.permission.BIND_ACCESSIBILITY_SERVICE")] + [IntentFilter(new[] { "android.accessibilityservice.AccessibilityService" })] + [MetaData("android.accessibilityservice", Resource = "@xml/accserviceconfig")] + public class Kp2aAccessibilityService : Android.AccessibilityServices.AccessibilityService, IDialogInterfaceOnCancelListener + { + const string _logTag = "KP2AAS"; + private const int autoFillNotificationId = 0; + private const string androidAppPrefix = "androidapp://"; + + public override void OnCreate() + { + base.OnCreate(); + Android.Util.Log.Debug(_logTag, "OnCreate Service"); + } + + protected override void OnServiceConnected() + { + Android.Util.Log.Debug(_logTag, "service connected"); + base.OnServiceConnected(); + } + + public override void OnAccessibilityEvent(AccessibilityEvent e) + { + + Android.Util.Log.Debug(_logTag, "OnAccEvent"); + bool cancelNotification = true; + if (e.EventType == EventTypes.WindowContentChanged || e.EventType == EventTypes.WindowStateChanged) + { + Android.Util.Log.Debug(_logTag, "event: " + e.EventType + ", package = " + e.PackageName); + var root = RootInActiveWindow; + if ((ExistsNodeOrChildren(root, n => n.WindowId == e.WindowId) && !ExistsNodeOrChildren(root, n => (n.ViewIdResourceName != null) && (n.ViewIdResourceName.StartsWith("com.android.systemui"))))) + { + var allEditTexts = GetNodeOrChildren(root, n=> { return IsEditText(n); }); + + var usernameEdit = allEditTexts.TakeWhile(edit => (edit.Password == false)).LastOrDefault(); + + string searchString = androidAppPrefix + root.PackageName; + + string url = androidAppPrefix + root.PackageName; + + if (root.PackageName == "com.android.chrome") + { + var addressField = root.FindAccessibilityNodeInfosByViewId("com.android.chrome:id/url_bar").FirstOrDefault(); + UrlFromAddressField(ref url, addressField); + + } + else if (root.PackageName == "com.android.browser") + { + var addressField = root.FindAccessibilityNodeInfosByViewId("com.android.browser:id/url").FirstOrDefault(); + UrlFromAddressField(ref url, addressField); + } + + var emptyPasswordFields = GetNodeOrChildren(root, n => { return IsPasswordField(n); }).ToList(); + if (emptyPasswordFields.Any()) + { + if ((LookupCredentialsActivity.LastReceivedCredentials != null) && (LookupCredentialsActivity.LastReceivedCredentials.Url == url)) + { + FillPassword(url, usernameEdit, emptyPasswordFields); + } + else + { + AskFillPassword(url, usernameEdit, emptyPasswordFields); + cancelNotification = false; + } + + } + + } + + } + if (cancelNotification) + ((NotificationManager)GetSystemService(NotificationService)).Cancel(autoFillNotificationId); + + } + private static void UrlFromAddressField(ref string url, AccessibilityNodeInfo addressField) + { + if (addressField != null) + { + url = addressField.Text; + if (!url.Contains("://")) + url = "http://" + url; + } + + } + + private static bool IsPasswordField(AccessibilityNodeInfo n) + { + //if (n.Password) Android.Util.Log.Debug(_logTag, "pwdx with " + (n.Text == null ? "null" : n.Text)); + var res = n.Password && string.IsNullOrEmpty(n.Text); + // if (n.Password) Android.Util.Log.Debug(_logTag, "pwd with " + n.Text + res); + return res; + } + + private static bool IsEditText(AccessibilityNodeInfo n) + { + //it seems like n.Editable is not a good check as this is false for some fields which are actually editable, at least in tests with Chrome. + return (n.ClassName != null) && (n.ClassName.Contains("EditText")); + } + + private void AskFillPassword(string url, AccessibilityNodeInfo usernameEdit, IEnumerable passwordFields) + { + var runSearchIntent = new Intent(this, typeof(LookupCredentialsActivity)); + runSearchIntent.PutExtra("url", url); + runSearchIntent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop); + var pending = PendingIntent.GetActivity(this, 0, runSearchIntent, PendingIntentFlags.UpdateCurrent); + + var targetName = url; + + if (url.StartsWith(androidAppPrefix)) + { + var packageName = url.Substring(androidAppPrefix.Length); + try + { + targetName = PackageManager.GetPackageInfo(packageName, 0).ApplicationInfo.Name; + } + catch (Exception e) + { + Android.Util.Log.Debug(_logTag, e.ToString()); + targetName = packageName; + } + } + else + { + targetName = KeePassLib.Utility.UrlUtil.GetHost(url); + } + + + var builder = new Notification.Builder(this); + //TODO icon + //TODO plugin icon + builder.SetSmallIcon(Resource.Drawable.ic_notify_keyboard) + .SetContentText(GetString(Resource.String.NotificationContentText, new Java.Lang.Object[] { targetName })) + .SetContentTitle(GetString(Resource.String.NotificationTitle)) + .SetWhen(Java.Lang.JavaSystem.CurrentTimeMillis()) + .SetTicker( GetString(Resource.String.NotificationTickerText, new Java.Lang.Object[] { targetName })) + .SetVisibility(Android.App.NotificationVisibility.Secret) + .SetContentIntent(pending); + var notificationManager = (NotificationManager)GetSystemService(NotificationService); + notificationManager.Notify(autoFillNotificationId, builder.Build()); + + } + + private void FillPassword(string url, AccessibilityNodeInfo usernameEdit, IEnumerable passwordFields) + { + + FillDataInTextField(usernameEdit, LookupCredentialsActivity.LastReceivedCredentials.User); + foreach (var pwd in passwordFields) + FillDataInTextField(pwd, LookupCredentialsActivity.LastReceivedCredentials.Password); + + LookupCredentialsActivity.LastReceivedCredentials = null; + } + + private static void FillDataInTextField(AccessibilityNodeInfo edit, string newValue) + { + Bundle b = new Bundle(); + b.PutString(AccessibilityNodeInfo.ActionArgumentSetTextCharsequence, newValue); + edit.PerformAction(Android.Views.Accessibility.Action.SetText, b); + } + + private bool ExistsNodeOrChildren(AccessibilityNodeInfo n, Func p) + { + return GetNodeOrChildren(n, p).Any(); + } + + private IEnumerable GetNodeOrChildren(AccessibilityNodeInfo n, Func p) + { + if (n != null) + { + if (p(n)) + yield return n; + for (int i = 0; i < n.ChildCount; i++) + { + foreach (var x in GetNodeOrChildren(n.GetChild(i), p)) + yield return x; + } + } + + } + + public override void OnInterrupt() + { + + } + + public void OnCancel(IDialogInterface dialog) + { + + } + } +} \ No newline at end of file diff --git a/src/AutoFillPlugin/LookupCredentialsActivity.cs b/src/AutoFillPlugin/LookupCredentialsActivity.cs new file mode 100644 index 00000000..9777df6e --- /dev/null +++ b/src/AutoFillPlugin/LookupCredentialsActivity.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Android.App; +using Android.Content; +using Android.OS; +using Android.Runtime; +using Android.Views; +using Android.Widget; + +using Keepass2android.Pluginsdk; + +namespace keepass2android.AutoFillPlugin +{ + [Activity(Label = "@string/LookupTitle", LaunchMode = Android.Content.PM.LaunchMode.SingleInstance)] + public class LookupCredentialsActivity : Activity + { + protected override void OnCreate(Bundle bundle) + { + base.OnCreate(bundle); + + var url = Intent.GetStringExtra("url"); + _lastQueriedUrl = url; + StartActivityForResult(Kp2aControl.GetQueryEntryIntent(url), 123); + } + + string _lastQueriedUrl; + + protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data) + { + base.OnActivityResult(requestCode, resultCode, data); + + var jsonOutput = new Org.Json.JSONObject(data.GetStringExtra(Strings.ExtraEntryOutputData)); + Dictionary output = new Dictionary(); + for (var iter = jsonOutput.Keys(); iter.HasNext;) + { + string key = iter.Next().ToString(); + string value = jsonOutput.Get(key).ToString(); + output[key] = value; + } + + + string user = "", password = ""; + output.TryGetValue(KeePassLib.PwDefs.UserNameField, out user); + output.TryGetValue(KeePassLib.PwDefs.PasswordField, out password); + + LastReceivedCredentials = new Credentials() { User = user, Password = password, Url = _lastQueriedUrl }; + + Finish(); + } + + public static Credentials LastReceivedCredentials; + } +} \ No newline at end of file diff --git a/src/AutoFillPlugin/MainActivity.cs b/src/AutoFillPlugin/MainActivity.cs new file mode 100644 index 00000000..71c31ecc --- /dev/null +++ b/src/AutoFillPlugin/MainActivity.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Android.App; +using Android.Content; +using Android.OS; +using Android.Runtime; +using Android.Views; +using Android.Widget; + +namespace keepass2android.AutoFillPlugin +{ + [Activity(Label = "MainActivity", MainLauncher =true)] + public class MainActivity : Activity + { + protected override void OnCreate(Bundle bundle) + { + base.OnCreate(bundle); + + // Create your application here + } + } +} \ No newline at end of file diff --git a/src/AutoFillPlugin/Properties/AndroidManifest.xml b/src/AutoFillPlugin/Properties/AndroidManifest.xml new file mode 100644 index 00000000..ef6e7cf6 --- /dev/null +++ b/src/AutoFillPlugin/Properties/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/AutoFillPlugin/Properties/AssemblyInfo.cs b/src/AutoFillPlugin/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..a22aab22 --- /dev/null +++ b/src/AutoFillPlugin/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +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("AutoFillPlugin")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AutoFillPlugin")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[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")] diff --git a/src/AutoFillPlugin/Resources/AboutResources.txt b/src/AutoFillPlugin/Resources/AboutResources.txt new file mode 100644 index 00000000..194ae28a --- /dev/null +++ b/src/AutoFillPlugin/Resources/AboutResources.txt @@ -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. \ No newline at end of file diff --git a/src/AutoFillPlugin/Resources/drawable-xhdpi/ic_notify_keyboard.png b/src/AutoFillPlugin/Resources/drawable-xhdpi/ic_notify_keyboard.png new file mode 100644 index 0000000000000000000000000000000000000000..2e473aa2261de06a93d94931d7739bc8714a84fd GIT binary patch literal 15815 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 literal 0 HcmV?d00001 diff --git a/src/AutoFillPlugin/Resources/values/Strings.xml b/src/AutoFillPlugin/Resources/values/Strings.xml new file mode 100644 index 00000000..d9e359e7 --- /dev/null +++ b/src/AutoFillPlugin/Resources/values/Strings.xml @@ -0,0 +1,8 @@ + + + Look up credentials + KP2A AutoFillPlugin + Keepass2Android AutoFill + AutoFill form for %1$s + AutoFill available for %1$s + diff --git a/src/AutoFillPlugin/Resources/xml/accserviceconfig.xml b/src/AutoFillPlugin/Resources/xml/accserviceconfig.xml new file mode 100644 index 00000000..5f9db482 --- /dev/null +++ b/src/AutoFillPlugin/Resources/xml/accserviceconfig.xml @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/src/KeePass.sln b/src/KeePass.sln index 35046706..50351742 100644 --- a/src/KeePass.sln +++ b/src/KeePass.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.23107.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePassLib2Android", "KeePassLib2Android\KeePassLib2Android.csproj", "{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}" EndProject @@ -39,6 +39,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZlibAndroid", "ZlibAndroid\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaterialTest2", "MaterialTest2\MaterialTest2.csproj", "{B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoFillPlugin", "AutoFillPlugin\AutoFillPlugin.csproj", "{6FF440E6-E8FF-4E43-8221-9E3972F14812}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -408,6 +410,42 @@ Global {B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.ReleaseNoNet|Mixed Platforms.Deploy.0 = Release|Any CPU {B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU {B7BBC4A2-0301-4DFF-B03C-C88CD4F1F890}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Debug|Win32.ActiveCfg = Debug|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Debug|Win32.Build.0 = Debug|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Debug|Win32.Deploy.0 = Debug|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Debug|x64.ActiveCfg = Debug|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Debug|x64.Build.0 = Debug|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Debug|x64.Deploy.0 = Debug|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Release|Any CPU.Build.0 = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Release|Any CPU.Deploy.0 = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Release|Win32.ActiveCfg = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Release|Win32.Build.0 = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Release|Win32.Deploy.0 = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Release|x64.ActiveCfg = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Release|x64.Build.0 = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.Release|x64.Deploy.0 = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.ReleaseNoNet|Any CPU.Deploy.0 = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.ReleaseNoNet|Mixed Platforms.Deploy.0 = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.ReleaseNoNet|Win32.Deploy.0 = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.ReleaseNoNet|x64.Build.0 = Release|Any CPU + {6FF440E6-E8FF-4E43-8221-9E3972F14812}.ReleaseNoNet|x64.Deploy.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE