diff --git a/.gitignore b/.gitignore
index af8e3100..42b39152 100644
--- a/.gitignore
+++ b/.gitignore
@@ -153,3 +153,4 @@ intermediates
/src/java/Kp2aAccServiceLib/app/app.iml
/src/java/Kp2aAccServiceLib/gradle
adbprompt.ps1
+/src/java/KP2ASoftkeyboard_AS/build/android-profile/*.rawproto
diff --git a/src/AndroidFileChooserBinding/AndroidFileChooserBinding.csproj b/src/AndroidFileChooserBinding/AndroidFileChooserBinding.csproj
index 3fe01d37..3956876e 100644
--- a/src/AndroidFileChooserBinding/AndroidFileChooserBinding.csproj
+++ b/src/AndroidFileChooserBinding/AndroidFileChooserBinding.csproj
@@ -10,7 +10,7 @@
AndroidFileChooserBinding
AndroidFileChooserBinding
512
- v6.0
+ v8.0
True
diff --git a/src/JavaFileStorageBindings/JavaFileStorageBindings.csproj b/src/JavaFileStorageBindings/JavaFileStorageBindings.csproj
index 65d58114..7433f4f8 100644
--- a/src/JavaFileStorageBindings/JavaFileStorageBindings.csproj
+++ b/src/JavaFileStorageBindings/JavaFileStorageBindings.csproj
@@ -11,7 +11,7 @@
JavaFileStorageBindings
512
True
- v6.0
+ v8.0
true
diff --git a/src/KP2AKdbLibraryBinding/KP2AKdbLibraryBinding.csproj b/src/KP2AKdbLibraryBinding/KP2AKdbLibraryBinding.csproj
index a0ed90dd..3ff0ddff 100644
--- a/src/KP2AKdbLibraryBinding/KP2AKdbLibraryBinding.csproj
+++ b/src/KP2AKdbLibraryBinding/KP2AKdbLibraryBinding.csproj
@@ -10,7 +10,7 @@
KP2AKdbLibraryBinding
KP2AKdbLibraryBinding
512
- v6.0
+ v8.0
True
diff --git a/src/KeePass.sln b/src/KeePass.sln
index 4c59d566..dc4bfbc0 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 15
+VisualStudioVersion = 15.0.27004.2009
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePassLib2Android", "KeePassLib2Android\KeePassLib2Android.csproj", "{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}"
EndProject
@@ -27,6 +27,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.FtpClient.Androi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamsungPass", "SamsungPass\Xamarin.SamsungPass\SamsungPass\SamsungPass.csproj", "{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kp2aAutofillBinding", "Kp2aAutofillBinding\Kp2aAutofillBinding.csproj", "{39D433EC-253C-44D8-89A0-85C06A41FB8C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kp2aAutofillLib", "Kp2aAutofillLib\Kp2aAutofillLib.csproj", "{612D3DD7-995B-4484-8D40-1607889EF0B8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutofillTestApp", "AutofillTestApp\AutofillTestApp.csproj", "{C75CFA4A-F969-4E27-B9AC-A73706B10E39}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -283,10 +289,97 @@ Global
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Win32.ActiveCfg = ReleaseNoNet|Any CPU
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Debug|Win32.Build.0 = Debug|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Debug|x64.Build.0 = Debug|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Release|Win32.ActiveCfg = Release|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Release|Win32.Build.0 = Release|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Release|x64.ActiveCfg = Release|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.Release|x64.Build.0 = Release|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
+ {39D433EC-253C-44D8-89A0-85C06A41FB8C}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Debug|Win32.Build.0 = Debug|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Debug|x64.Build.0 = Debug|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Release|Win32.ActiveCfg = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Release|Win32.Build.0 = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Release|x64.ActiveCfg = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.Release|x64.Build.0 = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
+ {612D3DD7-995B-4484-8D40-1607889EF0B8}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Debug|Win32.Build.0 = Debug|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Debug|Win32.Deploy.0 = Debug|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Debug|x64.Build.0 = Debug|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Debug|x64.Deploy.0 = Debug|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Release|Win32.ActiveCfg = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Release|Win32.Build.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Release|Win32.Deploy.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Release|x64.ActiveCfg = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Release|x64.Build.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.Release|x64.Deploy.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.ReleaseNoNet|Any CPU.Deploy.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.ReleaseNoNet|Mixed Platforms.Deploy.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.ReleaseNoNet|Win32.Deploy.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
+ {C75CFA4A-F969-4E27-B9AC-A73706B10E39}.ReleaseNoNet|x64.Deploy.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {2B48EDA2-ABCE-4DB5-A609-DFDF5FAAE767}
+ EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
Policies = $0
$0.DotNetNamingPolicy = $1
diff --git a/src/KeePassLib2Android/KeePassLib2Android.csproj b/src/KeePassLib2Android/KeePassLib2Android.csproj
index abda7528..d3029e5f 100644
--- a/src/KeePassLib2Android/KeePassLib2Android.csproj
+++ b/src/KeePassLib2Android/KeePassLib2Android.csproj
@@ -12,7 +12,7 @@
Resources\Resource.designer.cs
Resource
KeePassLib2Android
- v6.0
+ v8.0
True
8482b288
diff --git a/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj b/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj
index 50b23c77..c3b7bb60 100644
--- a/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj
+++ b/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj
@@ -12,7 +12,7 @@
512
Resources\Resource.Designer.cs
Off
- v6.0
+ v8.0
true
06ffb71c
diff --git a/src/Kp2aKeyboardBinding/Kp2aKeyboardBinding.csproj b/src/Kp2aKeyboardBinding/Kp2aKeyboardBinding.csproj
index 8d8c5fae..df9c7b3b 100644
--- a/src/Kp2aKeyboardBinding/Kp2aKeyboardBinding.csproj
+++ b/src/Kp2aKeyboardBinding/Kp2aKeyboardBinding.csproj
@@ -11,7 +11,7 @@
Resources
Kp2aKeyboardBinding
True
- v6.0
+ v8.0
True
diff --git a/src/PluginSdkBinding/PluginSdkBinding.csproj b/src/PluginSdkBinding/PluginSdkBinding.csproj
index 1dc53f91..ad38386a 100644
--- a/src/PluginSdkBinding/PluginSdkBinding.csproj
+++ b/src/PluginSdkBinding/PluginSdkBinding.csproj
@@ -11,7 +11,7 @@
PluginSdkBinding
512
True
- v6.0
+ v8.0
true
@@ -53,8 +53,8 @@
-
- Jars\app-debug.aar
+
+ Jars\Keepass2AndroidPluginSDK2-debug.aar
diff --git a/src/SamsungPass b/src/SamsungPass
index 95bc88e1..53d6fee0 160000
--- a/src/SamsungPass
+++ b/src/SamsungPass
@@ -1 +1 @@
-Subproject commit 95bc88e1826f64ccb9fb7e68d7f77edb12ac73ef
+Subproject commit 53d6fee0e227aa10b37649836fb8676d842180a5
diff --git a/src/TwofishCipher/TwofishCipher.csproj b/src/TwofishCipher/TwofishCipher.csproj
index 48337a23..2368da5f 100644
--- a/src/TwofishCipher/TwofishCipher.csproj
+++ b/src/TwofishCipher/TwofishCipher.csproj
@@ -13,7 +13,7 @@
Resources\Resource.Designer.cs
Off
True
- v6.0
+ v8.0
true
diff --git a/src/ZlibAndroid/ZlibAndroid.csproj b/src/ZlibAndroid/ZlibAndroid.csproj
index d2238c67..76403f80 100644
--- a/src/ZlibAndroid/ZlibAndroid.csproj
+++ b/src/ZlibAndroid/ZlibAndroid.csproj
@@ -13,7 +13,7 @@
Assets
True
ZlibAndroid
- v6.0
+ v8.0
true
diff --git a/src/java/JavaFileStorageTest-AS/build.gradle b/src/java/JavaFileStorageTest-AS/build.gradle
index dc21c09e..2189cb68 100644
--- a/src/java/JavaFileStorageTest-AS/build.gradle
+++ b/src/java/JavaFileStorageTest-AS/build.gradle
@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.3.2'
+ classpath 'com.android.tools.build:gradle:3.0.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/src/java/JavaFileStorageTest-AS/gradle/wrapper/gradle-wrapper.properties b/src/java/JavaFileStorageTest-AS/gradle/wrapper/gradle-wrapper.properties
index 1ef1f1f2..10b45347 100644
--- a/src/java/JavaFileStorageTest-AS/gradle/wrapper/gradle-wrapper.properties
+++ b/src/java/JavaFileStorageTest-AS/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon May 22 10:43:15 CEST 2017
+#Mon Dec 18 11:13:13 CET 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
diff --git a/src/java/Keepass2AndroidPluginSDK2/app/build.gradle b/src/java/Keepass2AndroidPluginSDK2/app/build.gradle
index c6a22a32..1e620b4c 100644
--- a/src/java/Keepass2AndroidPluginSDK2/app/build.gradle
+++ b/src/java/Keepass2AndroidPluginSDK2/app/build.gradle
@@ -1,7 +1,7 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 'Google Inc.:Google APIs:23'
- buildToolsVersion '23.0.1'
+ buildToolsVersion '26.0.2'
defaultConfig {
minSdkVersion 8
diff --git a/src/java/Keepass2AndroidPluginSDK2/app/src/main/AndroidManifest.xml b/src/java/Keepass2AndroidPluginSDK2/app/src/main/AndroidManifest.xml
index 91aef12e..1a0bef65 100644
--- a/src/java/Keepass2AndroidPluginSDK2/app/src/main/AndroidManifest.xml
+++ b/src/java/Keepass2AndroidPluginSDK2/app/src/main/AndroidManifest.xml
@@ -10,7 +10,6 @@
diff --git a/src/java/Keepass2AndroidPluginSDK2/app/src/main/res/drawable-hdpi/ic_launcher.png b/src/java/Keepass2AndroidPluginSDK2/app/src/main/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 96a442e5..00000000
Binary files a/src/java/Keepass2AndroidPluginSDK2/app/src/main/res/drawable-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/java/Keepass2AndroidPluginSDK2/app/src/main/res/drawable-mdpi/ic_launcher.png b/src/java/Keepass2AndroidPluginSDK2/app/src/main/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 359047df..00000000
Binary files a/src/java/Keepass2AndroidPluginSDK2/app/src/main/res/drawable-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/java/Keepass2AndroidPluginSDK2/app/src/main/res/drawable-xhdpi/ic_launcher.png b/src/java/Keepass2AndroidPluginSDK2/app/src/main/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 71c6d760..00000000
Binary files a/src/java/Keepass2AndroidPluginSDK2/app/src/main/res/drawable-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/java/Keepass2AndroidPluginSDK2/build.gradle b/src/java/Keepass2AndroidPluginSDK2/build.gradle
index 88d246d4..4f430187 100644
--- a/src/java/Keepass2AndroidPluginSDK2/build.gradle
+++ b/src/java/Keepass2AndroidPluginSDK2/build.gradle
@@ -4,7 +4,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.2.3'
+ classpath 'com.android.tools.build:gradle:2.2.1'
}
}
diff --git a/src/java/Keepass2AndroidPluginSDK2/gradle/wrapper/gradle-wrapper.properties b/src/java/Keepass2AndroidPluginSDK2/gradle/wrapper/gradle-wrapper.properties
index 0c71e760..76a0a412 100644
--- a/src/java/Keepass2AndroidPluginSDK2/gradle/wrapper/gradle-wrapper.properties
+++ b/src/java/Keepass2AndroidPluginSDK2/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Mon Dec 18 11:08:40 CET 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
diff --git a/src/keepass2android/Resources/values/strings.xml b/src/keepass2android/Resources/values/strings.xml
index 4300cd26..97491d7d 100644
--- a/src/keepass2android/Resources/values/strings.xml
+++ b/src/keepass2android/Resources/values/strings.xml
@@ -1030,4 +1030,7 @@ Initial public release
Make sure this works on your system and consider using the built-in keyboard if not.
Description provided by the plugin:
+ Fill with Keepass2Android
+ Could not associate web domain %1$s with app %2$s
+
diff --git a/src/keepass2android/keepass2android.csproj b/src/keepass2android/keepass2android.csproj
index c9228c12..bbd12639 100644
--- a/src/keepass2android/keepass2android.csproj
+++ b/src/keepass2android/keepass2android.csproj
@@ -15,10 +15,10 @@
keepass2android
OnLoad
Properties\AndroidManifest.xml
- v6.0
+ v8.0
1G
- true
+ false
9e78b013
@@ -41,10 +41,12 @@
armeabi,armeabi-v7a,x86
false
SdkOnly
- False
+ false
False
false
true
+ false
+ false
full
@@ -185,6 +187,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -1723,6 +1735,12 @@
+
+
+
+
+
+
diff --git a/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs b/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs
new file mode 100644
index 00000000..0ba9593c
--- /dev/null
+++ b/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs
@@ -0,0 +1,101 @@
+using System;
+using Android.App.Assist;
+using Android.Service.Autofill;
+using Android.Views;
+using Android.Views.Autofill;
+
+namespace keepass2android.services.AutofillBase
+{
+ ///
+ /// A stripped down version of a {@link ViewNode} that contains only autofill-relevant metadata. It
+ /// also contains a {@code mSaveType} flag that is calculated based on the {@link ViewNode}]'s
+ /// autofill hints.
+ ///
+ public class AutofillFieldMetadata
+ {
+ public SaveDataType SaveType { get; set; }
+
+ public string[] AutofillHints { get; set; }
+
+ public AutofillId AutofillId { get; }
+ public AutofillType AutofillType { get; }
+ string[] AutofillOptions { get; }
+ public bool Focused { get; }
+
+ public AutofillFieldMetadata(AssistStructure.ViewNode view)
+ {
+ AutofillId = view.AutofillId;
+ AutofillType = view.AutofillType;
+ AutofillOptions = view.GetAutofillOptions();
+ Focused = view.IsFocused;
+ //TODO port and use AutoFillHints
+ SetHints(AutofillHelper.FilterForSupportedHints(view.GetAutofillHints()));
+ }
+
+ void SetHints(string[] value)
+ {
+ AutofillHints = value;
+ UpdateSaveTypeFromHints();
+ }
+
+ ///
+ /// When the ViewNode is a list that the user needs to choose a string from (i.e. a
+ /// spinner), this is called to return the index of a specific item in the list.
+ ///
+ /// The autofill option index.
+ /// Value.
+ public int GetAutofillOptionIndex(String value)
+ {
+ for (int i = 0; i < AutofillOptions.Length; i++)
+ {
+ if (AutofillOptions[i].Equals(value))
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ void UpdateSaveTypeFromHints()
+ {
+ SaveType = 0;
+ if (AutofillHints == null)
+ {
+ return;
+ }
+ foreach (var hint in AutofillHints)
+ {
+ switch (hint)
+ {
+ case View.AutofillHintCreditCardExpirationDate:
+ case View.AutofillHintCreditCardExpirationDay:
+ case View.AutofillHintCreditCardExpirationMonth:
+ case View.AutofillHintCreditCardExpirationYear:
+ case View.AutofillHintCreditCardNumber:
+ case View.AutofillHintCreditCardSecurityCode:
+ SaveType |= SaveDataType.CreditCard;
+ break;
+ case View.AutofillHintEmailAddress:
+ SaveType |= SaveDataType.EmailAddress;
+ break;
+ case View.AutofillHintPhone:
+ case View.AutofillHintName:
+ SaveType |= SaveDataType.Generic;
+ break;
+ case View.AutofillHintPassword:
+ SaveType |= SaveDataType.Password;
+ SaveType &= ~SaveDataType.EmailAddress;
+ SaveType &= ~SaveDataType.Username;
+ break;
+ case View.AutofillHintPostalAddress:
+ case View.AutofillHintPostalCode:
+ SaveType |= SaveDataType.Address;
+ break;
+ case View.AutofillHintUsername:
+ SaveType |= SaveDataType.Username;
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs b/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs
new file mode 100644
index 00000000..96ac5b7f
--- /dev/null
+++ b/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using Android.Service.Autofill;
+using Android.Views.Autofill;
+
+namespace keepass2android.services.AutofillBase
+{
+ ///
+ /// Data structure that stores a collection of AutofillFieldMetadatas. Contains all of the
+ /// client's View hierarchy autofill-relevant metadata.
+ ///
+ public class AutofillFieldMetadataCollection
+ {
+ List AutofillIds = new List();
+ Dictionary> AutofillHintsToFieldsMap = new Dictionary>();
+ public List AllAutofillHints { get; }
+ public List FocusedAutofillHints { get; }
+ int Size = 0;
+ public SaveDataType SaveType { get; set; }
+
+ public AutofillFieldMetadataCollection()
+ {
+ SaveType = 0;
+ FocusedAutofillHints = new List();
+ AllAutofillHints = new List();
+ }
+
+ public void Add(keepass2android.services.AutofillBase.AutofillFieldMetadata autofillFieldMetadata)
+ {
+ SaveType |= autofillFieldMetadata.SaveType;
+ Size++;
+ AutofillIds.Add(autofillFieldMetadata.AutofillId);
+ var hintsList = autofillFieldMetadata.AutofillHints;
+ AllAutofillHints.AddRange(hintsList);
+ if (autofillFieldMetadata.Focused)
+ {
+ FocusedAutofillHints.AddRange(hintsList);
+ }
+ foreach (var hint in autofillFieldMetadata.AutofillHints)
+ {
+ if (!AutofillHintsToFieldsMap.ContainsKey(hint))
+ {
+ AutofillHintsToFieldsMap.Add(hint, new List());
+ }
+ AutofillHintsToFieldsMap[hint].Add(autofillFieldMetadata);
+ }
+ }
+
+ public AutofillId[] GetAutofillIds()
+ {
+ return AutofillIds.ToArray();
+ }
+
+ public List GetFieldsForHint(String hint)
+ {
+ return AutofillHintsToFieldsMap[hint];
+ }
+
+
+ }
+}
diff --git a/src/keepass2android/services/AutofillBase/AutofillHelper.cs b/src/keepass2android/services/AutofillBase/AutofillHelper.cs
new file mode 100644
index 00000000..22005b33
--- /dev/null
+++ b/src/keepass2android/services/AutofillBase/AutofillHelper.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Collections.Generic;
+using Android.Content;
+using Android.Service.Autofill;
+using Android.Util;
+using Android.Views;
+using Android.Widget;
+using FilledAutofillFieldCollection = keepass2android.services.AutofillBase.model.FilledAutofillFieldCollection;
+
+//TODO compare port
+namespace keepass2android.services.AutofillBase
+{
+ ///
+ /// This is a class containing helper methods for building Autofill Datasets and Responses.
+ ///
+ public class AutofillHelper
+ {
+ ///
+ /// Wraps autofill data in a LoginCredential Dataset object which can then be sent back to the
+ /// client View.
+ ///
+ /// The dataset.
+ /// Context.
+ /// Autofill fields.
+ /// Filled autofill field collection.
+ /// If set to true dataset auth.
+ public static Dataset NewDataset(Context context,
+ keepass2android.services.AutofillBase.AutofillFieldMetadataCollection autofillFields, FilledAutofillFieldCollection filledAutofillFieldCollection, bool datasetAuth, IAutofillIntentBuilder intentBuilder)
+ {
+ var datasetName = filledAutofillFieldCollection.DatasetName;
+ if (datasetName != null)
+ {
+ Dataset.Builder datasetBuilder;
+ if (datasetAuth)
+ {
+ datasetBuilder = new Dataset.Builder
+ (NewRemoteViews(context.PackageName, datasetName,
+ Resource.Drawable.ic_launcher));
+ IntentSender sender = intentBuilder.GetAuthIntentSenderForDataset(context, datasetName);
+ datasetBuilder.SetAuthentication(sender);
+ }
+ else
+ {
+ datasetBuilder = new Dataset.Builder
+ (NewRemoteViews(context.PackageName, datasetName,
+ Resource.Drawable.ic_launcher));
+ }
+ var setValueAtLeastOnce = filledAutofillFieldCollection.ApplyToFields(autofillFields, datasetBuilder);
+ if (setValueAtLeastOnce)
+ {
+ return datasetBuilder.Build();
+ }
+ }
+ return null;
+ }
+
+ public static RemoteViews NewRemoteViews(string packageName, string remoteViewsText,int drawableId)
+ {
+ RemoteViews presentation = new RemoteViews(packageName, Resource.Layout.autofill_service_list_item);
+ presentation.SetTextViewText(Resource.Id.text, remoteViewsText);
+ presentation.SetImageViewResource(Resource.Id.icon, drawableId);
+ return presentation;
+ }
+
+ ///
+ /// Wraps autofill data in a Response object (essentially a series of Datasets) which can then
+ /// be sent back to the client View.
+ ///
+ /// The response.
+ /// Context.
+ /// If set to true dataset auth.
+ /// Autofill fields.
+ /// Client form data map.
+ ///
+ public static FillResponse NewResponse(Context context, bool datasetAuth, keepass2android.services.AutofillBase.AutofillFieldMetadataCollection autofillFields, Dictionary clientFormDataMap, IAutofillIntentBuilder intentBuilder)
+ {
+ var responseBuilder = new FillResponse.Builder();
+ if (clientFormDataMap != null)
+ {
+ var datasetNames = clientFormDataMap.Keys;
+ foreach (var datasetName in datasetNames)
+ {
+ var filledAutofillFieldCollection = clientFormDataMap[datasetName];
+ if (filledAutofillFieldCollection != null)
+ {
+ var dataset = NewDataset(context, autofillFields, filledAutofillFieldCollection, datasetAuth, intentBuilder);
+ if (dataset != null)
+ {
+ responseBuilder.AddDataset(dataset);
+ }
+ }
+ }
+ }
+ if (autofillFields.SaveType != 0)
+ {
+ var autofillIds = autofillFields.GetAutofillIds();
+ responseBuilder.SetSaveInfo
+ (new SaveInfo.Builder(autofillFields.SaveType, autofillIds).Build());
+ return responseBuilder.Build();
+ }
+ else
+ {
+ Log.Debug(CommonUtil.Tag, "These fields are not meant to be saved by autofill.");
+ return null;
+ }
+ }
+
+ public static string[] FilterForSupportedHints(string[] hints)
+ {
+ var filteredHints = new string[hints.Length];
+ int i = 0;
+ foreach (var hint in hints)
+ {
+ if (IsValidHint(hint))
+ {
+ filteredHints[i++] = hint;
+ }
+ else
+ {
+ Log.Debug(CommonUtil.Tag, "Invalid autofill hint: " + hint);
+ }
+ }
+ var finalFilteredHints = new string[i];
+ Array.Copy(filteredHints, 0, finalFilteredHints, 0, i);
+ return finalFilteredHints;
+ }
+
+ public static bool IsValidHint(String hint)
+ {
+ switch (hint)
+ {
+ case View.AutofillHintCreditCardExpirationDate:
+ case View.AutofillHintCreditCardExpirationDay:
+ case View.AutofillHintCreditCardExpirationMonth:
+ case View.AutofillHintCreditCardExpirationYear:
+ case View.AutofillHintCreditCardNumber:
+ case View.AutofillHintCreditCardSecurityCode:
+ case View.AutofillHintEmailAddress:
+ case View.AutofillHintPhone:
+ case View.AutofillHintName:
+ case View.AutofillHintPassword:
+ case View.AutofillHintPostalAddress:
+ case View.AutofillHintPostalCode:
+ case View.AutofillHintUsername:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+}
diff --git a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs
new file mode 100644
index 00000000..9050110b
--- /dev/null
+++ b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs
@@ -0,0 +1,101 @@
+using System;
+using Android.Content;
+using Android.OS;
+using Android.Runtime;
+using Android.Service.Autofill;
+using Android.Util;
+
+namespace keepass2android.services.AutofillBase
+{
+ public interface IAutofillIntentBuilder
+ {
+ IntentSender GetAuthIntentSenderForResponse(Context context);
+ IntentSender GetAuthIntentSenderForDataset(Context context, string dataset);
+ }
+
+ public abstract class AutofillServiceBase: AutofillService, IAutofillIntentBuilder
+ {
+ public AutofillServiceBase()
+ {
+
+ }
+
+ public AutofillServiceBase(IntPtr javaReference, JniHandleOwnership transfer)
+ : base(javaReference, transfer)
+ {
+ }
+
+
+ public override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback)
+ {
+ Log.Debug(CommonUtil.Tag, "onFillRequest");
+ var structure = request.FillContexts[request.FillContexts.Count - 1].Structure;
+
+ //TODO package signature verification?
+
+ var clientState = request.ClientState;
+ Log.Debug(CommonUtil.Tag, "onFillRequest(): data=" + CommonUtil.BundleToString(clientState));
+
+
+ cancellationSignal.CancelEvent += (sender, e) => {
+ Log.Warn(CommonUtil.Tag, "Cancel autofill not implemented yet.");
+ };
+ // Parse AutoFill data in Activity
+ var parser = new StructureParser(this, structure);
+ try
+ {
+ parser.ParseForFill();
+ }
+ catch (Java.Lang.SecurityException e)
+ {
+ Log.Warn(CommonUtil.Tag, "Security exception handling request");
+ callback.OnFailure(e.Message);
+ return;
+ }
+
+ keepass2android.services.AutofillBase.AutofillFieldMetadataCollection autofillFields = parser.AutofillFields;
+ var responseBuilder = new FillResponse.Builder();
+ // Check user's settings for authenticating Responses and Datasets.
+ bool responseAuth = true;
+ var autofillIds = autofillFields.GetAutofillIds();
+ if (responseAuth && autofillIds.Length != 0)
+ {
+ // If the entire Autofill Response is authenticated, AuthActivity is used
+ // to generate Response.
+ var sender = GetAuthIntentSenderForResponse(this);
+ var presentation = keepass2android.services.AutofillBase.AutofillHelper
+ .NewRemoteViews(PackageName, GetString(Resource.String.autofill_sign_in_prompt),
+ Resource.Drawable.ic_launcher);
+ responseBuilder
+ .SetAuthentication(autofillIds, sender, presentation);
+ callback.OnSuccess(responseBuilder.Build());
+ }
+ else
+ {
+ var datasetAuth = true;
+ var response = keepass2android.services.AutofillBase.AutofillHelper.NewResponse(this, datasetAuth, autofillFields, null, this);
+ callback.OnSuccess(response);
+ }
+ }
+
+ public override void OnSaveRequest(SaveRequest request, SaveCallback callback)
+ {
+ //TODO implement
+ callback.OnFailure("not implemented");
+ }
+
+
+ public override void OnConnected()
+ {
+ Log.Debug(CommonUtil.Tag, "onConnected");
+ }
+
+ public override void OnDisconnected()
+ {
+ Log.Debug(CommonUtil.Tag, "onDisconnected");
+ }
+
+ public abstract IntentSender GetAuthIntentSenderForResponse(Context context);
+ public abstract IntentSender GetAuthIntentSenderForDataset(Context context, string dataset);
+ }
+}
diff --git a/src/keepass2android/services/AutofillBase/CommonUtil.cs b/src/keepass2android/services/AutofillBase/CommonUtil.cs
new file mode 100644
index 00000000..c6305eea
--- /dev/null
+++ b/src/keepass2android/services/AutofillBase/CommonUtil.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Text;
+using Android.OS;
+using Java.Util;
+
+namespace keepass2android.services.AutofillBase
+{
+ public class CommonUtil
+ {
+ public const string Tag = "Kp2aAutofill";
+ public const bool Debug = true;
+ public const string EXTRA_DATASET_NAME = "dataset_name";
+ public const string EXTRA_FOR_RESPONSE = "for_response";
+
+ static void BundleToString(StringBuilder builder, Bundle data)
+ {
+ var keySet = data.KeySet();
+ builder.Append("[Bundle with ").Append(keySet.Count).Append(" keys:");
+ foreach (var key in keySet)
+ {
+ builder.Append(' ').Append(key).Append('=');
+ Object value = data.Get(key);
+ if (value is Bundle)
+ {
+ BundleToString(builder, (Bundle)value);
+ }
+ else
+ {
+ builder.Append((value is Object[])
+ ? Arrays.ToString((bool[])value) : value);
+ }
+ }
+ builder.Append(']');
+ }
+
+ public static string BundleToString(Bundle data)
+ {
+ if (data == null)
+ {
+ return "N/A";
+ }
+ StringBuilder builder = new StringBuilder();
+ BundleToString(builder, data);
+ return builder.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/keepass2android/services/AutofillBase/Kp2aDigitalAssetLinksDataSource.cs b/src/keepass2android/services/AutofillBase/Kp2aDigitalAssetLinksDataSource.cs
new file mode 100644
index 00000000..511edada
--- /dev/null
+++ b/src/keepass2android/services/AutofillBase/Kp2aDigitalAssetLinksDataSource.cs
@@ -0,0 +1,30 @@
+using Android.Content;
+
+namespace keepass2android.services.AutofillBase
+{
+
+ internal class Kp2aDigitalAssetLinksDataSource
+ {
+ private static Kp2aDigitalAssetLinksDataSource instance;
+
+ private Kp2aDigitalAssetLinksDataSource() { }
+
+ public static Kp2aDigitalAssetLinksDataSource Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ instance = new Kp2aDigitalAssetLinksDataSource();
+ }
+ return instance;
+ }
+ }
+
+ public bool IsValid(Context context, string webDomain, string packageName)
+ {
+ //TODO implement
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/keepass2android/services/AutofillBase/StructureParser.cs b/src/keepass2android/services/AutofillBase/StructureParser.cs
new file mode 100644
index 00000000..90444137
--- /dev/null
+++ b/src/keepass2android/services/AutofillBase/StructureParser.cs
@@ -0,0 +1,115 @@
+using System;
+using Android.App.Assist;
+using Android.Content;
+using Android.Util;
+using FilledAutofillFieldCollection = keepass2android.services.AutofillBase.model.FilledAutofillFieldCollection;
+
+namespace keepass2android.services.AutofillBase
+{
+ ///
+ /// Parser for an AssistStructure object. This is invoked when the Autofill Service receives an
+ /// AssistStructure from the client Activity, representing its View hierarchy. In this sample, it
+ /// parses the hierarchy and collects autofill metadata from {@link ViewNode}s along the way.
+ ///
+ public sealed class StructureParser
+ {
+ public Context mContext { get; }
+ public keepass2android.services.AutofillBase.AutofillFieldMetadataCollection AutofillFields { get; set; }
+ AssistStructure Structure;
+ public FilledAutofillFieldCollection ClientFormData { get; set; }
+
+ public StructureParser(Context context, AssistStructure structure)
+ {
+ mContext = context;
+ Structure = structure;
+ AutofillFields = new keepass2android.services.AutofillBase.AutofillFieldMetadataCollection();
+ }
+
+ public void ParseForFill()
+ {
+ Parse(true);
+ }
+
+ public void ParseForSave()
+ {
+ Parse(false);
+ }
+
+ ///
+ /// Traverse AssistStructure and add ViewNode metadata to a flat list.
+ ///
+ /// The parse.
+ /// If set to true for fill.
+ void Parse(bool forFill)
+ {
+ Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, "Parsing structure for " + Structure.ActivityComponent);
+ var nodes = Structure.WindowNodeCount;
+ ClientFormData = new FilledAutofillFieldCollection();
+ String webDomain = null;
+ for (int i = 0; i < nodes; i++)
+ {
+ var node = Structure.GetWindowNodeAt(i);
+ var view = node.RootViewNode;
+ ParseLocked(forFill, view, ref webDomain);
+ }
+ if (!string.IsNullOrEmpty(webDomain))
+ {
+ String packageName = Structure.ActivityComponent.PackageName;
+ bool valid = Kp2aDigitalAssetLinksDataSource.Instance.IsValid(mContext, webDomain, packageName);
+ if (!valid)
+ {
+ throw new Java.Lang.SecurityException(mContext.GetString(
+ Resource.String.invalid_link_association, webDomain, packageName));
+ }
+ Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, $"Domain {webDomain} is valid for {packageName}");
+ }
+ else
+ {
+ Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, "no web domain");
+ }
+ }
+
+ void ParseLocked(bool forFill, AssistStructure.ViewNode viewNode, ref string validWebdomain)
+ {
+ String webDomain = viewNode.WebDomain;
+ if (webDomain != null)
+ {
+ Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, $"child web domain: {webDomain}");
+ if (!string.IsNullOrEmpty(validWebdomain))
+ {
+ if (webDomain == validWebdomain)
+ {
+ throw new Java.Lang.SecurityException($"Found multiple web domains: valid= {validWebdomain}, child={webDomain}");
+ }
+ }
+ else
+ {
+ validWebdomain = webDomain;
+ }
+ }
+
+ if (viewNode.GetAutofillHints() != null && viewNode.GetAutofillHints().Length > 0)
+ {
+ if (forFill)
+ {
+ AutofillFields.Add(new keepass2android.services.AutofillBase.AutofillFieldMetadata(viewNode));
+ }
+ else
+ {
+ //TODO implement
+ throw new NotImplementedException("TODO: Port and use AutoFill hints");
+ //ClientFormData.Add(new FilledAutofillField(viewNode));
+ }
+ }
+ var childrenSize = viewNode.ChildCount;
+ if (childrenSize > 0)
+ {
+ for (int i = 0; i < childrenSize; i++)
+ {
+ ParseLocked(forFill, viewNode.GetChildAt(i), ref validWebdomain);
+ }
+ }
+ }
+
+ }
+}
diff --git a/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs b/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs
new file mode 100644
index 00000000..f135a03e
--- /dev/null
+++ b/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs
@@ -0,0 +1,84 @@
+using Android.App.Assist;
+using Android.Views.Autofill;
+
+namespace keepass2android.services.AutofillBase.model
+{
+ ///
+ /// JSON serializable data class containing the same data as an {@link AutofillValue}.
+ ///
+ public class FilledAutofillField
+ {
+ public string TextValue { get; set; }
+ public long? DateValue { get; set; }
+ public bool? ToggleValue { get; set; }
+
+ ///
+ /// Does not need to be serialized into persistent storage, so it's not exposed.
+ ///
+ /// The autofill hints.
+ public string[] AutofillHints { get; set; }
+
+ public FilledAutofillField()
+ {}
+
+ public FilledAutofillField(AssistStructure.ViewNode viewNode)
+ {
+ AutofillHints = AutofillHelper.FilterForSupportedHints(viewNode.GetAutofillHints());
+
+ //TODO port updated FilledAutofillField?
+ AutofillValue autofillValue = viewNode.AutofillValue;
+ if (autofillValue != null)
+ {
+ if (autofillValue.IsList)
+ {
+ string[] autofillOptions = viewNode.GetAutofillOptions();
+ int index = autofillValue.ListValue;
+ if (autofillOptions != null && autofillOptions.Length > 0)
+ {
+ TextValue = autofillOptions[index];
+ }
+ }
+ else if (autofillValue.IsDate)
+ {
+ DateValue = autofillValue.DateValue;
+ }
+ else if (autofillValue.IsText)
+ {
+ // Using toString of AutofillValue.getTextValue in order to save it to
+ // SharedPreferences.
+ TextValue = autofillValue.TextValue;
+ }
+ }
+ }
+
+ public bool IsNull()
+ {
+ return TextValue == null && DateValue == null && ToggleValue == null;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (this == obj) return true;
+ if (obj == null || GetType() != obj.GetType()) return false;
+
+ FilledAutofillField that = (FilledAutofillField)obj;
+
+ if (TextValue != null ? !TextValue.Equals(that.TextValue) : that.TextValue != null)
+ return false;
+ if (DateValue != null ? !DateValue.Equals(that.DateValue) : that.DateValue != null)
+ return false;
+ return ToggleValue != null ? ToggleValue.Equals(that.ToggleValue) : that.ToggleValue == null;
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ var result = TextValue != null ? TextValue.GetHashCode() : 0;
+ result = 31 * result + (DateValue != null ? DateValue.GetHashCode() : 0);
+ result = 31 * result + (ToggleValue != null ? ToggleValue.GetHashCode() : 0);
+ return result;
+ }
+ }
+ }
+}
diff --git a/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs b/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs
new file mode 100644
index 00000000..320ce1ce
--- /dev/null
+++ b/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs
@@ -0,0 +1,137 @@
+using System.Collections.Generic;
+using Android.Service.Autofill;
+using Android.Util;
+using Android.Views;
+using Android.Views.Autofill;
+
+namespace keepass2android.services.AutofillBase.model
+{
+ ///
+ /// FilledAutofillFieldCollection is the model that holds all of the data on a client app's page,
+ /// plus the dataset name associated with it.
+ ///
+ public class FilledAutofillFieldCollection
+ {
+ public Dictionary HintMap { get; set; }
+ public string DatasetName { get; set; }
+
+ public FilledAutofillFieldCollection(Dictionary hintMap, string datasetName = "")
+ {
+ HintMap = hintMap;
+ DatasetName = datasetName;
+ }
+
+ public FilledAutofillFieldCollection() : this(new Dictionary())
+ {}
+
+ ///
+ /// Adds a filledAutofillField to the collection, indexed by all of its hints.
+ ///
+ /// The add.
+ /// Filled autofill field.
+ public void Add(keepass2android.services.AutofillBase.model.FilledAutofillField filledAutofillField)
+ {
+ string[] autofillHints = filledAutofillField.AutofillHints;
+ //TODO apply W3C transformation
+ foreach (string hint in autofillHints)
+ {
+ HintMap.Add(hint, filledAutofillField);
+ }
+ }
+
+ ///
+ /// Populates a Dataset.Builder with appropriate values for each AutofillId
+ /// in a AutofillFieldMetadataCollection.
+ ///
+ /// In other words, it constructs an autofill Dataset.Builder
+ /// by applying saved values (from this FilledAutofillFieldCollection)
+ /// to Views specified in a AutofillFieldMetadataCollection, which represents the current
+ /// page the user is on.
+ ///
+ /// true, if to fields was applyed, false otherwise.
+ /// Autofill field metadata collection.
+ /// Dataset builder.
+ public bool ApplyToFields(AutofillFieldMetadataCollection autofillFieldMetadataCollection,
+ Dataset.Builder datasetBuilder)
+ {
+ bool setValueAtLeastOnce = false;
+ List allHints = autofillFieldMetadataCollection.AllAutofillHints;
+ for (int hintIndex = 0; hintIndex < allHints.Count; hintIndex++)
+ {
+ string hint = allHints[hintIndex];
+ List fillableAutofillFields = autofillFieldMetadataCollection.GetFieldsForHint(hint);
+ if (fillableAutofillFields == null)
+ {
+ continue;
+ }
+ for (int autofillFieldIndex = 0; autofillFieldIndex < fillableAutofillFields.Count; autofillFieldIndex++)
+ {
+ keepass2android.services.AutofillBase.model.FilledAutofillField filledAutofillField = HintMap[hint];
+ if (filledAutofillField == null)
+ {
+ continue;
+ }
+ AutofillFieldMetadata autofillFieldMetadata = fillableAutofillFields[autofillFieldIndex];
+ var autofillId = autofillFieldMetadata.AutofillId;
+ var autofillType = autofillFieldMetadata.AutofillType;
+ switch (autofillType)
+ {
+ case AutofillType.List:
+ var listValue = autofillFieldMetadata.GetAutofillOptionIndex(filledAutofillField.TextValue);
+ if (listValue != -1)
+ {
+ datasetBuilder.SetValue(autofillId, AutofillValue.ForList(listValue));
+ setValueAtLeastOnce = true;
+ }
+ break;
+ case AutofillType.Date:
+ var dateValue = filledAutofillField.DateValue;
+ datasetBuilder.SetValue(autofillId, AutofillValue.ForDate((long)dateValue));
+ setValueAtLeastOnce = true;
+ break;
+ case AutofillType.Text:
+ var textValue = filledAutofillField.TextValue;
+ if (textValue != null)
+ {
+ datasetBuilder.SetValue(autofillId, AutofillValue.ForText(textValue));
+ setValueAtLeastOnce = true;
+ }
+ break;
+ case AutofillType.Toggle:
+ var toggleValue = filledAutofillField.ToggleValue;
+ if (toggleValue != null)
+ {
+ datasetBuilder.SetValue(autofillId, AutofillValue.ForToggle(toggleValue.Value));
+ setValueAtLeastOnce = true;
+ }
+ break;
+ default:
+ Log.Warn(CommonUtil.Tag, "Invalid autofill type - " + autofillType);
+ break;
+ }
+ }
+ }
+ return setValueAtLeastOnce;
+ }
+
+ ///
+ /// Takes in a list of autofill hints (`autofillHints`), usually associated with a View or set of
+ /// Views. Returns whether any of the filled fields on the page have at least 1 of these
+ /// `autofillHint`s.
+ ///
+ /// true, if with hints was helpsed, false otherwise.
+ /// Autofill hints.
+ public bool HelpsWithHints(List autofillHints)
+ {
+ for (int i = 0; i < autofillHints.Count; i++)
+ {
+ var autofillHint = autofillHints[i];
+ if (HintMap.ContainsKey(autofillHint) && !HintMap[autofillHint].IsNull())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/src/keepass2android/services/Kp2aAutofillService.cs b/src/keepass2android/services/Kp2aAutofillService.cs
new file mode 100644
index 00000000..7cb1cfbf
--- /dev/null
+++ b/src/keepass2android/services/Kp2aAutofillService.cs
@@ -0,0 +1,38 @@
+using System;
+using Android;
+using Android.App;
+using Android.Content;
+using Android.Runtime;
+using AutofillServiceBase = keepass2android.services.AutofillBase.AutofillServiceBase;
+
+namespace keepass2android.services
+{
+ [Service(Label = "Keepass2Android Autofill Service", Permission=Manifest.Permission.BindAutofillService)]
+ [IntentFilter(new [] {"android.service.autofill.AutofillService"})]
+ [MetaData("android.autofill", Resource = "@xml/autofillservice")]
+ [Register("keepass2android.services.Kp2aAutofillService")]
+ public class Kp2aAutofillService: AutofillServiceBase
+ {
+ public Kp2aAutofillService()
+ {
+
+ }
+
+ public Kp2aAutofillService(IntPtr javaReference, JniHandleOwnership transfer)
+ : base(javaReference, transfer)
+ {
+ }
+
+ public override IntentSender GetAuthIntentSenderForResponse(Context context)
+ {
+ Intent intent = new Intent(context, typeof(KeePass));
+ return PendingIntent.GetActivity(context, 0, intent, PendingIntentFlags.CancelCurrent).IntentSender;
+ }
+
+ public override IntentSender GetAuthIntentSenderForDataset(Context context, string dataset)
+ {
+ //TODO implement
+ return GetAuthIntentSenderForResponse(context);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/netftpandroid b/src/netftpandroid
index 62733d71..040e8bbe 160000
--- a/src/netftpandroid
+++ b/src/netftpandroid
@@ -1 +1 @@
-Subproject commit 62733d714fec6a2806daced432230998eabca29f
+Subproject commit 040e8bbe564bd140203255e11c86c01c2f7c1892