From db74e573d13a69601219f9e5b8b752bebc4669f6 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Sat, 2 Dec 2017 16:41:57 +0100 Subject: [PATCH 01/50] catch exception when decrypting key fails, fixes #50 --- src/keepass2android/PasswordActivity.cs | 33 ++++++++++++++++++------- src/keepass2android/QuickUnlock.cs | 15 +++++++---- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/keepass2android/PasswordActivity.cs b/src/keepass2android/PasswordActivity.cs index b48a03d2..04a38b97 100644 --- a/src/keepass2android/PasswordActivity.cs +++ b/src/keepass2android/PasswordActivity.cs @@ -1054,10 +1054,19 @@ namespace keepass2android var btn = FindViewById(Resource.Id.fingerprintbtn); btn.SetImageResource(Resource.Drawable.ic_fingerprint_success); - - var masterPassword = _fingerprintDec.DecryptStored(Database.GetFingerprintPrefKey(_ioConnection)); - _password = FindViewById(Resource.Id.password_edit).Text = masterPassword; + try + { + var masterPassword = _fingerprintDec.DecryptStored(Database.GetFingerprintPrefKey(_ioConnection)); + _password = FindViewById(Resource.Id.password_edit).Text = masterPassword; + + } + catch (Java.Security.GeneralSecurityException ex) + { + HandleFingerprintKeyInvalidated(); + return; + } + btn.PostDelayed(() => { //re-init fingerprint unlock in case something goes wrong with opening the database @@ -1961,12 +1970,7 @@ namespace keepass2android } else { - //key invalidated permanently - btn.SetImageResource(Resource.Drawable.ic_fingerprint_error); - btn.Tag = GetString(Resource.String.fingerprint_unlock_failed); - _fingerprintDec = null; - - ClearFingerprintUnlockData(); + HandleFingerprintKeyInvalidated(); return false; } } @@ -1982,6 +1986,17 @@ namespace keepass2android } + private void HandleFingerprintKeyInvalidated() + { + var btn = FindViewById(Resource.Id.fingerprintbtn); +//key invalidated permanently + btn.SetImageResource(Resource.Drawable.ic_fingerprint_error); + btn.Tag = GetString(Resource.String.fingerprint_unlock_failed); + _fingerprintDec = null; + + ClearFingerprintUnlockData(); + } + private void InitializeOptionCheckboxes() { CheckBox cbQuickUnlock = (CheckBox)FindViewById(Resource.Id.enable_quickunlock); cbQuickUnlock.Checked = _prefs.GetBoolean(GetString(Resource.String.QuickUnlockDefaultEnabled_key), true); diff --git a/src/keepass2android/QuickUnlock.cs b/src/keepass2android/QuickUnlock.cs index 8ba3d556..b2ce30ea 100644 --- a/src/keepass2android/QuickUnlock.cs +++ b/src/keepass2android/QuickUnlock.cs @@ -285,11 +285,7 @@ namespace keepass2android else { Kp2aLog.Log("failed to initialize fingerprint."); - //key invalidated permanently - btn.SetImageResource(Resource.Drawable.ic_fingerprint_error); - btn.Tag = GetString(Resource.String.fingerprint_unlock_failed); - _fingerprintIdentifier = null; - + HandleFingerprintKeyInvalidated(); } } catch (Exception e) @@ -304,6 +300,15 @@ namespace keepass2android } + private void HandleFingerprintKeyInvalidated() + { + var btn = FindViewById(Resource.Id.fingerprintbtn); +//key invalidated permanently + btn.SetImageResource(Resource.Drawable.ic_fingerprint_error); + btn.Tag = GetString(Resource.String.fingerprint_unlock_failed); + _fingerprintIdentifier = null; + } + private void ClearFingerprintUnlockData() { ISharedPreferencesEditor edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit(); From d621ea15a3d70885266309ca6462732ef3039bc4 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Sun, 3 Dec 2017 07:42:38 +0100 Subject: [PATCH 02/50] adjust targetFrameworkVersion of .csproj files to that of the manifest file --- .../AndroidFileChooserBinding.csproj | 2 +- src/JavaFileStorageBindings/JavaFileStorageBindings.csproj | 2 +- src/KP2AKdbLibraryBinding/KP2AKdbLibraryBinding.csproj | 2 +- src/KeePassLib2Android/KeePassLib2Android.csproj | 2 +- src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj | 2 +- src/Kp2aKeyboardBinding/Kp2aKeyboardBinding.csproj | 2 +- src/PluginSdkBinding/PluginSdkBinding.csproj | 2 +- src/SamsungPass | 2 +- src/TwofishCipher/TwofishCipher.csproj | 2 +- src/ZlibAndroid/ZlibAndroid.csproj | 2 +- src/keepass2android/keepass2android.csproj | 4 ++-- src/netftpandroid | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/AndroidFileChooserBinding/AndroidFileChooserBinding.csproj b/src/AndroidFileChooserBinding/AndroidFileChooserBinding.csproj index a841a519..3fe01d37 100644 --- a/src/AndroidFileChooserBinding/AndroidFileChooserBinding.csproj +++ b/src/AndroidFileChooserBinding/AndroidFileChooserBinding.csproj @@ -10,7 +10,7 @@ AndroidFileChooserBinding AndroidFileChooserBinding 512 - v7.1 + v6.0 True diff --git a/src/JavaFileStorageBindings/JavaFileStorageBindings.csproj b/src/JavaFileStorageBindings/JavaFileStorageBindings.csproj index 9123ffe9..65d58114 100644 --- a/src/JavaFileStorageBindings/JavaFileStorageBindings.csproj +++ b/src/JavaFileStorageBindings/JavaFileStorageBindings.csproj @@ -11,7 +11,7 @@ JavaFileStorageBindings 512 True - v7.1 + v6.0 true diff --git a/src/KP2AKdbLibraryBinding/KP2AKdbLibraryBinding.csproj b/src/KP2AKdbLibraryBinding/KP2AKdbLibraryBinding.csproj index 77429eaa..a0ed90dd 100644 --- a/src/KP2AKdbLibraryBinding/KP2AKdbLibraryBinding.csproj +++ b/src/KP2AKdbLibraryBinding/KP2AKdbLibraryBinding.csproj @@ -10,7 +10,7 @@ KP2AKdbLibraryBinding KP2AKdbLibraryBinding 512 - v7.1 + v6.0 True diff --git a/src/KeePassLib2Android/KeePassLib2Android.csproj b/src/KeePassLib2Android/KeePassLib2Android.csproj index c10e7427..abda7528 100644 --- a/src/KeePassLib2Android/KeePassLib2Android.csproj +++ b/src/KeePassLib2Android/KeePassLib2Android.csproj @@ -12,7 +12,7 @@ Resources\Resource.designer.cs Resource KeePassLib2Android - v7.1 + v6.0 True 8482b288 diff --git a/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj b/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj index b51f04ce..50b23c77 100644 --- a/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj +++ b/src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj @@ -12,7 +12,7 @@ 512 Resources\Resource.Designer.cs Off - v7.1 + v6.0 true 06ffb71c diff --git a/src/Kp2aKeyboardBinding/Kp2aKeyboardBinding.csproj b/src/Kp2aKeyboardBinding/Kp2aKeyboardBinding.csproj index e21cd150..8d8c5fae 100644 --- a/src/Kp2aKeyboardBinding/Kp2aKeyboardBinding.csproj +++ b/src/Kp2aKeyboardBinding/Kp2aKeyboardBinding.csproj @@ -11,7 +11,7 @@ Resources Kp2aKeyboardBinding True - v7.1 + v6.0 True diff --git a/src/PluginSdkBinding/PluginSdkBinding.csproj b/src/PluginSdkBinding/PluginSdkBinding.csproj index d32a1b32..1dc53f91 100644 --- a/src/PluginSdkBinding/PluginSdkBinding.csproj +++ b/src/PluginSdkBinding/PluginSdkBinding.csproj @@ -11,7 +11,7 @@ PluginSdkBinding 512 True - v7.1 + v6.0 true diff --git a/src/SamsungPass b/src/SamsungPass index 79184ec7..95bc88e1 160000 --- a/src/SamsungPass +++ b/src/SamsungPass @@ -1 +1 @@ -Subproject commit 79184ec7cd77d89602e8ea4d5f38f7e2631aceb1 +Subproject commit 95bc88e1826f64ccb9fb7e68d7f77edb12ac73ef diff --git a/src/TwofishCipher/TwofishCipher.csproj b/src/TwofishCipher/TwofishCipher.csproj index 35a72b44..48337a23 100644 --- a/src/TwofishCipher/TwofishCipher.csproj +++ b/src/TwofishCipher/TwofishCipher.csproj @@ -13,7 +13,7 @@ Resources\Resource.Designer.cs Off True - v7.1 + v6.0 true diff --git a/src/ZlibAndroid/ZlibAndroid.csproj b/src/ZlibAndroid/ZlibAndroid.csproj index 295d16ac..d2238c67 100644 --- a/src/ZlibAndroid/ZlibAndroid.csproj +++ b/src/ZlibAndroid/ZlibAndroid.csproj @@ -13,7 +13,7 @@ Assets True ZlibAndroid - v7.1 + v6.0 true diff --git a/src/keepass2android/keepass2android.csproj b/src/keepass2android/keepass2android.csproj index 11e2b9ee..c9228c12 100644 --- a/src/keepass2android/keepass2android.csproj +++ b/src/keepass2android/keepass2android.csproj @@ -15,7 +15,7 @@ keepass2android OnLoad Properties\AndroidManifest.xml - v7.1 + v6.0 1G true @@ -43,7 +43,7 @@ SdkOnly False False - False + false true diff --git a/src/netftpandroid b/src/netftpandroid index 561a509f..62733d71 160000 --- a/src/netftpandroid +++ b/src/netftpandroid @@ -1 +1 @@ -Subproject commit 561a509f390aef519cefd0c964af4f089ead211a +Subproject commit 62733d714fec6a2806daced432230998eabca29f From da828523f3d2f4bcf3727d81d2788cc1a24f53cf Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Sun, 3 Dec 2017 07:44:01 +0100 Subject: [PATCH 03/50] Improve error messages when initializing fingerprint unlock fails. This is especially important for users who have migrated to a new device and previously received a very technical error message, but they simply need to reenable fingerprint on the new device. Fixes #37. --- src/keepass2android/PasswordActivity.cs | 13 ++++++++++--- src/keepass2android/QuickUnlock.cs | 4 ++-- src/keepass2android/Resources/values/strings.xml | 7 +++++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/keepass2android/PasswordActivity.cs b/src/keepass2android/PasswordActivity.cs index 04a38b97..84c44f6a 100644 --- a/src/keepass2android/PasswordActivity.cs +++ b/src/keepass2android/PasswordActivity.cs @@ -1976,8 +1976,15 @@ namespace keepass2android } catch (Exception e) { + //exception can happen here if the app was restored from Google Backup (including preferences) but no fingerprint data is there. btn.SetImageResource(Resource.Drawable.ic_fingerprint_error); - btn.Tag = "Error initializing Fingerprint Unlock: " + e; + Kp2aLog.Log("failed to init fingerprint unlock:" + e.ToString()); + string error = GetString(Resource.String.FingerprintInitFailed) + " " + + GetString(Resource.String.fingerprint_reenable2); + + btn.Tag = error; + + Toast.MakeText(this, Resource.String.fingerprint_reenable2, ToastLength.Long).Show(); _fingerprintDec = null; return false; @@ -1991,7 +1998,7 @@ namespace keepass2android var btn = FindViewById(Resource.Id.fingerprintbtn); //key invalidated permanently btn.SetImageResource(Resource.Drawable.ic_fingerprint_error); - btn.Tag = GetString(Resource.String.fingerprint_unlock_failed); + btn.Tag = GetString(Resource.String.fingerprint_unlock_failed) + " " + GetString(Resource.String.fingerprint_reenable2); _fingerprintDec = null; ClearFingerprintUnlockData(); @@ -2122,7 +2129,7 @@ namespace keepass2android _act.ClearFingerprintUnlockData(); _act.InitFingerprintUnlock(); - Message = _act.GetString(Resource.String.fingerprint_disabled_wrong_masterkey); + Message = _act.GetString(Resource.String.fingerprint_disabled_wrong_masterkey) + " " + _act.GetString(Resource.String.fingerprint_reenable2); } else { diff --git a/src/keepass2android/QuickUnlock.cs b/src/keepass2android/QuickUnlock.cs index b2ce30ea..45000438 100644 --- a/src/keepass2android/QuickUnlock.cs +++ b/src/keepass2android/QuickUnlock.cs @@ -305,8 +305,8 @@ namespace keepass2android var btn = FindViewById(Resource.Id.fingerprintbtn); //key invalidated permanently btn.SetImageResource(Resource.Drawable.ic_fingerprint_error); - btn.Tag = GetString(Resource.String.fingerprint_unlock_failed); - _fingerprintIdentifier = null; + btn.Tag = GetString(Resource.String.fingerprint_unlock_failed) + " " + GetString(Resource.String.fingerprint_reenable2); + _fingerprintIdentifier = null; } private void ClearFingerprintUnlockData() diff --git a/src/keepass2android/Resources/values/strings.xml b/src/keepass2android/Resources/values/strings.xml index e0b6fb9b..4300cd26 100644 --- a/src/keepass2android/Resources/values/strings.xml +++ b/src/keepass2android/Resources/values/strings.xml @@ -95,9 +95,12 @@ Enable full Fingerprint Unlock Enable Fingerprint Unlock for QuickUnlock Touch sensor to unlock database - Fingerprint Unlock failed. Decryption key was invalidated by Android OS. This usually happens if a new fingerprint was enrolled or security settings were changed. Please unlock with your password and then re-enabled Fingerprint Unlock in the database settings. - Unlocking the database failed: Invalid composite key. Fingerprint Unlock was disabled because apparently the stored master password is no longer valid. Please unlock with your password and then re-enabled Fingerprint Unlock in the database settings. + Fingerprint Unlock failed. Decryption key was invalidated by Android OS. This usually happens if a new fingerprint was enrolled or security settings were changed. + Unlocking the database failed: Invalid composite key. Fingerprint Unlock was disabled because apparently the stored master password is no longer valid. Please re-enable Fingerprint Unlock for the new master password. + Please unlock with your password and then re-enable Fingerprint Unlock in the database settings. + Failed to initialize fingerprint sensor. + This will store your master password on this device, encrypted with the Android Keystore and protected using fingerprint authentication. Allows to unlock your database only with your fingerprint. Allows to use fingerprint authentication instead of the QuickUnlock code. Does not store any information related to your master password. From f7e8f25b703bde73a4ec8c835a116c6c302b0b6a Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Wed, 6 Dec 2017 05:33:53 +0100 Subject: [PATCH 04/50] update build tools for keyboard --- src/java/KP2ASoftkeyboard_AS/.idea/gradle.xml | 2 +- src/java/KP2ASoftkeyboard_AS/.idea/misc.xml | 15 +-------------- src/java/KP2ASoftkeyboard_AS/app/build.gradle | 2 +- src/java/KP2ASoftkeyboard_AS/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 4 ++-- 5 files changed, 6 insertions(+), 19 deletions(-) diff --git a/src/java/KP2ASoftkeyboard_AS/.idea/gradle.xml b/src/java/KP2ASoftkeyboard_AS/.idea/gradle.xml index 9ab4ad8a..3efaea6d 100644 --- a/src/java/KP2ASoftkeyboard_AS/.idea/gradle.xml +++ b/src/java/KP2ASoftkeyboard_AS/.idea/gradle.xml @@ -3,7 +3,7 @@ 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 96a442e5b8e9394ccf50bab9988cb2316026245d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9397 zcmV;mBud+fP)L`9r|n3#ts(U@pVoQ)(ZPc(6i z8k}N`MvWQ78F(rhG(?6FnFXYo>28{yZ}%O}TvdDT_5P?j=iW=V`8=UNc_}`JbG!ST zs@lK(TWkH+P**sB$A`cEY%Y53cQ}1&6`x-M$Cz&{o9bLU^M-%^mY?+vedlvt$RT-^ zu|w7}IaWaljBq#|I%Mpo!Wc2bbZF3KF9|D%wZe{YFM=hJAv$>j>nhx`=Wis#KG!cJA5x!4)f) zezMz1?Vn$GnZNjbFXH(pK83nn!^3=+^*kTTs5rV9Dq^XS(IKO!mKt5!dSmb3IVCxZ z8TTk5IE)F1V29$G7v#j9d-hy&_pdg8?kT4)zqr>?`}I%W>(?GO%*C&}?Fp|bI*~2&KZ$%^B6R&1~2kA{`CWy+>F-x=z-f{_&vyu_3yp{jtw(*syi% zu3t2|4{c~LJXRt2m>rMg2V_kLltCZ<`m>qcI?BPP?6hf``|e!rZEFszeYQ3f-*nAS zZ+h1$mFwy+7156lkB(k6)!1fUbJCxgIBK38$jj5cC$r&YXN)nr#PY=tJaLc?C_o?j+8H3Q>891JJ9&$l-r+-SG#q)*;r52% z@nlKflb65o%s*Jt)!pw1k{vIoQIvoJ0Y&Msiw0X!qJ)_47G*?aJ6bJFLh_4b$5&1k5wN>du*>6#i7R9T8; z7>EHOV=ue7mo77SJPwER4(A+s?n0JjYK)b}Om6n>ke?0JR=jTI+RFBg_iwb7k%n*2 zR_M0DJ9x+0zxba4(B1y^JQ_Nj6dlP5PGXvSq8fF#mxrFYj3d9(V#jJwt+IqU9+8+D z6C6Us1OI$d8OF!3+Hm1 zW5in zXV^%U35HooOpSmeqlG6e0kUMYNonKp1vr|My9}4-WO+uOxe_c-o&}%voNYHkqtle% z5yQ_^oozSUUNu30EQSAl!Q%(%3G1NXENSMjCL*Vx-Td2~rk(}d z8pT!HZe>1r5EGuz`pgsg@^yQEi=BIa#meLq0!?{TZ}q#}=7UC9_l=w|wv+pP!g4#! zRys6EN$Jv}#U47$k&)pDzvks}LGfPku6P9p!56Py)~1)W(11n7n}`Wx!=;_JTiu#d zpCqx=hEk@t4sp?!j{W}wP@V-=Pd=T^>6IKBy;#mLA7hCe{V7B3@I7Ipa}L`MbF|YQ z)$BNWsiEnoNHrtJli|n8cOnn4NyF=8MbVxgof0>Uv%wM_j94a;8(LMjlL~E(99gJ*2%JtNtAkD@j;^ za~Y~&j6uY{=Rv5S4joH*RW_m9N{ZSN0HhAwFyJNok zS9kx$>wMf%tUi&Eb`6u0lWJ|k?A-42(lp2UmS(PrAc(24wexRiHUieMwf$o%m6$xs zp#-SdBUu2D5`v;(9-sm&kN2M74c&AvKe_v@tQ|dzJ2qSgQHpnUP(iQ?J%Il;Jdyp# z7}cpq6Kdm+FS~zS4Eo;fuO=DFP*UlpO|_CNt5&NUqBvQWxmg7#ARvMf=%#H@p%RZ` zjK$hMbNb+vVP3UlkfIt&ptJ<00Ic{Ka+lF+&w;OEs1O2#V8~O|R*Gq9TIgM&UqM&bZOXBwnbC? zDr))NR&g>lwVgcmnx`K1$)PTTw3m}-T11^ZkY{}jQ@lGD$XzJIcVFkYBBW=o_}TUU zt@yd{Jz;@~72x#!RG(#ira6}v-*J#<{@@^OI-Q2T^}=IKLubsa&V-%WwlF1s7fz~u zMdQTV7SnRet#^`VO0V7H(?59X{uy+S`(sorO@2-+qioUdo9+6r4#|jb=?t50oh42R z{}I>Krut|YKkOc|O|M>y#(3YA;I(i+MiHSfwbJA$jIUr$Y2i|u)*>@2eUYk`j4C5r z>61dKu!AqM_E7#DoDzbd-bfT%AYXUUB{SS|{b{`5^?wz1{PVQgTlvyqOX8(#GTz(U zNPhnj>$lC`xaD56`TjW&uW8p~qikP*F8kHFM0frzdk%UNGjb1O$%uLK`0-)2UsZ3L z#+j+CI_8k4VslL%$aVR@joX>M-@odbX!os$xY$HDIOCokY?{Q0v2kQErf|ZlN>D9w zC+2}E&?rDdi#%))$p%P4C_xGXu=@U~_<|V4L|{>TP$XBp$5pCPXLzK3!;gP>7=QNi zkNOur`>xY=@VSpB#LsN9JKpOz({ANcdv>?K+D_*_HZ<;9>kplj^Ph5!e&&a#?(3vK z_Q@}D_M5kGcx^AuaI~qKYUnb1Mj-n;MURXa)+x7~e2gbMW|gw?5Rg zTOMlo>6zIJ$VNVgn(@kTSL0eP)nR35IHpoHM2W#h6cNmTm@-9`dFJ$;k(S`7Lg@RY zp!hNmb9un!O4Wt05ANDGirv(B14gW| zwjP}C9bK{J`qZ_S2o)b`RonR-b8~y8)$H0`+gg6>#^wu8eCp9xA9B>>8(KRizI?+^ zAJ#i>*({qM-c4gBB~5dzg(wj!HA`hkh!aDl5>u&J;>2K#Ax2)2wt|L!9X;(=*jy!`r4_FhCBoRxNjXNv(~jGQ|%<}%K6RimaBJcP0v}oCgRN3B;oiM)opj? zXm;;tv3q-yy}NqMOr^~3&1lW$w3}UK_IT2sCrkYx5$&6e2A%g;QZUX~A&L!2rFd0p z5%men@^zN_Xw2|v%*c2|wQfkN4r6u&k;LxYY+w3{KY#cie)!iz>(yAgt=&-+Sy2V& z9BJxI+VMKQ%dvY~x>gmEijj3ss_*NAT(8d1@DQ6e&#Ln&6Qk>wHrh>;V2nvomC`8& z(w?`?*_^3u-TJrMzv2~7dH(XLJvUOXk4U8oW6Ol)YsawhIB{GdvIzu1hzMTrE)cvB z%2GxMpaF89<9uF(?cfN(BNR?wwWvCZ6e62+G_{$+;`yjgLj{(^z*zzwd;K3RElb*%=??P zm+lLY0@Y}^kVdMYX5M)YJ~8h=i(S{q#NfU0xPTao4WPDQL=Y_;vg=p%iay1_`<0Ga zMG&<(pOU+bI2u9_g8IJBTqGX*3@G$Zc`pj0f@)vd2?Aj`ms>DHg>;w~p}HXV(*VJX zphd;fht9qL3E)D8h$$A;SGl22Ygv>`iU=A)z=1ZYN$|2`*$`R)?KD>$tw_e9h_x~eX_udS~Q%yz?48i*aIa+_wx|j{B zsG7mwZ)6M3dmvgMC3K-66;ML(9o2xU!F8+qF)>v{1;ip)6v_I)6law|rd_Dx2oV|n z(Qm_PUnTTuKFG)w%s|)lS!w~Lm$k|Al=0djocyHU;>1H=!N}0E0lSV^b2^6~^lUco zyoH+|_!li3#euHd4TJS8=CLaHG9H8g&h3Xm z#>BkpUBAmae(#)qO3)ZMG3irM=5IzA^s+)w86=tIMT{&?Awux<(k2>U#n`c&@Z?u= z%=#BoO-9Nc^?)hz*YW~~tU8rLR-MZBJsY_7fp2r~mY>q-O;L%5Fp?}V6CK=F(18U3 znxB8ZR0TT{)T64RDt!+yFgp!JXGP0|It0Hz2Em#YfRv>O>8A?J=Sz!nq<|{&mW=?~ zDQT{S6PH0|jwy37t+0Ob6izz)JdRlNEUbyk>-K?}FOT=Dj9SuS_0nTFd+A^D?Bo83 zTkicXcW=IuZoZd(Dl;&#`LI;_s?e;OH9quf?*XuV0O$Qh0j~HWKpA|PXV4&b2zs z@W5<)dtovIRZ@gvsi$^s;v05(XwF3$lJ;wzYfE`46fnT7>!qt|hWHRE>yQP)i8= zVbC|O{Ud6%kwGcch>>|pE-=?cW;TDR0lE5Nw7l66lr-zIYT3bj^ujCn$b0{ZO;gwK z#}}W(*T3~in$6ZCpbB98pftPTo;!K>U;H*7_}t4m;;4i9#^2t`pS<=jsnx198);d3 z-M6Mx{7-c0A-jhJQ`5mBy8TBnfbr2~sER5E5oz}=so34cg)GYarRWi8w#W$%G{?Z*4xDb#LX1B1 zg!4G{m~*)H_J8J^SNt`XU-fxjea`>p_$Qyn*Dn18*WdPCp8oWw^XU)%kfRQHMgfQh z1j_ua@O4G%QK;&YH3Y9(q!hkgOUCkcVH5N0Ug(EPX%H6qCfPqg))qrd#ec^47dBu- z=sRkmjGS>3K(tfRTo;zCXO-74hV;y1!vCN}v|w?AWR$YpYXs@Dr?iNLKD9s|2)0aHY!TKTYhwMI z7b#54h!H6rUU9+xnL$g6h?t?Li5guXPY1g)$bI$~rHWP%QkYJ6Y-U^0C(@*$ruN2*zn0QRBOeVpgMFbT%k!Dn1*u#%J^y)enX1K;0~ z%3Q zP(b%}P!Loj6M{v96(Qa~K!bq-V-P89U_K)0zHC_F#L==3IPh2hHG6&?rxvQ%|EljR zfGIDyu=rIrl1dyjuMfwuh?pXZmARwNZ?GbW;5BH5D#nN|WbGm+UGAh7_AcG>4&|{0 zrg?k@h8zm!0A|5Zo%X%g|2tBPKHHB6`~4h?I@bepDe6?^f8w zBnzfOf|j{kR5m6BLRr0$!RZ$PHSk*)tyjkws*DpyHIiiL*8o(Smx(OKT7@D&Y3OI^ zEUMtKa2*SLjt(eJsZsLsrgV`A+xL(~JN#JU6+L)gCe%VuSNbCzTr09w>eZ#779SKV z)m)@#TNVy|q3Tz_U`^7MY`l}`GU~OlQi|*cprX?tm@tIV+8kOGkaa=9Y<{N|RZ)ns zHlgnz2S%qwK9wXjest~Ux$YNNA{0?6Xpv{_mqYt8D`g&7Yb~>lX+HP&AK<=+Zl_kO z6a2g`^4=9W92GQ3e9Mk6?DlzlkIM`iOzwk*5L81TcuyYkI-<3^@49_+^XC7&N}SL1 zh$kIBxb`9+v}acfV?FQ zN#04eHe0*j{pz=zOj3#EHLrT3e)O;3xqpCWrl$e)PcD9jQ4P-8_zyZg^M7i|*kOuj znsvlwNUsy5+01^P_sqMOjXjxKwHn4)$87t-MWZZ*5Dbit4|D9vL+spsJ0JPd?{Ms) zFW^<@yqjZ=IvG%$ck_Cu9|b8CvoV%5P5IZWzs>i4`~`N+-p`7a6RbLHJ;nxtSB#Mb z`1I552=9DrYWFNZ{-=Mt;SVo5@3cmv`IZT@@>#~zCe-=qENxsn+uHfL`e?SbT3IQ_ zt~e)Lcirs_S5^X#?hDYmgV%8QQDe+?>*1&0e^BnaeZz(&D~3<)#QuUL8h*NlXgtr| z&a{_Z)o9FK_U5<0!E3N|yY1P2g%J9s*?!zF78+NSb%!ix)tbQ09oO&|U$~Bwk35^- zec9VN^xz{043e^xD}WEmzh8d^-~Pd8**bEfd+I?HuO~n4SksoN8LRPUy={E<@BjRMUh?X71Xaey>t^$&Eq2B7)u_r$ z|IQwpG52G!F$J5fRo1LqLB7iKz_!bI@27skX~+Eze|Y}IBuRp?hR7z|eA~7B<99#7 zrX4r2a_tCDUb_}Cg)g!OEVeJ5AEVRyb!9~f4OL68qhZZRP0l*>MdkxvxXeGWx$T>+ zI^X!wnYQDnwK9?i)j)eLXJU2Cw>~>R?72@MecvT7;h~2gATow_cbc)$Ws+xNSB{++ zo^tTp^y*(-Y-XF=$XyoBJnMN9+p!Qrep1)%ym_v7zZH{;u~L>T=4XP!f^?uC4ULUR zdl`>x+DVkHVd;|9#N*oubBFQEyRT#UK^0c7T}l)eEEFS)qvZl%f>#I;iCwAWb=kW0 z(e#lm51o?d>D|kgtTscVQCNDAXMAjxSX&{_Qf)T((wMHWWLbz6WpPXP0(3_SBWwI19Vx?$i6WUqP$4O|wjNbYzst$z{58`cBhm z&F(N-KeXFzo#aC|6BbC($As#B8X=}ggpDyQUp|Q>9cG$47#>TQn%T(eHA`5se7KnZ zF_dj_6NN0xS-oZ%Nj%PTpK=MC zw*4IMGls_v)mokI)Dph*pD<)7prEF|j6I$2=XF=Ua3z;BN^yt&H@G%7& zWnL7*e0S9svjSP>kuc;VCbZXUN3G7D8`G@!Qnjt=p=7yC?QH0tsa@RsuPMLj@wf-c z|LV)H$Auga+MTAU#>)eeuh_L`!qC=Ls|{m}Cy)|w6#aP}w6_-ya~9LF z{dQAPa-|&ME858gIK=}lVK7MLT~Oye&UM9y?0X=8Qmvb*)=X}iv%Me)Gqav+FWdGT zuk&#ak~?2Kzf}w)xZuKGx%+`1?Ecoq?*H@EjFm%C6OT577vWKoJB z$A^sIasm!5TGOFFGmHkKNTE7KW3nveUq1bt4Uj)!1_6BJ zU6=EoPrjVdk+pQX+j-GTpQS&&^43tT43kuRlvE8fGdYc!1|m)3WCuwlqB>NeQc0** zYE&wTj*QpuPLfJ)j2$(`sI@k@oR!^9d(3&Kd6r3*<)pooPNzq=)1%#NQ;nAsF*5VR zOYXQC;B^4*Sik--jy?J`uDj-! zSep}9YT4*SOrT2I6MF4H+EZFRPh+}^b4@i8OYk9Y&86o*Y4(`Ax1W4#tX^5m6LjZPb61LF2?qBy?B_?1YE!nej)R5c8qG`2s_uF`Cu+ z`X_$#2Ur#!Pw0WVd60fYG8A#y55LDyJ!Yt$5G6Efb<6Nr%-BTC_|llMB?%*A5%rOX z`fyBbD5g@4Ns^)P;F7zjv{t6u?k1J0kR*v#Dhair3iXjH^^qz=!xd`vm`W`oN-Wj_ zNML7~t!rRbc|9I0mUjpEgOJ9XGg2;vjDZ;b~V638P!uVuejytg~ci-I(n9#M6AR=mQG0YjoLKGPgFp(jS4Pn7UJR)Et z-8ZsqWsRLXri#f_BSeWIat3P+Q3Td1#ws={2CLGpDdvrgP#KD7 z&SnaR^#_Bsq;Xt;kyI^}iX~1WYzdHamc$tH1#Mz6f<2(WuH^s%^yXK78Gyg}{;LNA zoW%$)#R!a0wv&q%qj%+~i3^k&1jY!ljfi82Vr$~W5G6u&$Wp0VqR3*bDIWLE4Y64K ze08)CmeFrq2>QGFSDAk%Rhs}$r*rJVNuoO(~AJ!PG{T~d_i(dQ;OsQc+q&twwlJV|`Bv$N}R$K=uxCPyc!RBBXfRjRcZi5yAQk|YKj*>d`|Xw~ckP!!SW%^gsH z4oDR1AJt?S?}B;<&e0TPFsNAMQwxCt69o{uA>=K^qd1+MST3tptj8GHnN(upgb*ji zq`i%b+{{=o7ByB78@8!x_Gs&uqLOKv_6{gO2b4jbc8YT@EEzqBp!v_c?XXFx9Dq zb{!I|Nu<;4kZbyl3*LDg#$f7`nKwT9p9|2|t&fmAe64Of^c3TKI%Q?_^+uxaj|?xL zw5U4G#YlpQDngbfM)q85qt=DJt|y5nG){VqE;V8I&WBCAH+|pe@QT+};^BWB8(lGB zqe!DD7GqI`0pj%h;hm z;n?F&(5YS1X4{T?Hf24&;~ic?rDC*Zgk;*ga9b~Je`?R%gBQy3U5$!cEi-#s>T+d# zWH}Mbv|6p1R<`wiiPB32Gn*u}EQxC^LGJIR?H}~g*|#s5IQY`pJzcYP=0El5RWIen z8*k;5(^qldFJ}(enhxl1pnB_vPi5uu!@1|-9|Owd=%J>WPwQ>dkLW|!5WV<$<73Xb z{0CRJT1OpP567)vYea*J7*!3_M-nC`C)l*@dKzsw^5El5v)K$c-nf?sZ)?i>Gc=yt zg{xL=urnv{!j}h=hh{KFAjIS@=h9C!xJWW@nmR0Ns^Wrk)72_X;&VM@qLNZyn;-h1m-)j4PH{!#b7fObo=TF+Xw z)_t{JRqgNW{e9m)=MZ*rJl6A%IHK!gcqM)U)>TjF8ytMTRLpN39jns9J?@oOe47l4 z1dw7d06;*nuu_+V$6Qs4K>#PCRHVFExV^duw#+4>?(j) z*AHP%*L5@qEpM#j?*@5nOq@HlBR^5M@^_J9)U!&MV7N?QAAfFbdJaGWPgRws)6~+R z-NrZmx0V*7Od$!{dkY1w*wll3j_1b``)C%NHS6N>yBU998+?y%)4SU2YA} zA%$NKSGVi)4!sVH=l1lla~XcBLKrfnO2~CXCa>$GlX_p?dYsM`3%)hidhs()bzlDL zr7zEG>kK#SwpW`1YyR;!pa1&-`0t?)V)3FnK7V~pCo%hYIQUj+f?7Oh#@-(|a?XKA zr;?n->{Mx?{fOYn3n4;UD5a5kBx9Z>DQ1SETOzUjjZ`HF0&e`i-6T<17qM|ec7?fBc z;0k&%hz+o?+KMG>1)PSqUSqTR@!luCa_YiGo3TkPUp^w8T}r$YFf$gPyy|ZYU`={9 z3c4MNG|FgE6ETxVuw_~St-lefEMgF+NTdzZD8wWJ0s<69@frs3IxH*_A4`(dIZhJT z)TwApTxD36oOSS>-?;UKV^n{)k!mFpfWRL3*Rxl@V_bS?f`4@I!*C2lX%(H}L=`CT z0BxGtLQ@`yX#0U)3`bO@9NHBjM^*Gw64K=(1QdKEK*p+u<&qTSoUzKhfO`4Wz>@z)uK^Aw6m!k{QPq@f~bd?t)6?} z1bJ=k7!E&fDxUmP-(QVQ?F@i8a-dv4%Gg64haX`yNv^E%Ea<=YJ4SdqH4e{1~Sk?qbu|M;*f zbqpYh(szvQ9ev=Amrj8q0@9+|SbxTQw)=Lr&Hm@e_hY2mXXchai5dBmusvCYf%>!X zK>#8PKtTjx&+y*EIR|SkT*`=|2>VPq0kb=fM~F#u|GG<9sj?zc-#-8BqmC*-%N5t% z3v1um65bJjO9}`JV*qzjs9O-*vCma1qq%z0=Thg*sPtm8u4CiyU5H^JCTU0mH2?_M zGn{jci{Y)p`kvomV&MR6*th{{opqpyh3Ux4m)!GykUSWKMk@t>>SyNTwj2L%XZ{Nn z>Xv_j0zm+HA-wSFCJ4n;tqux{Z<*M!+ghP`mh}};q{({$d;y{&M#518E{~{H2e(KJ+~I! z(QA0${wLzt8F#!r1DoX%bYVIIT!6Y1 zJctN_2;>9AahjEz5Cm@p&;a2*ykj`$0UrSH$QJ^n3By@S!UCJh5jS2|HIuruyXF34 zRDv0v?9yEOYVFWR0jftU~yzAQIFKu_~N!vxLSpD zIxEmBpAwnRC3gEyg%Yon(xeEA2t*11fhfB~8i^HvMIcQOp5dF9V>l7DZ+tS31TC`?6B2!P-{Ai`NS%8sfWFCh_# z2!sJ<26G0;dxnUBNT3Wrj-j+52u(2zc*4ieoxAxfi_hFMD8$Dt*t4hHU+Z6a>y4`) z-dgRJ&wT2GICjQeJ24|X4P=?_kA+q7QY|L{F) z>E#!CslTU!sFuPzhBSJAZ4?NAGFdr600O~tQ;`JDd9Vkv#1X>KptUV8Q)hHgp)4=n zf7k1aF8a|v_e`5zKCDz~Nuz3ARYohScS~Kpws!0=fL0XBO0`T-YycqYn}yY@ZV?g2 zlnDnM86|@t(hM=mC6W&G)j}8N_Fwtr#>s`2R4qD9xuZ_o&BU=o5&`up5LX5DnnxN7 z(!|510_PdtJ9u$`Fq8(A0!#>KLogu_1c1^6@0sdRitRngzWe^er2PiAMIqpkE7Xj4 zqSD0i@PNn2cHaUJ;)tnGEM^?Y2OX%5fOPNhi#0IY;la!zy_Gm@B#Lw#(Mo_^%= znu44{7-|HeMy{k$Y%?&%Kq&>KG_*4CK85oRio&-@sE4y2Y3h;2*%j9ragC&24JaC` z`!uzlS%RjYWaMg=C2{s!Ax`QU03w3c0Yn(2{;azYNJdU3mn!CrxI&4*JCC^T#}y}2 zA`QzFa=EsmQ0RGvftbU zQ>{c90A|-98)Xj4nT0b0yyJf8t%xIraRd)QQ&z*I6o?d@PmrXe$eT_q-0f@}wCCAq zEl$Ss8*j&&jkjWZGSHg|Kx;aNPWFa9~0$jGSbWOU>XjH6xDc0w(iTEtcE6dO3#5TC{ScvW=I(b=Nv*)M5VtC-7j0@OiMO};u|K_aA+ua&Wy|G z0O?p6>sL7#>4bE^@$`cedW&;pHYGbq)cE=gVUygN~?!_hF|0teV`9}~ml+s!M!x_o7(s*;* zCVc-VU&If8em*{M)JJgGyiZ}QGSUDFC<*}~u!v@1)yzPXBMKoDa!^zNBmjHLN~pCo z86Fi-BjwE?n=_NmIA?K7liV3M;v_;xTNl23?ow=ga}EA*-%{NFA9)Ej6(HYiJs85m`CL9ANNz_7Wfw>}W{H&o zhy)^>0cdZXg2B-WvL1};5P}FJQvqpeDFK{}*W_F4Q?l}yJ$-+C<-Fxs|HfnZ?SC!9 z1CQT|j+S@fx%Cg={YRgO&z2Z>i~diz*O?*BnAkIbU{QcAP}Z33z=$xNR5+KgfMs35xDG&i*Vb0Kg44zZ^zZ& zc>uXE4-p1))`B-&1MC}R(r5-n0MAaC)!S!3D{E#4D+*c5&ME_7bO-`vnhuJ0%rG^y z*MSI{U{o_J!WqGvFVAW?BdzlmMhBQRZ2?B+Z$U21!?_gN1W=^F4PGQ^jHW1{`Cb9o zLx~8DXBkZ|AhymqMH-oHxQxU~>&7f9WD8o#QYOvxW(yKUdVH3~XXbxdwyFjxt+lAv zZaWSag=@ z=8P$&K}1lbY?iX@ee4?s0wKUBJ964=H$0STaA3T?n~R$9CTTo$W*+}*eEXdRL>ghx z0ulvhz0Z>9A)>e;5?WE{3wn~(Mxl@k5Z8vY60)g)Z7AM`NMj7L0~nqG?*MV$0cj#* zg?t%+Zb&IZs~iSLH{&P2T8vGbH$W*3fW~XQxiirODk4xy!&-;m-f<)T^zbbx6J$2bI!+g&Q(Tb>mTpfw(MhPbbX*24YD+xC~pjzlg4B?I0>ZG1eo;$GZ-@3q)Ayc(TT%9uB8CcO9K>t$rJ4+!Ga!{2blb3*{mJ?rAx;e_@g zW=}sb8SURhsg02gkr06Qo;))H{@ois2J0*E-a_ku;$#FwS}J2z^z{y5!Tf{u-m?$! zW7XmPw~xK}Y|U*DV-zVxM2Z?xn6(ROnxdy?JIXW%Qzy=WHv^~-wPRiPJ(xPPjP?m_ zU@!3AH)Mt2y@NuFGk%)cvT4gxH~;vV!~gKarE2vv&(f8P@Ag++xft8kE4o&xvN3^V zhgKTPzIFc&iMV*lvDmVC6ReMr3kzh>qKs;xT2uwI^KCQwiCuxGcI>;nX1mYH6|D_I zV?e$kJ`M5;L7M=zY84}cF$$#|Dx-Bwp4xT+U;&*D<@0j8tMo%x5%Tg?~5R?T=3cv%@lt|5rbf!U~$$KWHR3?Xk zu&I|c5%P}XIIb@4XrJ=aC`y!W*}^Y88R7A}hVa+MJ05U+?`P+M8rvjM6j3edroqA2 zxm4Kuj7oLnm$`fxbar$}K3^bGfWT*$Wd5R*hEfJ52%w-LATTp*YNZ}ksTNg7J=bnd z-Pkqa!RO=D(kYB&|Wjqg0rvF8kum{NfucTYqrP z`5U%u**G!G6{S=zQMp`3K3_yWUyzoz^2Q(tmC>3+s5Oq`4(BY=)S@2MFgiNo;u?&k zg`0}`37-~9P0%vHiA@+H2!cEy8o#>wuOImB)G_Pj7yce!TXGVt#ORn z(=jFB*q2Zp6$}lGp?}+$um^#4QjKaSEI75c$z6AAYL348>#uKEccl>fFbuUZ0R$d} zZ~}6sT!$|qC`YPurgrtQ76=RC$YS~T-}$t1r_YJ6x+vSq`|xwOl@gGLU>BhcFBv~FMie-ahi$Rz-LINpu0Hu~Za`}LYEdk2y0hQVU6k7}mB|~9e!x(}I6ii4k;VvE0 z?|KG+Oj%0Bi3m(dlp;$c5Cu`1CM@ypLV(%bX9 zr_WVSKiJ10x1!vdPr`gLXF?@f1r%~#N8UkH?XgO1p%e>?-DLnfb z=86?7j~f~sKElT8lSw^&-{|PJ_Z)D@o-cw6^yvN1aY@hS38meM!r|M7s_XW%93Aak za$IUh=gpcu=jzR`4$^18^F8_11#h4-#Jd^}{s&{CB`(>qac=+s03~!qSaf7zbY(hY za%Ew3WdJfTF)=MLIW00WR4_R@Gcr0eGA%GSIxsM(l48sN001R)MObuXVRU6WZEs|0 vW_bWIFflPLFgYzTHdHV-Ix;spGd3+SH##sdcWUue00000NkvXXu0mjfB?gph 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 71c6d760f05183ef8a47c614d8d13380c8528499..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14383 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 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 From 0c185c78e366c5087ab3fcc273d8c99e4ad75585 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Tue, 26 Dec 2017 11:54:31 +0100 Subject: [PATCH 07/50] update support libraries to 25er version, some progress on AutoFill service implementation --- src/KeePass.sln | 90 ------------ .../Properties/AndroidManifest_debug.xml | 2 +- .../Properties/AndroidManifest_net.xml | 2 +- .../Properties/AndroidManifest_nonet.xml | 1 + .../layout/autofill_service_list_item.xml | 40 ++++++ .../Resources/values/config.xml | 2 +- .../Resources/xml/autofillservice.xml | 4 + src/keepass2android/keepass2android.csproj | 103 +++++++++++--- src/keepass2android/packages.config | 17 ++- .../AutofillFieldMetadataCollection.cs | 4 +- .../services/AutofillBase/AutofillHelper.cs | 2 +- .../AutofillBase/AutofillServiceBase.cs | 17 +-- .../ChooseForAutofillActivityBase.cs | 133 ++++++++++++++++++ .../services/AutofillBase/StructureParser.cs | 13 +- .../AutofillBase/model/FilledAutofillField.cs | 13 +- .../model/FilledAutofillFieldCollection.cs | 18 +-- .../services/AutofillBase/model/W3cHints.cs | 74 ++++++++++ .../Kp2aAutofill/ChooseForAutofillActivity.cs | 71 ++++++++++ .../{ => Kp2aAutofill}/Kp2aAutofillService.cs | 13 +- .../services/Kp2aAutofillIntentBuilder.cs | 38 +++++ 20 files changed, 496 insertions(+), 161 deletions(-) create mode 100644 src/keepass2android/Resources/layout/autofill_service_list_item.xml create mode 100644 src/keepass2android/Resources/xml/autofillservice.xml create mode 100644 src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs create mode 100644 src/keepass2android/services/AutofillBase/model/W3cHints.cs create mode 100644 src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs rename src/keepass2android/services/{ => Kp2aAutofill}/Kp2aAutofillService.cs (62%) create mode 100644 src/keepass2android/services/Kp2aAutofillIntentBuilder.cs diff --git a/src/KeePass.sln b/src/KeePass.sln index dc4bfbc0..a2837f93 100644 --- a/src/KeePass.sln +++ b/src/KeePass.sln @@ -27,12 +27,6 @@ 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 @@ -289,90 +283,6 @@ 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 diff --git a/src/keepass2android/Properties/AndroidManifest_debug.xml b/src/keepass2android/Properties/AndroidManifest_debug.xml index fa4d8262..41eebd8e 100644 --- a/src/keepass2android/Properties/AndroidManifest_debug.xml +++ b/src/keepass2android/Properties/AndroidManifest_debug.xml @@ -1,8 +1,8 @@  - + diff --git a/src/keepass2android/Properties/AndroidManifest_net.xml b/src/keepass2android/Properties/AndroidManifest_net.xml index a90903aa..11be5674 100644 --- a/src/keepass2android/Properties/AndroidManifest_net.xml +++ b/src/keepass2android/Properties/AndroidManifest_net.xml @@ -5,8 +5,8 @@ package="keepass2android.keepass2android" android:installLocation="auto"> - + diff --git a/src/keepass2android/Properties/AndroidManifest_nonet.xml b/src/keepass2android/Properties/AndroidManifest_nonet.xml index 8783c768..85bef5a3 100644 --- a/src/keepass2android/Properties/AndroidManifest_nonet.xml +++ b/src/keepass2android/Properties/AndroidManifest_nonet.xml @@ -6,6 +6,7 @@ android:installLocation="auto"> + diff --git a/src/keepass2android/Resources/layout/autofill_service_list_item.xml b/src/keepass2android/Resources/layout/autofill_service_list_item.xml new file mode 100644 index 00000000..8d06a671 --- /dev/null +++ b/src/keepass2android/Resources/layout/autofill_service_list_item.xml @@ -0,0 +1,40 @@ + + + + + + + + \ No newline at end of file diff --git a/src/keepass2android/Resources/values/config.xml b/src/keepass2android/Resources/values/config.xml index f2351554..29f65174 100644 --- a/src/keepass2android/Resources/values/config.xml +++ b/src/keepass2android/Resources/values/config.xml @@ -43,8 +43,8 @@ https://github.com/PhilippC/keepass2android/issues market://details?id=org.openintents.filemanager https://openintents.googlecode.com/files/FileManager-2.0.2.apk - KP2A Internal File Browsing Permission KP2A Search + KP2A Choose autofill dataset diff --git a/src/keepass2android/Resources/xml/autofillservice.xml b/src/keepass2android/Resources/xml/autofillservice.xml new file mode 100644 index 00000000..f47169a1 --- /dev/null +++ b/src/keepass2android/Resources/xml/autofillservice.xml @@ -0,0 +1,4 @@ + + diff --git a/src/keepass2android/keepass2android.csproj b/src/keepass2android/keepass2android.csproj index bbd12639..adedc61b 100644 --- a/src/keepass2android/keepass2android.csproj +++ b/src/keepass2android/keepass2android.csproj @@ -1,5 +1,6 @@  + Debug AnyCPU @@ -19,7 +20,8 @@ 1G false - 9e78b013 + + True @@ -103,17 +105,42 @@ - - ..\packages\Xamarin.Android.Support.v7.MediaRouter.21.0.3.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.MediaRouter.dll + + ..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Animated.Vector.Drawable.dll - - ..\packages\Xamarin.Android.Support.v4.22.2.1.0\lib\MonoAndroid403\Xamarin.Android.Support.v4.dll + + ..\packages\Xamarin.Android.Support.Compat.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Compat.dll - - ..\packages\Xamarin.Android.Support.v7.AppCompat.22.2.1.0\lib\MonoAndroid403\Xamarin.Android.Support.v7.AppCompat.dll + + ..\packages\Xamarin.Android.Support.Core.UI.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Core.UI.dll - - ..\packages\Xamarin.Android.Support.Design.22.2.1.0\lib\MonoAndroid403\Xamarin.Android.Support.Design.dll + + ..\packages\Xamarin.Android.Support.Core.Utils.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Core.Utils.dll + + + ..\packages\Xamarin.Android.Support.Design.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Design.dll + + + ..\packages\Xamarin.Android.Support.Fragment.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Fragment.dll + + + ..\packages\Xamarin.Android.Support.Media.Compat.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Media.Compat.dll + + + ..\packages\Xamarin.Android.Support.Transition.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Transition.dll + + + ..\packages\Xamarin.Android.Support.v13.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.v13.dll + True + + + ..\packages\Xamarin.Android.Support.v7.AppCompat.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.dll + + + ..\packages\Xamarin.Android.Support.v7.RecyclerView.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.v7.RecyclerView.dll + + + ..\packages\Xamarin.Android.Support.Vector.Drawable.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Vector.Drawable.dll ..\packages\Xamarin.GooglePlayServices.Base.27.0.0.0\lib\MonoAndroid41\Xamarin.GooglePlayServices.Base.dll @@ -191,12 +218,16 @@ + + - + + + @@ -904,12 +935,6 @@ Designer - - - 22.2.0.0 - False - - @@ -1741,10 +1766,56 @@ + + + False + 25.1.1.0 + + + False + 25.1.1.0 + + + False + 25.1.1.0 + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/keepass2android/packages.config b/src/keepass2android/packages.config index 1a50c8bb..2e6476f6 100644 --- a/src/keepass2android/packages.config +++ b/src/keepass2android/packages.config @@ -3,10 +3,19 @@ - - - - + + + + + + + + + + + + + diff --git a/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs b/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs index 96ac5b7f..8e38a148 100644 --- a/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs +++ b/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs @@ -12,7 +12,7 @@ namespace keepass2android.services.AutofillBase public class AutofillFieldMetadataCollection { List AutofillIds = new List(); - Dictionary> AutofillHintsToFieldsMap = new Dictionary>(); + Dictionary> AutofillHintsToFieldsMap = new Dictionary>(); public List AllAutofillHints { get; } public List FocusedAutofillHints { get; } int Size = 0; @@ -25,7 +25,7 @@ namespace keepass2android.services.AutofillBase AllAutofillHints = new List(); } - public void Add(keepass2android.services.AutofillBase.AutofillFieldMetadata autofillFieldMetadata) + public void Add(AutofillFieldMetadata autofillFieldMetadata) { SaveType |= autofillFieldMetadata.SaveType; Size++; diff --git a/src/keepass2android/services/AutofillBase/AutofillHelper.cs b/src/keepass2android/services/AutofillBase/AutofillHelper.cs index 22005b33..fbd42107 100644 --- a/src/keepass2android/services/AutofillBase/AutofillHelper.cs +++ b/src/keepass2android/services/AutofillBase/AutofillHelper.cs @@ -72,7 +72,7 @@ namespace keepass2android.services.AutofillBase /// Autofill fields. /// Client form data map. /// - public static FillResponse NewResponse(Context context, bool datasetAuth, keepass2android.services.AutofillBase.AutofillFieldMetadataCollection autofillFields, Dictionary clientFormDataMap, IAutofillIntentBuilder intentBuilder) + public static FillResponse NewResponse(Context context, bool datasetAuth, AutofillFieldMetadataCollection autofillFields, Dictionary clientFormDataMap, IAutofillIntentBuilder intentBuilder) { var responseBuilder = new FillResponse.Builder(); if (clientFormDataMap != null) diff --git a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs index 9050110b..b6e5c627 100644 --- a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs +++ b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs @@ -9,11 +9,12 @@ namespace keepass2android.services.AutofillBase { public interface IAutofillIntentBuilder { - IntentSender GetAuthIntentSenderForResponse(Context context); + IntentSender GetAuthIntentSenderForResponse(Context context, string query); IntentSender GetAuthIntentSenderForDataset(Context context, string dataset); + Intent GetRestartAppIntent(Context context); } - public abstract class AutofillServiceBase: AutofillService, IAutofillIntentBuilder + public abstract class AutofillServiceBase: AutofillService { public AutofillServiceBase() { @@ -41,10 +42,11 @@ namespace keepass2android.services.AutofillBase Log.Warn(CommonUtil.Tag, "Cancel autofill not implemented yet."); }; // Parse AutoFill data in Activity + string query = null; var parser = new StructureParser(this, structure); try { - parser.ParseForFill(); + query = parser.ParseForFill(); } catch (Java.Lang.SecurityException e) { @@ -53,7 +55,7 @@ namespace keepass2android.services.AutofillBase return; } - keepass2android.services.AutofillBase.AutofillFieldMetadataCollection autofillFields = parser.AutofillFields; + AutofillFieldMetadataCollection autofillFields = parser.AutofillFields; var responseBuilder = new FillResponse.Builder(); // Check user's settings for authenticating Responses and Datasets. bool responseAuth = true; @@ -62,7 +64,7 @@ namespace keepass2android.services.AutofillBase { // If the entire Autofill Response is authenticated, AuthActivity is used // to generate Response. - var sender = GetAuthIntentSenderForResponse(this); + var sender = IntentBuilder.GetAuthIntentSenderForResponse(this, query); var presentation = keepass2android.services.AutofillBase.AutofillHelper .NewRemoteViews(PackageName, GetString(Resource.String.autofill_sign_in_prompt), Resource.Drawable.ic_launcher); @@ -73,7 +75,7 @@ namespace keepass2android.services.AutofillBase else { var datasetAuth = true; - var response = keepass2android.services.AutofillBase.AutofillHelper.NewResponse(this, datasetAuth, autofillFields, null, this); + var response = AutofillHelper.NewResponse(this, datasetAuth, autofillFields, null, IntentBuilder); callback.OnSuccess(response); } } @@ -95,7 +97,6 @@ namespace keepass2android.services.AutofillBase Log.Debug(CommonUtil.Tag, "onDisconnected"); } - public abstract IntentSender GetAuthIntentSenderForResponse(Context context); - public abstract IntentSender GetAuthIntentSenderForDataset(Context context, string dataset); + public abstract IAutofillIntentBuilder IntentBuilder{get;} } } diff --git a/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs b/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs new file mode 100644 index 00000000..de501975 --- /dev/null +++ b/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs @@ -0,0 +1,133 @@ +using System; +using Android.App; +using Android.App.Assist; +using Android.Content; +using Android.OS; +using Android.Service.Autofill; +using Android.Support.V7.App; +using Android.Util; +using Android.Views.Autofill; +using Android.Widget; +using Java.Util; +using keepass2android.services.AutofillBase.model; +using System.Collections.Generic; + +namespace keepass2android.services.AutofillBase +{ + + public abstract class ChooseForAutofillActivityBase : AppCompatActivity + { + protected Intent ReplyIntent; + + + public static string ExtraQueryString => "EXTRA_QUERY_STRING"; + + public int RequestCodeQuery => 663245; + + protected override void OnCreate(Bundle savedInstanceState) + { + base.OnCreate(savedInstanceState); + + //if launched from history, don't re-use the task. Proceed to FileSelect instead. + if (Intent.Flags.HasFlag(ActivityFlags.LaunchedFromHistory)) + { + Kp2aLog.Log("Forwarding to FileSelect. QueryCredentialsActivity started from history."); + RestartApp(); + return; + } + + string requestedUrl = Intent.GetStringExtra(ExtraQueryString); + if (requestedUrl == null) + { + Toast.MakeText(this, "Cannot execute query for null.", ToastLength.Long).Show(); + RestartApp(); + return; + } + + var i = GetQueryIntent(requestedUrl); + StartActivityForResult(i, RequestCodeQuery); + } + + protected abstract Intent GetQueryIntent(string requestedUrl); + + protected void RestartApp() + { + Intent intent = IntentBuilder.GetRestartAppIntent(this); + StartActivity(intent); + Finish(); + } + + + public override void Finish() + { + if (ReplyIntent != null) + { + SetResult(Result.Ok, ReplyIntent); + } + else + { + SetResult(Result.Canceled); + } + base.Finish(); + } + + void OnFailure() + { + Log.Warn(CommonUtil.Tag, "Failed auth."); + ReplyIntent = null; + } + + protected void OnSuccess(FilledAutofillFieldCollection clientFormDataMap) + { + var intent = Intent; + var forResponse = intent.GetBooleanExtra(CommonUtil.EXTRA_FOR_RESPONSE, true); + AssistStructure structure = (AssistStructure)intent.GetParcelableExtra(AutofillManager.ExtraAssistStructure); + StructureParser parser = new StructureParser(this, structure); + parser.ParseForFill(); + AutofillFieldMetadataCollection autofillFields = parser.AutofillFields; + ReplyIntent = new Intent(); + if (forResponse) + { + Dictionary dict = new Dictionary { {clientFormDataMap.DatasetName, clientFormDataMap }}; + SetResponseIntent(AutofillHelper.NewResponse(this, false, autofillFields, dict, IntentBuilder)); + } + else + { + SetDatasetIntent(AutofillHelper.NewDataset(this, autofillFields, clientFormDataMap, false, IntentBuilder)); + } + } + + protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) + { + base.OnActivityResult(requestCode, resultCode, data); + + if (requestCode == RequestCodeQuery) + { + if (resultCode != Result.Ok) + OnFailure(); + else + OnSuccess(GetDataset(data)); + Finish(); + + } + + } + + /// + /// Creates the FilledAutofillFieldCollection from the intent returned from the query activity + /// + protected abstract FilledAutofillFieldCollection GetDataset(Intent data); + + public abstract IAutofillIntentBuilder IntentBuilder { get; } + + protected void SetResponseIntent(FillResponse fillResponse) + { + ReplyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, fillResponse); + } + + protected void SetDatasetIntent(Dataset dataset) + { + ReplyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, dataset); + } + } +} \ No newline at end of file diff --git a/src/keepass2android/services/AutofillBase/StructureParser.cs b/src/keepass2android/services/AutofillBase/StructureParser.cs index 90444137..5966db3c 100644 --- a/src/keepass2android/services/AutofillBase/StructureParser.cs +++ b/src/keepass2android/services/AutofillBase/StructureParser.cs @@ -25,14 +25,14 @@ namespace keepass2android.services.AutofillBase AutofillFields = new keepass2android.services.AutofillBase.AutofillFieldMetadataCollection(); } - public void ParseForFill() + public string ParseForFill() { - Parse(true); + return Parse(true); } - public void ParseForSave() + public string ParseForSave() { - Parse(false); + return Parse(false); } /// @@ -40,7 +40,7 @@ namespace keepass2android.services.AutofillBase /// /// The parse. /// If set to true for fill. - void Parse(bool forFill) + string Parse(bool forFill) { Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, "Parsing structure for " + Structure.ActivityComponent); var nodes = Structure.WindowNodeCount; @@ -67,7 +67,8 @@ namespace keepass2android.services.AutofillBase { Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, "no web domain"); } - } + return webDomain; + } void ParseLocked(bool forFill, AssistStructure.ViewNode viewNode, ref string validWebdomain) { diff --git a/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs b/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs index f135a03e..8af0be3b 100644 --- a/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs +++ b/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs @@ -3,19 +3,12 @@ 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() @@ -44,8 +37,6 @@ namespace keepass2android.services.AutofillBase.model } else if (autofillValue.IsText) { - // Using toString of AutofillValue.getTextValue in order to save it to - // SharedPreferences. TextValue = autofillValue.TextValue; } } @@ -63,7 +54,7 @@ namespace keepass2android.services.AutofillBase.model FilledAutofillField that = (FilledAutofillField)obj; - if (TextValue != null ? !TextValue.Equals(that.TextValue) : that.TextValue != null) + if (!TextValue?.Equals(that.TextValue) ?? that.TextValue != null) return false; if (DateValue != null ? !DateValue.Equals(that.DateValue) : that.DateValue != null) return false; diff --git a/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs b/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs index 320ce1ce..393874ea 100644 --- a/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs +++ b/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs @@ -6,22 +6,22 @@ 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 + /// + /// 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 Dictionary HintMap { get; set; } public string DatasetName { get; set; } - public FilledAutofillFieldCollection(Dictionary hintMap, string datasetName = "") + public FilledAutofillFieldCollection(Dictionary hintMap, string datasetName = "") { HintMap = hintMap; DatasetName = datasetName; } - public FilledAutofillFieldCollection() : this(new Dictionary()) + public FilledAutofillFieldCollection() : this(new Dictionary()) {} /// @@ -29,7 +29,7 @@ namespace keepass2android.services.AutofillBase.model /// /// The add. /// Filled autofill field. - public void Add(keepass2android.services.AutofillBase.model.FilledAutofillField filledAutofillField) + public void Add(FilledAutofillField filledAutofillField) { string[] autofillHints = filledAutofillField.AutofillHints; //TODO apply W3C transformation diff --git a/src/keepass2android/services/AutofillBase/model/W3cHints.cs b/src/keepass2android/services/AutofillBase/model/W3cHints.cs new file mode 100644 index 00000000..11941ea1 --- /dev/null +++ b/src/keepass2android/services/AutofillBase/model/W3cHints.cs @@ -0,0 +1,74 @@ +namespace keepass2android.services.AutofillBase.model +{ + public class W3cHints + { + + // Supported W3C autofill tokens (https://html.spec.whatwg.org/multipage/forms.html#autofill) + public static string HONORIFIC_PREFIX = "honorific-prefix"; + public static string NAME = "name"; + public static string GIVEN_NAME = "given-name"; + public static string ADDITIONAL_NAME = "additional-name"; + public static string FAMILY_NAME = "family-name"; + public static string HONORIFIC_SUFFIX = "honorific-suffix"; + public static string USERNAME = "username"; + public static string NEW_PASSWORD = "new-password"; + public static string CURRENT_PASSWORD = "current-password"; + public static string ORGANIZATION_TITLE = "organization-title"; + public static string ORGANIZATION = "organization"; + public static string STREET_ADDRESS = "street-address"; + public static string ADDRESS_LINE1 = "address-line1"; + public static string ADDRESS_LINE2 = "address-line2"; + public static string ADDRESS_LINE3 = "address-line3"; + public static string ADDRESS_LEVEL4 = "address-level4"; + public static string ADDRESS_LEVEL3 = "address-level3"; + public static string ADDRESS_LEVEL2 = "address-level2"; + public static string ADDRESS_LEVEL1 = "address-level1"; + public static string COUNTRY = "country"; + public static string COUNTRY_NAME = "country-name"; + public static string POSTAL_CODE = "postal-code"; + public static string CC_NAME = "cc-name"; + public static string CC_GIVEN_NAME = "cc-given-name"; + public static string CC_ADDITIONAL_NAME = "cc-additional-name"; + public static string CC_FAMILY_NAME = "cc-family-name"; + public static string CC_NUMBER = "cc-number"; + public static string CC_EXPIRATION = "cc-exp"; + public static string CC_EXPIRATION_MONTH = "cc-exp-month"; + public static string CC_EXPIRATION_YEAR = "cc-exp-year"; + public static string CC_CSC = "cc-csc"; + public static string CC_TYPE = "cc-type"; + public static string TRANSACTION_CURRENCY = "transaction-currency"; + public static string TRANSACTION_AMOUNT = "transaction-amount"; + public static string LANGUAGE = "language"; + public static string BDAY = "bday"; + public static string BDAY_DAY = "bday-day"; + public static string BDAY_MONTH = "bday-month"; + public static string BDAY_YEAR = "bday-year"; + public static string SEX = "sex"; + public static string URL = "url"; + public static string PHOTO = "photo"; + // Optional W3C prefixes + public static string PREFIX_SECTION = "section-"; + public static string SHIPPING = "shipping"; + public static string BILLING = "billing"; + // W3C prefixes below... + public static string PREFIX_HOME = "home"; + public static string PREFIX_WORK = "work"; + public static string PREFIX_FAX = "fax"; + public static string PREFIX_PAGER = "pager"; + // ... require those suffix + public static string TEL = "tel"; + public static string TEL_COUNTRY_CODE = "tel-country-code"; + public static string TEL_NATIONAL = "tel-national"; + public static string TEL_AREA_CODE = "tel-area-code"; + public static string TEL_LOCAL = "tel-local"; + public static string TEL_LOCAL_PREFIX = "tel-local-prefix"; + public static string TEL_LOCAL_SUFFIX = "tel-local-suffix"; + public static string TEL_EXTENSION = "tel_extension"; + public static string EMAIL = "email"; + public static string IMPP = "impp"; + + private W3cHints() + { + } + } +} \ No newline at end of file diff --git a/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs b/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs new file mode 100644 index 00000000..143a884f --- /dev/null +++ b/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Android.App; +using Android.Content; +using Android.Content.PM; +using Android.OS; +using Android.Runtime; +using Android.Views; +using Android.Widget; +using keepass2android.services.AutofillBase; +using keepass2android.services.AutofillBase.model; +using Keepass2android.Pluginsdk; +using KeePassLib; + +namespace keepass2android.services.Kp2aAutofill +{ + [Activity(Label = "@string/app_name", + ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, + Theme = "@style/MyTheme_ActionBar", + Permission = "keepass2android." + AppNames.PackagePart + ".permission.Kp2aChooseAutofill")] + public class ChooseForAutofillActivity : ChooseForAutofillActivityBase + { + protected override Intent GetQueryIntent(string requestedUrl) + { + //launch FileSelectActivity (which is root of the stack (exception: we're even below!)) with the appropriate task. + //will return the results later + Intent i = new Intent(this, typeof(FileSelectActivity)); + //don't show user notifications when an entry is opened. + var task = new SearchUrlTask() { UrlToSearchFor = requestedUrl, ShowUserNotifications = false }; + task.ToIntent(i); + return i; + } + + protected override FilledAutofillFieldCollection GetDataset(Intent data) + { + if (!App.Kp2a.GetDb().Loaded || (App.Kp2a.QuickLocked)) + return null; + + string username = App.Kp2a.GetDb().LastOpenedEntry.Entry.Strings.ReadSafe(PwDefs.UserNameField); + string password = App.Kp2a.GetDb().LastOpenedEntry.Entry.Strings.ReadSafe(PwDefs.PasswordField); + + FilledAutofillField pwdField = + new FilledAutofillField + { + AutofillHints = new[] {W3cHints.NAME, W3cHints.EMAIL}, + TextValue = password + }; + + FilledAutofillField userField = new FilledAutofillField + { + AutofillHints = new[] {W3cHints.NEW_PASSWORD, W3cHints.CURRENT_PASSWORD}, + TextValue = username + }; + + FilledAutofillFieldCollection fieldCollection = new FilledAutofillFieldCollection(); + fieldCollection.HintMap = new Dictionary(); + fieldCollection.Add(userField); + fieldCollection.Add(pwdField); + + fieldCollection.DatasetName = App.Kp2a.GetDb().LastOpenedEntry.Entry.Strings.ReadSafe(PwDefs.TitleField); + + return fieldCollection; + } + + public override IAutofillIntentBuilder IntentBuilder => new Kp2aAutofillIntentBuilder(); + + } +} \ No newline at end of file diff --git a/src/keepass2android/services/Kp2aAutofillService.cs b/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs similarity index 62% rename from src/keepass2android/services/Kp2aAutofillService.cs rename to src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs index 7cb1cfbf..fdb81185 100644 --- a/src/keepass2android/services/Kp2aAutofillService.cs +++ b/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs @@ -3,6 +3,7 @@ using Android; using Android.App; using Android.Content; using Android.Runtime; +using keepass2android.services.AutofillBase; using AutofillServiceBase = keepass2android.services.AutofillBase.AutofillServiceBase; namespace keepass2android.services @@ -23,16 +24,6 @@ namespace keepass2android.services { } - 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); - } + public override IAutofillIntentBuilder IntentBuilder => new Kp2aAutofillIntentBuilder(); } } \ No newline at end of file diff --git a/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs b/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs new file mode 100644 index 00000000..d5c10b9c --- /dev/null +++ b/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs @@ -0,0 +1,38 @@ +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.services.AutofillBase; + +namespace keepass2android.services +{ + class Kp2aAutofillIntentBuilder: IAutofillIntentBuilder + { + + public IntentSender GetAuthIntentSenderForResponse(Context context, string query) + { + Intent intent = new Intent(context, typeof(KeePass)); + return PendingIntent.GetActivity(context, 0, intent, PendingIntentFlags.CancelCurrent).IntentSender; + } + + public IntentSender GetAuthIntentSenderForDataset(Context context, string dataset) + { + //TODO implement + //return GetAuthIntentSenderForResponse(context, null); + throw new NotImplementedException(); + } + + public Intent GetRestartAppIntent(Context context) + { + var intent = new Intent(context, typeof(FileSelectActivity)); + intent.AddFlags(ActivityFlags.ForwardResult); + return intent; + } + } +} \ No newline at end of file From e2df24522f078087df81f8b069ea4df676adaca5 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Tue, 26 Dec 2017 19:26:05 +0100 Subject: [PATCH 08/50] working --- .../AndroidFileChooserBinding.csproj | 2 +- src/JavaFileStorageBindings/JavaFileStorageBindings.csproj | 2 +- src/KP2AKdbLibraryBinding/KP2AKdbLibraryBinding.csproj | 2 +- src/KeePassLib2Android/KeePassLib2Android.csproj | 2 +- src/Kp2aBusinessLogic/Kp2aBusinessLogic.csproj | 2 +- src/Kp2aKeyboardBinding/Kp2aKeyboardBinding.csproj | 2 +- src/PluginSdkBinding/PluginSdkBinding.csproj | 6 +++--- src/SamsungPass | 2 +- src/TwofishCipher/TwofishCipher.csproj | 2 +- src/ZlibAndroid/ZlibAndroid.csproj | 2 +- src/keepass2android/keepass2android.csproj | 6 ++++-- src/netftpandroid | 2 +- 12 files changed, 17 insertions(+), 15 deletions(-) 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/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/keepass2android/keepass2android.csproj b/src/keepass2android/keepass2android.csproj index c9228c12..e95bacda 100644 --- a/src/keepass2android/keepass2android.csproj +++ b/src/keepass2android/keepass2android.csproj @@ -15,7 +15,7 @@ keepass2android OnLoad Properties\AndroidManifest.xml - v6.0 + v8.0 1G true @@ -41,10 +41,12 @@ armeabi,armeabi-v7a,x86 false SdkOnly - False + false False false true + false + false full 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 From 7561afd92d0eb6b82c55a266832070de8b7b1f03 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Tue, 26 Dec 2017 19:48:46 +0100 Subject: [PATCH 09/50] remove support v4 references to avoid incompatible duplicates --- .../AndroidFileChooserBinding.csproj | 3 --- .../JavaFileStorageBindings.csproj | 9 --------- src/SamsungPass | 2 +- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/AndroidFileChooserBinding/AndroidFileChooserBinding.csproj b/src/AndroidFileChooserBinding/AndroidFileChooserBinding.csproj index 3956876e..c0c6d534 100644 --- a/src/AndroidFileChooserBinding/AndroidFileChooserBinding.csproj +++ b/src/AndroidFileChooserBinding/AndroidFileChooserBinding.csproj @@ -47,9 +47,6 @@ - - ..\packages\Xamarin.Android.Support.v4.20.0.0.4\lib\MonoAndroid10\Xamarin.Android.Support.v4.dll - diff --git a/src/JavaFileStorageBindings/JavaFileStorageBindings.csproj b/src/JavaFileStorageBindings/JavaFileStorageBindings.csproj index 7433f4f8..7c7486b5 100644 --- a/src/JavaFileStorageBindings/JavaFileStorageBindings.csproj +++ b/src/JavaFileStorageBindings/JavaFileStorageBindings.csproj @@ -50,15 +50,6 @@ ..\Components\googleplayservices-19.0.0\lib\android\GooglePlayServicesLib.dll - - ..\Components\googleplayservices-19.0.0\lib\android\Xamarin.Android.Support.v4.dll - - - ..\Components\googleplayservices-19.0.0\lib\android\Xamarin.Android.Support.v7.AppCompat.dll - - - ..\Components\googleplayservices-19.0.0\lib\android\Xamarin.Android.Support.v7.MediaRouter.dll - diff --git a/src/SamsungPass b/src/SamsungPass index 53d6fee0..9677c3a7 160000 --- a/src/SamsungPass +++ b/src/SamsungPass @@ -1 +1 @@ -Subproject commit 53d6fee0e227aa10b37649836fb8676d842180a5 +Subproject commit 9677c3a7fef057a8c139b353395edfa982318caf From 1ed1e911894d557befadda3812accf5a9e84c551 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Thu, 28 Dec 2017 03:04:03 +0100 Subject: [PATCH 10/50] first working (but still very rudimentary) version of Oreo Autofill (#9). Not yet implemented and/or tested: partitioning, autofill fields without hints, saving, filling of other fields than username or password, package signature verification, DAL --- .../layout/autofill_service_list_item.xml | 3 +- src/keepass2android/ShareUrlResults.cs | 9 +- src/keepass2android/keepass2android.csproj | 1 + .../AutofillBase/AutofillFieldMetadata.cs | 9 +- .../AutofillFieldMetadataCollection.cs | 4 +- .../services/AutofillBase/AutofillHelper.cs | 44 +----- .../AutofillBase/AutofillHintsHelper.cs | 87 ++++++++++++ .../AutofillBase/AutofillServiceBase.cs | 23 +++- .../ChooseForAutofillActivityBase.cs | 29 ++-- .../services/AutofillBase/CommonUtil.cs | 15 +- .../services/AutofillBase/StructureParser.cs | 11 +- .../AutofillBase/model/FilledAutofillField.cs | 7 +- .../model/FilledAutofillFieldCollection.cs | 128 +++++++++++++++--- .../services/AutofillBase/model/W3cHints.cs | 118 ++++++++-------- .../Kp2aAutofill/ChooseForAutofillActivity.cs | 6 +- .../services/Kp2aAutofillIntentBuilder.cs | 4 +- 16 files changed, 336 insertions(+), 162 deletions(-) create mode 100644 src/keepass2android/services/AutofillBase/AutofillHintsHelper.cs diff --git a/src/keepass2android/Resources/layout/autofill_service_list_item.xml b/src/keepass2android/Resources/layout/autofill_service_list_item.xml index 8d06a671..b238ba41 100644 --- a/src/keepass2android/Resources/layout/autofill_service_list_item.xml +++ b/src/keepass2android/Resources/layout/autofill_service_list_item.xml @@ -16,7 +16,7 @@ (Resource.Id.list_fragment).ListAdapter = new PwGroupListAdapter(this, Group); View selectOtherEntry = FindViewById (Resource.Id.select_other_entry); - selectOtherEntry.Click += (sender, e) => { - GroupActivity.Launch (this, new SelectEntryForUrlTask(url)); + + var newTask = new SelectEntryForUrlTask(url); + if (AppTask is SelectEntryTask currentSelectTask) + newTask.ShowUserNotifications = currentSelectTask.ShowUserNotifications; + + selectOtherEntry.Click += (sender, e) => { + GroupActivity.Launch (this, newTask); }; diff --git a/src/keepass2android/keepass2android.csproj b/src/keepass2android/keepass2android.csproj index adedc61b..c3efbdbd 100644 --- a/src/keepass2android/keepass2android.csproj +++ b/src/keepass2android/keepass2android.csproj @@ -217,6 +217,7 @@ + diff --git a/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs b/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs index 0ba9593c..38f74458 100644 --- a/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs +++ b/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs @@ -28,9 +28,11 @@ namespace keepass2android.services.AutofillBase AutofillType = view.AutofillType; AutofillOptions = view.GetAutofillOptions(); Focused = view.IsFocused; - //TODO port and use AutoFillHints - SetHints(AutofillHelper.FilterForSupportedHints(view.GetAutofillHints())); - } + var supportedHints = AutofillHintsHelper.FilterForSupportedHints(view.GetAutofillHints()); + var storedHints = AutofillHintsHelper.ConvertToStoredHints(supportedHints); + SetHints(storedHints.ToArray()); + + } void SetHints(string[] value) { @@ -58,6 +60,7 @@ namespace keepass2android.services.AutofillBase void UpdateSaveTypeFromHints() { + //TODO future add savetypes for W3cHints SaveType = 0; if (AutofillHints == null) { diff --git a/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs b/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs index 8e38a148..d02434ae 100644 --- a/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs +++ b/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs @@ -40,7 +40,7 @@ namespace keepass2android.services.AutofillBase { if (!AutofillHintsToFieldsMap.ContainsKey(hint)) { - AutofillHintsToFieldsMap.Add(hint, new List()); + AutofillHintsToFieldsMap.Add(hint, new List()); } AutofillHintsToFieldsMap[hint].Add(autofillFieldMetadata); } @@ -51,7 +51,7 @@ namespace keepass2android.services.AutofillBase return AutofillIds.ToArray(); } - public List GetFieldsForHint(String hint) + public List GetFieldsForHint(String hint) { return AutofillHintsToFieldsMap[hint]; } diff --git a/src/keepass2android/services/AutofillBase/AutofillHelper.cs b/src/keepass2android/services/AutofillBase/AutofillHelper.cs index fbd42107..cc15f766 100644 --- a/src/keepass2android/services/AutofillBase/AutofillHelper.cs +++ b/src/keepass2android/services/AutofillBase/AutofillHelper.cs @@ -7,7 +7,6 @@ using Android.Views; using Android.Widget; using FilledAutofillFieldCollection = keepass2android.services.AutofillBase.model.FilledAutofillFieldCollection; -//TODO compare port namespace keepass2android.services.AutofillBase { /// @@ -93,6 +92,7 @@ namespace keepass2android.services.AutofillBase } if (autofillFields.SaveType != 0) { + //TODO implement save var autofillIds = autofillFields.GetAutofillIds(); responseBuilder.SetSaveInfo (new SaveInfo.Builder(autofillFields.SaveType, autofillIds).Build()); @@ -105,47 +105,5 @@ namespace keepass2android.services.AutofillBase } } - 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/AutofillHintsHelper.cs b/src/keepass2android/services/AutofillBase/AutofillHintsHelper.cs new file mode 100644 index 00000000..ea2c8730 --- /dev/null +++ b/src/keepass2android/services/AutofillBase/AutofillHintsHelper.cs @@ -0,0 +1,87 @@ +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.Util; +using Android.Views; +using Android.Widget; +using keepass2android.services.AutofillBase.model; + +namespace keepass2android.services.AutofillBase +{ + class AutofillHintsHelper + { + private static readonly HashSet validHints = new HashSet() + { + View.AutofillHintUsername, + View.AutofillHintPassword, + W3cHints.USERNAME, + W3cHints.CURRENT_PASSWORD, + W3cHints.NEW_PASSWORD + }; + + private static readonly Dictionary hintReplacements= new Dictionary() + { + {W3cHints.EMAIL, View.AutofillHintEmailAddress}, + {W3cHints.USERNAME, View.AutofillHintUsername}, + {W3cHints.CURRENT_PASSWORD, View.AutofillHintPassword}, + {W3cHints.NEW_PASSWORD, View.AutofillHintPassword}, + {W3cHints.CC_EXPIRATION_MONTH, View.AutofillHintCreditCardExpirationMonth }, + {W3cHints.CC_EXPIRATION_YEAR, View.AutofillHintCreditCardExpirationYear }, + {W3cHints.CC_EXPIRATION, View.AutofillHintCreditCardExpirationDate }, + {W3cHints.CC_NUMBER, View.AutofillHintCreditCardNumber }, + {W3cHints.CC_CSC, View.AutofillHintCreditCardSecurityCode }, + {W3cHints.POSTAL_CODE, View.AutofillHintPostalCode }, + + + }; + + public static bool IsValidHint(string hint) + { + return validHints.Contains(hint); + } + + + 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 List ConvertToStoredHints(string[] supportedHints) + { + List result = new List(); + foreach (string hint in supportedHints) + { + string storedHint = hint; + if (hintReplacements.ContainsKey(hint)) + storedHint = hintReplacements[hint]; + result.Add(storedHint); + + } + return result; + + } + } +} \ No newline at end of file diff --git a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs index b6e5c627..7aa5be42 100644 --- a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs +++ b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs @@ -4,6 +4,8 @@ using Android.OS; using Android.Runtime; using Android.Service.Autofill; using Android.Util; +using Android.Views.Autofill; +using Android.Widget; namespace keepass2android.services.AutofillBase { @@ -47,6 +49,7 @@ namespace keepass2android.services.AutofillBase try { query = parser.ParseForFill(); + } catch (Java.Lang.SecurityException e) { @@ -56,20 +59,30 @@ namespace 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) { + var responseBuilder = new FillResponse.Builder(); // If the entire Autofill Response is authenticated, AuthActivity is used // to generate Response. var sender = IntentBuilder.GetAuthIntentSenderForResponse(this, query); - var presentation = keepass2android.services.AutofillBase.AutofillHelper + RemoteViews presentation = keepass2android.services.AutofillBase.AutofillHelper .NewRemoteViews(PackageName, GetString(Resource.String.autofill_sign_in_prompt), Resource.Drawable.ic_launcher); - responseBuilder - .SetAuthentication(autofillIds, sender, presentation); + + var datasetBuilder = new Dataset.Builder(presentation); + + datasetBuilder + .SetAuthentication(sender); + foreach (var autofillId in autofillIds) + { + datasetBuilder.SetValue(autofillId, AutofillValue.ForText("PLACEHOLDER")); + } + + responseBuilder.AddDataset(datasetBuilder.Build()); + callback.OnSuccess(responseBuilder.Build()); } else diff --git a/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs b/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs index de501975..91b9e803 100644 --- a/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs +++ b/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs @@ -22,7 +22,7 @@ namespace keepass2android.services.AutofillBase public static string ExtraQueryString => "EXTRA_QUERY_STRING"; - public int RequestCodeQuery => 663245; + public int RequestCodeQuery => 6245; protected override void OnCreate(Bundle savedInstanceState) { @@ -48,6 +48,11 @@ namespace keepass2android.services.AutofillBase StartActivityForResult(i, RequestCodeQuery); } + protected override void OnStart() + { + base.OnStart(); + } + protected abstract Intent GetQueryIntent(string requestedUrl); protected void RestartApp() @@ -80,21 +85,12 @@ namespace keepass2android.services.AutofillBase protected void OnSuccess(FilledAutofillFieldCollection clientFormDataMap) { var intent = Intent; - var forResponse = intent.GetBooleanExtra(CommonUtil.EXTRA_FOR_RESPONSE, true); AssistStructure structure = (AssistStructure)intent.GetParcelableExtra(AutofillManager.ExtraAssistStructure); StructureParser parser = new StructureParser(this, structure); parser.ParseForFill(); AutofillFieldMetadataCollection autofillFields = parser.AutofillFields; ReplyIntent = new Intent(); - if (forResponse) - { - Dictionary dict = new Dictionary { {clientFormDataMap.DatasetName, clientFormDataMap }}; - SetResponseIntent(AutofillHelper.NewResponse(this, false, autofillFields, dict, IntentBuilder)); - } - else - { - SetDatasetIntent(AutofillHelper.NewDataset(this, autofillFields, clientFormDataMap, false, IntentBuilder)); - } + SetDatasetIntent(AutofillHelper.NewDataset(this, autofillFields, clientFormDataMap, false, IntentBuilder)); } protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) @@ -103,16 +99,21 @@ namespace keepass2android.services.AutofillBase if (requestCode == RequestCodeQuery) { - if (resultCode != Result.Ok) - OnFailure(); - else + if (resultCode == ExpectedActivityResult) OnSuccess(GetDataset(data)); + else + OnFailure(); Finish(); } } + protected virtual Result ExpectedActivityResult + { + get { return Result.Ok; } + } + /// /// Creates the FilledAutofillFieldCollection from the intent returned from the query activity /// diff --git a/src/keepass2android/services/AutofillBase/CommonUtil.cs b/src/keepass2android/services/AutofillBase/CommonUtil.cs index c6305eea..498abbef 100644 --- a/src/keepass2android/services/AutofillBase/CommonUtil.cs +++ b/src/keepass2android/services/AutofillBase/CommonUtil.cs @@ -1,6 +1,7 @@ using System; using System.Text; using Android.OS; +using Android.Util; using Java.Util; namespace keepass2android.services.AutofillBase @@ -9,8 +10,6 @@ namespace keepass2android.services.AutofillBase { 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) { @@ -43,5 +42,17 @@ namespace keepass2android.services.AutofillBase BundleToString(builder, data); return builder.ToString(); } + + public static void logd(string s) + { +#if DEBUG + Log.Debug(Tag, s); +#endif + } + + public static void loge(string s) + { + Kp2aLog.Log(s); + } } } \ No newline at end of file diff --git a/src/keepass2android/services/AutofillBase/StructureParser.cs b/src/keepass2android/services/AutofillBase/StructureParser.cs index 5966db3c..282543ab 100644 --- a/src/keepass2android/services/AutofillBase/StructureParser.cs +++ b/src/keepass2android/services/AutofillBase/StructureParser.cs @@ -52,20 +52,21 @@ namespace keepass2android.services.AutofillBase var view = node.RootViewNode; ParseLocked(forFill, view, ref webDomain); } - if (!string.IsNullOrEmpty(webDomain)) + String packageName = Structure.ActivityComponent.PackageName; + 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}"); + Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, $"Domain {webDomain} is valid for {packageName}"); } else { - Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, "no web domain"); + webDomain = "androidapp://" + packageName; + Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, "no web domain. Using package name."); } return webDomain; } @@ -93,7 +94,7 @@ namespace keepass2android.services.AutofillBase { if (forFill) { - AutofillFields.Add(new keepass2android.services.AutofillBase.AutofillFieldMetadata(viewNode)); + AutofillFields.Add(new AutofillFieldMetadata(viewNode)); } else { diff --git a/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs b/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs index 8af0be3b..7ae92034 100644 --- a/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs +++ b/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs @@ -11,12 +11,13 @@ namespace keepass2android.services.AutofillBase.model public string[] AutofillHints { get; set; } + public FilledAutofillField() {} - public FilledAutofillField(AssistStructure.ViewNode viewNode) + /*public FilledAutofillField(AssistStructure.ViewNode viewNode) { - AutofillHints = AutofillHelper.FilterForSupportedHints(viewNode.GetAutofillHints()); + AutofillHintsHelper = AutofillHelper.FilterForSupportedHints(viewNode.GetAutofillHints()); //TODO port updated FilledAutofillField? AutofillValue autofillValue = viewNode.AutofillValue; @@ -40,7 +41,7 @@ namespace keepass2android.services.AutofillBase.model TextValue = autofillValue.TextValue; } } - } + }*/ public bool IsNull() { diff --git a/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs b/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs index 393874ea..2454d98c 100644 --- a/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs +++ b/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs @@ -32,26 +32,114 @@ namespace keepass2android.services.AutofillBase.model public void Add(FilledAutofillField filledAutofillField) { string[] autofillHints = filledAutofillField.AutofillHints; - //TODO apply W3C transformation - foreach (string hint in autofillHints) - { - HintMap.Add(hint, filledAutofillField); - } + + string nextHint = null; + for (int i = 0; i < autofillHints.Length; i++) + { + string hint = autofillHints[i]; + if (i < autofillHints.Length - 1) + { + nextHint = autofillHints[i + 1]; + } + // First convert the compound W3C autofill hints + if (isW3cSectionPrefix(hint) && i < autofillHints.Length - 1) + { + hint = autofillHints[++i]; + CommonUtil.logd($"Hint is a W3C section prefix; using {hint} instead"); + if (i < autofillHints.Length - 1) + { + nextHint = autofillHints[i + 1]; + } + } + if (isW3cTypePrefix(hint) && nextHint != null && isW3cTypeHint(nextHint)) + { + hint = nextHint; + i++; + CommonUtil.logd($"Hint is a W3C type prefix; using {hint} instead"); + } + if (isW3cAddressType(hint) && nextHint != null) + { + hint = nextHint; + i++; + CommonUtil.logd($"Hint is a W3C address prefix; using {hint} instead"); + } + + // Then check if the "actual" hint is supported. + if (AutofillHintsHelper.IsValidHint(hint)) + { + HintMap.Add(hint, filledAutofillField); + } + else + { + CommonUtil.loge($"Invalid hint: {autofillHints[i]}"); + } + } + } - /// - /// 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, + + private static bool isW3cSectionPrefix(string hint) + { + return hint.StartsWith(W3cHints.PREFIX_SECTION); + } + + private static bool isW3cAddressType(string hint) + { + switch (hint) + { + case W3cHints.SHIPPING: + case W3cHints.BILLING: + return true; + } + return false; + } + + private static bool isW3cTypePrefix(string hint) + { + switch (hint) + { + case W3cHints.PREFIX_WORK: + case W3cHints.PREFIX_FAX: + case W3cHints.PREFIX_HOME: + case W3cHints.PREFIX_PAGER: + return true; + } + return false; + } + + private static bool isW3cTypeHint(string hint) + { + switch (hint) + { + case W3cHints.TEL: + case W3cHints.TEL_COUNTRY_CODE: + case W3cHints.TEL_NATIONAL: + case W3cHints.TEL_AREA_CODE: + case W3cHints.TEL_LOCAL: + case W3cHints.TEL_LOCAL_PREFIX: + case W3cHints.TEL_LOCAL_SUFFIX: + case W3cHints.TEL_EXTENSION: + case W3cHints.EMAIL: + case W3cHints.IMPP: + return true; + } + Log.Warn(CommonUtil.Tag, "Invalid W3C type hint: " + hint); + return false; + } + + /// + /// 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; @@ -66,8 +154,8 @@ namespace keepass2android.services.AutofillBase.model } for (int autofillFieldIndex = 0; autofillFieldIndex < fillableAutofillFields.Count; autofillFieldIndex++) { - keepass2android.services.AutofillBase.model.FilledAutofillField filledAutofillField = HintMap[hint]; - if (filledAutofillField == null) + FilledAutofillField filledAutofillField; + if (!HintMap.TryGetValue(hint, out filledAutofillField) || (filledAutofillField == null)) { continue; } diff --git a/src/keepass2android/services/AutofillBase/model/W3cHints.cs b/src/keepass2android/services/AutofillBase/model/W3cHints.cs index 11941ea1..fb96fad6 100644 --- a/src/keepass2android/services/AutofillBase/model/W3cHints.cs +++ b/src/keepass2android/services/AutofillBase/model/W3cHints.cs @@ -4,68 +4,68 @@ { // Supported W3C autofill tokens (https://html.spec.whatwg.org/multipage/forms.html#autofill) - public static string HONORIFIC_PREFIX = "honorific-prefix"; - public static string NAME = "name"; - public static string GIVEN_NAME = "given-name"; - public static string ADDITIONAL_NAME = "additional-name"; - public static string FAMILY_NAME = "family-name"; - public static string HONORIFIC_SUFFIX = "honorific-suffix"; - public static string USERNAME = "username"; - public static string NEW_PASSWORD = "new-password"; - public static string CURRENT_PASSWORD = "current-password"; - public static string ORGANIZATION_TITLE = "organization-title"; - public static string ORGANIZATION = "organization"; - public static string STREET_ADDRESS = "street-address"; - public static string ADDRESS_LINE1 = "address-line1"; - public static string ADDRESS_LINE2 = "address-line2"; - public static string ADDRESS_LINE3 = "address-line3"; - public static string ADDRESS_LEVEL4 = "address-level4"; - public static string ADDRESS_LEVEL3 = "address-level3"; - public static string ADDRESS_LEVEL2 = "address-level2"; - public static string ADDRESS_LEVEL1 = "address-level1"; - public static string COUNTRY = "country"; - public static string COUNTRY_NAME = "country-name"; - public static string POSTAL_CODE = "postal-code"; - public static string CC_NAME = "cc-name"; - public static string CC_GIVEN_NAME = "cc-given-name"; - public static string CC_ADDITIONAL_NAME = "cc-additional-name"; - public static string CC_FAMILY_NAME = "cc-family-name"; - public static string CC_NUMBER = "cc-number"; - public static string CC_EXPIRATION = "cc-exp"; - public static string CC_EXPIRATION_MONTH = "cc-exp-month"; - public static string CC_EXPIRATION_YEAR = "cc-exp-year"; - public static string CC_CSC = "cc-csc"; - public static string CC_TYPE = "cc-type"; - public static string TRANSACTION_CURRENCY = "transaction-currency"; - public static string TRANSACTION_AMOUNT = "transaction-amount"; - public static string LANGUAGE = "language"; - public static string BDAY = "bday"; - public static string BDAY_DAY = "bday-day"; - public static string BDAY_MONTH = "bday-month"; - public static string BDAY_YEAR = "bday-year"; - public static string SEX = "sex"; - public static string URL = "url"; - public static string PHOTO = "photo"; + public const string HONORIFIC_PREFIX = "honorific-prefix"; + public const string NAME = "name"; + public const string GIVEN_NAME = "given-name"; + public const string ADDITIONAL_NAME = "additional-name"; + public const string FAMILY_NAME = "family-name"; + public const string HONORIFIC_SUFFIX = "honorific-suffix"; + public const string USERNAME = "username"; + public const string NEW_PASSWORD = "new-password"; + public const string CURRENT_PASSWORD = "current-password"; + public const string ORGANIZATION_TITLE = "organization-title"; + public const string ORGANIZATION = "organization"; + public const string STREET_ADDRESS = "street-address"; + public const string ADDRESS_LINE1 = "address-line1"; + public const string ADDRESS_LINE2 = "address-line2"; + public const string ADDRESS_LINE3 = "address-line3"; + public const string ADDRESS_LEVEL4 = "address-level4"; + public const string ADDRESS_LEVEL3 = "address-level3"; + public const string ADDRESS_LEVEL2 = "address-level2"; + public const string ADDRESS_LEVEL1 = "address-level1"; + public const string COUNTRY = "country"; + public const string COUNTRY_NAME = "country-name"; + public const string POSTAL_CODE = "postal-code"; + public const string CC_NAME = "cc-name"; + public const string CC_GIVEN_NAME = "cc-given-name"; + public const string CC_ADDITIONAL_NAME = "cc-additional-name"; + public const string CC_FAMILY_NAME = "cc-family-name"; + public const string CC_NUMBER = "cc-number"; + public const string CC_EXPIRATION = "cc-exp"; + public const string CC_EXPIRATION_MONTH = "cc-exp-month"; + public const string CC_EXPIRATION_YEAR = "cc-exp-year"; + public const string CC_CSC = "cc-csc"; + public const string CC_TYPE = "cc-type"; + public const string TRANSACTION_CURRENCY = "transaction-currency"; + public const string TRANSACTION_AMOUNT = "transaction-amount"; + public const string LANGUAGE = "language"; + public const string BDAY = "bday"; + public const string BDAY_DAY = "bday-day"; + public const string BDAY_MONTH = "bday-month"; + public const string BDAY_YEAR = "bday-year"; + public const string SEX = "sex"; + public const string URL = "url"; + public const string PHOTO = "photo"; // Optional W3C prefixes - public static string PREFIX_SECTION = "section-"; - public static string SHIPPING = "shipping"; - public static string BILLING = "billing"; + public const string PREFIX_SECTION = "section-"; + public const string SHIPPING = "shipping"; + public const string BILLING = "billing"; // W3C prefixes below... - public static string PREFIX_HOME = "home"; - public static string PREFIX_WORK = "work"; - public static string PREFIX_FAX = "fax"; - public static string PREFIX_PAGER = "pager"; + public const string PREFIX_HOME = "home"; + public const string PREFIX_WORK = "work"; + public const string PREFIX_FAX = "fax"; + public const string PREFIX_PAGER = "pager"; // ... require those suffix - public static string TEL = "tel"; - public static string TEL_COUNTRY_CODE = "tel-country-code"; - public static string TEL_NATIONAL = "tel-national"; - public static string TEL_AREA_CODE = "tel-area-code"; - public static string TEL_LOCAL = "tel-local"; - public static string TEL_LOCAL_PREFIX = "tel-local-prefix"; - public static string TEL_LOCAL_SUFFIX = "tel-local-suffix"; - public static string TEL_EXTENSION = "tel_extension"; - public static string EMAIL = "email"; - public static string IMPP = "impp"; + public const string TEL = "tel"; + public const string TEL_COUNTRY_CODE = "tel-country-code"; + public const string TEL_NATIONAL = "tel-national"; + public const string TEL_AREA_CODE = "tel-area-code"; + public const string TEL_LOCAL = "tel-local"; + public const string TEL_LOCAL_PREFIX = "tel-local-prefix"; + public const string TEL_LOCAL_SUFFIX = "tel-local-suffix"; + public const string TEL_EXTENSION = "tel_extension"; + public const string EMAIL = "email"; + public const string IMPP = "impp"; private W3cHints() { diff --git a/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs b/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs index 143a884f..65a758d7 100644 --- a/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs +++ b/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs @@ -34,6 +34,8 @@ namespace keepass2android.services.Kp2aAutofill return i; } + protected override Result ExpectedActivityResult => KeePass.ExitCloseAfterTaskComplete; + protected override FilledAutofillFieldCollection GetDataset(Intent data) { if (!App.Kp2a.GetDb().Loaded || (App.Kp2a.QuickLocked)) @@ -45,13 +47,13 @@ namespace keepass2android.services.Kp2aAutofill FilledAutofillField pwdField = new FilledAutofillField { - AutofillHints = new[] {W3cHints.NAME, W3cHints.EMAIL}, + AutofillHints = new[] {View.AutofillHintPassword}, TextValue = password }; FilledAutofillField userField = new FilledAutofillField { - AutofillHints = new[] {W3cHints.NEW_PASSWORD, W3cHints.CURRENT_PASSWORD}, + AutofillHints = new[] {View.AutofillHintUsername}, TextValue = username }; diff --git a/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs b/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs index d5c10b9c..010e5f4a 100644 --- a/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs +++ b/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs @@ -9,6 +9,7 @@ using Android.Runtime; using Android.Views; using Android.Widget; using keepass2android.services.AutofillBase; +using keepass2android.services.Kp2aAutofill; namespace keepass2android.services { @@ -17,7 +18,8 @@ namespace keepass2android.services public IntentSender GetAuthIntentSenderForResponse(Context context, string query) { - Intent intent = new Intent(context, typeof(KeePass)); + Intent intent = new Intent(context, typeof(ChooseForAutofillActivity)); + intent.PutExtra(ChooseForAutofillActivityBase.ExtraQueryString, query); return PendingIntent.GetActivity(context, 0, intent, PendingIntentFlags.CancelCurrent).IntentSender; } From 6e13320f366b1a02263d60b8b186e535f0a0e116 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Thu, 28 Dec 2017 03:20:50 +0100 Subject: [PATCH 11/50] update build tools versions --- .gitignore | 3 +++ src/java/JavaFileStorage/app/build.gradle | 4 ++-- src/java/JavaFileStorageTest-AS/app/build.gradle | 4 ++-- src/java/KP2ASoftkeyboard_AS/app/build.gradle | 2 +- src/java/android-filechooser-AS/app/build.gradle | 6 +++--- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 42b39152..fd69d363 100644 --- a/.gitignore +++ b/.gitignore @@ -154,3 +154,6 @@ intermediates /src/java/Kp2aAccServiceLib/gradle adbprompt.ps1 /src/java/KP2ASoftkeyboard_AS/build/android-profile/*.rawproto +src/java/KP2ASoftkeyboard_AS/build/generated/mockable-android-23.jar +*.rawproto +src/java/Keepass2AndroidPluginSDK2/build/generated/mockable-Google-Inc.-Google-APIs-23.jar diff --git a/src/java/JavaFileStorage/app/build.gradle b/src/java/JavaFileStorage/app/build.gradle index 547c20c2..88ed1df7 100644 --- a/src/java/JavaFileStorage/app/build.gradle +++ b/src/java/JavaFileStorage/app/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 25 - buildToolsVersion '25.0.3' + compileSdkVersion 26 + buildToolsVersion '26.0.2' defaultConfig { minSdkVersion 15 targetSdkVersion 23 diff --git a/src/java/JavaFileStorageTest-AS/app/build.gradle b/src/java/JavaFileStorageTest-AS/app/build.gradle index 2531f272..2d8e6d03 100644 --- a/src/java/JavaFileStorageTest-AS/app/build.gradle +++ b/src/java/JavaFileStorageTest-AS/app/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 25 - buildToolsVersion '25.0.3' + compileSdkVersion 26 + buildToolsVersion '26.0.2' defaultConfig { applicationId "com.crocoapps.javafilestoragetest" diff --git a/src/java/KP2ASoftkeyboard_AS/app/build.gradle b/src/java/KP2ASoftkeyboard_AS/app/build.gradle index d7664c41..b5c94888 100644 --- a/src/java/KP2ASoftkeyboard_AS/app/build.gradle +++ b/src/java/KP2ASoftkeyboard_AS/app/build.gradle @@ -1,7 +1,7 @@ apply plugin: 'com.android.library' android { compileSdkVersion 23 - buildToolsVersion '25.0.3' + buildToolsVersion '26.0.2' defaultConfig { minSdkVersion 18 diff --git a/src/java/android-filechooser-AS/app/build.gradle b/src/java/android-filechooser-AS/app/build.gradle index 5dc632e4..2b327ed9 100644 --- a/src/java/android-filechooser-AS/app/build.gradle +++ b/src/java/android-filechooser-AS/app/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 25 - buildToolsVersion '25.0.3' + compileSdkVersion 26 + buildToolsVersion '26.0.2' defaultConfig { minSdkVersion 15 @@ -23,5 +23,5 @@ android { } dependencies { - compile 'com.android.support:support-v4:23.0.0' + compile 'com.android.support:support-v4:26.1.0' } From 9bab31514e52a8a2bd4b609d97dc4588790c0a4a Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Thu, 28 Dec 2017 03:22:32 +0100 Subject: [PATCH 12/50] add google Maven repo to build.gradle --- src/java/JavaFileStorageTest-AS/build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/java/JavaFileStorageTest-AS/build.gradle b/src/java/JavaFileStorageTest-AS/build.gradle index 2189cb68..0451ca17 100644 --- a/src/java/JavaFileStorageTest-AS/build.gradle +++ b/src/java/JavaFileStorageTest-AS/build.gradle @@ -3,6 +3,10 @@ buildscript { repositories { jcenter() + maven { + url 'https://maven.google.com/' + name 'Google' + } } dependencies { classpath 'com.android.tools.build:gradle:3.0.1' @@ -17,6 +21,7 @@ buildscript { allprojects { repositories { jcenter() + google() } } From 45cadb9cfbdb83b2eb3461ab5cce470adfd2a0c1 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Thu, 28 Dec 2017 09:31:53 +0100 Subject: [PATCH 13/50] update gson to avoid xpp3 dependency, should fix gradle build errors --- src/java/JavaFileStorage/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java/JavaFileStorage/app/build.gradle b/src/java/JavaFileStorage/app/build.gradle index 88ed1df7..ba1bbfcf 100644 --- a/src/java/JavaFileStorage/app/build.gradle +++ b/src/java/JavaFileStorage/app/build.gradle @@ -25,7 +25,7 @@ dependencies { compile 'com.squareup.okhttp3:okhttp:3.9.0' compile 'com.burgstaller:okhttp-digest:1.7' compile 'com.google.android.gms:play-services:4.0.30' - compile 'com.google.http-client:google-http-client-gson:1.16.0-rc' + compile 'com.google.http-client:google-http-client-gson:1.20.0' compile('com.google.api-client:google-api-client-android:1.16.0-rc') { exclude group: 'com.google.android.google-play-services' } From eaf9447abc7d70dd7d7c1bd17b17b64c41b3175f Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Thu, 28 Dec 2017 09:43:48 +0100 Subject: [PATCH 14/50] adjust filename of aar --- src/PluginSdkBinding/PluginSdkBinding.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PluginSdkBinding/PluginSdkBinding.csproj b/src/PluginSdkBinding/PluginSdkBinding.csproj index ad38386a..222df093 100644 --- a/src/PluginSdkBinding/PluginSdkBinding.csproj +++ b/src/PluginSdkBinding/PluginSdkBinding.csproj @@ -53,8 +53,8 @@ - - Jars\Keepass2AndroidPluginSDK2-debug.aar + + Jars\app-release.aar From 7436ac0b5ef1b0ff66e9648c0b7ad08b0565ef2e Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Thu, 28 Dec 2017 10:16:37 +0100 Subject: [PATCH 15/50] add support v4 package --- src/keepass2android/packages.config | 1 + 1 file changed, 1 insertion(+) diff --git a/src/keepass2android/packages.config b/src/keepass2android/packages.config index 2e6476f6..faacc1ec 100644 --- a/src/keepass2android/packages.config +++ b/src/keepass2android/packages.config @@ -12,6 +12,7 @@ + From de8e363d3339fa6c815f22f8931c7193130507e3 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Thu, 28 Dec 2017 14:57:21 +0100 Subject: [PATCH 16/50] remove support v4 from all packages.config --- src/AndroidFileChooserBinding/packages.config | 2 +- src/keepass2android/packages.config | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/AndroidFileChooserBinding/packages.config b/src/AndroidFileChooserBinding/packages.config index 8cdf15fa..1508affa 100644 --- a/src/AndroidFileChooserBinding/packages.config +++ b/src/AndroidFileChooserBinding/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/keepass2android/packages.config b/src/keepass2android/packages.config index faacc1ec..2e6476f6 100644 --- a/src/keepass2android/packages.config +++ b/src/keepass2android/packages.config @@ -12,7 +12,6 @@ - From 1a2885408dde16daba57cfa300ede5176cac4471 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Thu, 28 Dec 2017 14:58:30 +0100 Subject: [PATCH 17/50] remove support v4 --- src/SamsungPass | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SamsungPass b/src/SamsungPass index 9677c3a7..c9dd9b21 160000 --- a/src/SamsungPass +++ b/src/SamsungPass @@ -1 +1 @@ -Subproject commit 9677c3a7fef057a8c139b353395edfa982318caf +Subproject commit c9dd9b21a59eb7d1761ea99ba6a34ac8c5bdbf42 From 4c0cfb77fb2c903ec79597e672792aaa76e98b2b Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Thu, 28 Dec 2017 21:31:59 +0100 Subject: [PATCH 18/50] once again, update support packages, now to 26 versions --- src/keepass2android/keepass2android.csproj | 100 +++++++++++++-------- src/keepass2android/packages.config | 33 ++++--- 2 files changed, 81 insertions(+), 52 deletions(-) diff --git a/src/keepass2android/keepass2android.csproj b/src/keepass2android/keepass2android.csproj index c3efbdbd..ee598bb5 100644 --- a/src/keepass2android/keepass2android.csproj +++ b/src/keepass2android/keepass2android.csproj @@ -105,42 +105,56 @@ + + ..\packages\Xamarin.Android.Arch.Core.Common.1.0.0\lib\MonoAndroid80\Xamarin.Android.Arch.Core.Common.dll + + + ..\packages\Xamarin.Android.Arch.Lifecycle.Common.1.0.1\lib\MonoAndroid80\Xamarin.Android.Arch.Lifecycle.Common.dll + + + ..\packages\Xamarin.Android.Arch.Lifecycle.Runtime.1.0.0\lib\MonoAndroid80\Xamarin.Android.Arch.Lifecycle.Runtime.dll + - ..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Animated.Vector.Drawable.dll + ..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Animated.Vector.Drawable.dll + + + ..\packages\Xamarin.Android.Support.Annotations.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Annotations.dll - ..\packages\Xamarin.Android.Support.Compat.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Compat.dll + ..\packages\Xamarin.Android.Support.Compat.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Compat.dll - ..\packages\Xamarin.Android.Support.Core.UI.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Core.UI.dll + ..\packages\Xamarin.Android.Support.Core.UI.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Core.UI.dll - ..\packages\Xamarin.Android.Support.Core.Utils.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Core.Utils.dll + ..\packages\Xamarin.Android.Support.Core.Utils.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Core.Utils.dll - ..\packages\Xamarin.Android.Support.Design.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Design.dll + ..\packages\Xamarin.Android.Support.Design.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Design.dll - ..\packages\Xamarin.Android.Support.Fragment.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Fragment.dll + ..\packages\Xamarin.Android.Support.Fragment.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Fragment.dll - ..\packages\Xamarin.Android.Support.Media.Compat.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Media.Compat.dll + ..\packages\Xamarin.Android.Support.Media.Compat.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Media.Compat.dll - ..\packages\Xamarin.Android.Support.Transition.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Transition.dll + ..\packages\Xamarin.Android.Support.Transition.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Transition.dll - - ..\packages\Xamarin.Android.Support.v13.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.v13.dll - True + + ..\packages\Xamarin.Android.Support.v13.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.v13.dll + + + ..\packages\Xamarin.Android.Support.v4.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.v4.dll - ..\packages\Xamarin.Android.Support.v7.AppCompat.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.dll + ..\packages\Xamarin.Android.Support.v7.AppCompat.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.v7.AppCompat.dll - ..\packages\Xamarin.Android.Support.v7.RecyclerView.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.v7.RecyclerView.dll + ..\packages\Xamarin.Android.Support.v7.RecyclerView.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.v7.RecyclerView.dll - ..\packages\Xamarin.Android.Support.Vector.Drawable.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Vector.Drawable.dll + ..\packages\Xamarin.Android.Support.Vector.Drawable.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Vector.Drawable.dll ..\packages\Xamarin.GooglePlayServices.Base.27.0.0.0\lib\MonoAndroid41\Xamarin.GooglePlayServices.Base.dll @@ -1793,30 +1807,40 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/keepass2android/packages.config b/src/keepass2android/packages.config index 2e6476f6..fefffd38 100644 --- a/src/keepass2android/packages.config +++ b/src/keepass2android/packages.config @@ -3,20 +3,25 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - + + \ No newline at end of file From 0697f4a96459d2b051768141bb6a54fa57d8065e Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Thu, 28 Dec 2017 21:53:03 +0100 Subject: [PATCH 19/50] minor refactoring and code formatting --- .../services/AutofillBase/AutofillServiceBase.cs | 12 ++++-------- .../AutofillBase/ChooseForAutofillActivityBase.cs | 5 ----- .../model/FilledAutofillFieldCollection.cs | 7 +++---- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs index 7aa5be42..bc71915f 100644 --- a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs +++ b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs @@ -65,17 +65,13 @@ namespace keepass2android.services.AutofillBase if (responseAuth && autofillIds.Length != 0) { var responseBuilder = new FillResponse.Builder(); - // If the entire Autofill Response is authenticated, AuthActivity is used - // to generate Response. + var sender = IntentBuilder.GetAuthIntentSenderForResponse(this, query); - RemoteViews presentation = keepass2android.services.AutofillBase.AutofillHelper - .NewRemoteViews(PackageName, GetString(Resource.String.autofill_sign_in_prompt), - Resource.Drawable.ic_launcher); + RemoteViews presentation = AutofillHelper.NewRemoteViews(PackageName, GetString(Resource.String.autofill_sign_in_prompt),Resource.Drawable.ic_launcher); var datasetBuilder = new Dataset.Builder(presentation); - - datasetBuilder - .SetAuthentication(sender); + datasetBuilder.SetAuthentication(sender); + //need to add placeholders so we can directly fill after ChooseActivity foreach (var autofillId in autofillIds) { datasetBuilder.SetValue(autofillId, AutofillValue.ForText("PLACEHOLDER")); diff --git a/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs b/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs index 91b9e803..2c6dfe1b 100644 --- a/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs +++ b/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs @@ -48,11 +48,6 @@ namespace keepass2android.services.AutofillBase StartActivityForResult(i, RequestCodeQuery); } - protected override void OnStart() - { - base.OnStart(); - } - protected abstract Intent GetQueryIntent(string requestedUrl); protected void RestartApp() diff --git a/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs b/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs index 2454d98c..9be62df6 100644 --- a/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs +++ b/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs @@ -139,8 +139,7 @@ namespace keepass2android.services.AutofillBase.model /// true, if to fields was applyed, false otherwise. /// Autofill field metadata collection. /// Dataset builder. - public bool ApplyToFields(AutofillFieldMetadataCollection autofillFieldMetadataCollection, - Dataset.Builder datasetBuilder) + public bool ApplyToFields(AutofillFieldMetadataCollection autofillFieldMetadataCollection, Dataset.Builder datasetBuilder) { bool setValueAtLeastOnce = false; List allHints = autofillFieldMetadataCollection.AllAutofillHints; @@ -152,14 +151,14 @@ namespace keepass2android.services.AutofillBase.model { continue; } - for (int autofillFieldIndex = 0; autofillFieldIndex < fillableAutofillFields.Count; autofillFieldIndex++) + foreach (AutofillFieldMetadata autofillFieldMetadata in fillableAutofillFields) { FilledAutofillField filledAutofillField; if (!HintMap.TryGetValue(hint, out filledAutofillField) || (filledAutofillField == null)) { continue; } - AutofillFieldMetadata autofillFieldMetadata = fillableAutofillFields[autofillFieldIndex]; + var autofillId = autofillFieldMetadata.AutofillId; var autofillType = autofillFieldMetadata.AutofillType; switch (autofillType) From fb018946b993186c0e21d85f6714f79e4221487c Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Thu, 28 Dec 2017 21:59:55 +0100 Subject: [PATCH 20/50] minor refactoring --- .../AutofillBase/AutofillFieldMetadataCollection.cs | 2 +- .../services/AutofillBase/AutofillHelper.cs | 2 +- .../services/AutofillBase/StructureParser.cs | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs b/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs index d02434ae..d34fe3f7 100644 --- a/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs +++ b/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs @@ -12,7 +12,7 @@ namespace keepass2android.services.AutofillBase public class AutofillFieldMetadataCollection { List AutofillIds = new List(); - Dictionary> AutofillHintsToFieldsMap = new Dictionary>(); + Dictionary> AutofillHintsToFieldsMap = new Dictionary>(); public List AllAutofillHints { get; } public List FocusedAutofillHints { get; } int Size = 0; diff --git a/src/keepass2android/services/AutofillBase/AutofillHelper.cs b/src/keepass2android/services/AutofillBase/AutofillHelper.cs index cc15f766..d8f83f73 100644 --- a/src/keepass2android/services/AutofillBase/AutofillHelper.cs +++ b/src/keepass2android/services/AutofillBase/AutofillHelper.cs @@ -24,7 +24,7 @@ namespace keepass2android.services.AutofillBase /// 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) + AutofillFieldMetadataCollection autofillFields, FilledAutofillFieldCollection filledAutofillFieldCollection, bool datasetAuth, IAutofillIntentBuilder intentBuilder) { var datasetName = filledAutofillFieldCollection.DatasetName; if (datasetName != null) diff --git a/src/keepass2android/services/AutofillBase/StructureParser.cs b/src/keepass2android/services/AutofillBase/StructureParser.cs index 282543ab..6c0f751d 100644 --- a/src/keepass2android/services/AutofillBase/StructureParser.cs +++ b/src/keepass2android/services/AutofillBase/StructureParser.cs @@ -14,7 +14,7 @@ namespace keepass2android.services.AutofillBase public sealed class StructureParser { public Context mContext { get; } - public keepass2android.services.AutofillBase.AutofillFieldMetadataCollection AutofillFields { get; set; } + public AutofillFieldMetadataCollection AutofillFields { get; set; } AssistStructure Structure; public FilledAutofillFieldCollection ClientFormData { get; set; } @@ -22,7 +22,7 @@ namespace keepass2android.services.AutofillBase { mContext = context; Structure = structure; - AutofillFields = new keepass2android.services.AutofillBase.AutofillFieldMetadataCollection(); + AutofillFields = new AutofillFieldMetadataCollection(); } public string ParseForFill() @@ -42,7 +42,7 @@ namespace keepass2android.services.AutofillBase /// If set to true for fill. string Parse(bool forFill) { - Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, "Parsing structure for " + Structure.ActivityComponent); + Log.Debug(CommonUtil.Tag, "Parsing structure for " + Structure.ActivityComponent); var nodes = Structure.WindowNodeCount; ClientFormData = new FilledAutofillFieldCollection(); String webDomain = null; @@ -61,12 +61,12 @@ namespace keepass2android.services.AutofillBase 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}"); + Log.Debug(CommonUtil.Tag, $"Domain {webDomain} is valid for {packageName}"); } else { webDomain = "androidapp://" + packageName; - Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, "no web domain. Using package name."); + Log.Debug(CommonUtil.Tag, "no web domain. Using package name."); } return webDomain; } @@ -76,7 +76,7 @@ namespace keepass2android.services.AutofillBase String webDomain = viewNode.WebDomain; if (webDomain != null) { - Log.Debug(keepass2android.services.AutofillBase.CommonUtil.Tag, $"child web domain: {webDomain}"); + Log.Debug(CommonUtil.Tag, $"child web domain: {webDomain}"); if (!string.IsNullOrEmpty(validWebdomain)) { if (webDomain == validWebdomain) From c8d56a237b3cb357ab34208298ec0c08435bdf8a Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Fri, 29 Dec 2017 07:07:04 +0100 Subject: [PATCH 21/50] improve implementation of Oreo autofill (#9), now supporting all Android/W3cHints, using all Keepass fields (if hints match field name). Make hint comparison code clearer and always compare case insensitive --- .../AutofillBase/AutofillFieldMetadata.cs | 14 +- .../AutofillFieldMetadataCollection.cs | 38 ++++-- .../AutofillBase/AutofillHintsHelper.cs | 88 +++++++++++-- .../AutofillBase/model/FilledAutofillField.cs | 88 +++++++++++-- .../model/FilledAutofillFieldCollection.cs | 120 +++--------------- .../services/AutofillBase/model/W3cHints.cs | 55 +++++++- .../Kp2aAutofill/ChooseForAutofillActivity.cs | 52 +++++--- 7 files changed, 289 insertions(+), 166 deletions(-) diff --git a/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs b/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs index 38f74458..965af5fe 100644 --- a/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs +++ b/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs @@ -14,8 +14,8 @@ namespace keepass2android.services.AutofillBase public class AutofillFieldMetadata { public SaveDataType SaveType { get; set; } - - public string[] AutofillHints { get; set; } + + public string[] AutofillCanonicalHints { get; set; } public AutofillId AutofillId { get; } public AutofillType AutofillType { get; } @@ -29,14 +29,14 @@ namespace keepass2android.services.AutofillBase AutofillOptions = view.GetAutofillOptions(); Focused = view.IsFocused; var supportedHints = AutofillHintsHelper.FilterForSupportedHints(view.GetAutofillHints()); - var storedHints = AutofillHintsHelper.ConvertToStoredHints(supportedHints); - SetHints(storedHints.ToArray()); + var canonicalHints = AutofillHintsHelper.ConvertToCanonicalHints(supportedHints); + SetHints(canonicalHints.ToArray()); } void SetHints(string[] value) { - AutofillHints = value; + AutofillCanonicalHints = value; UpdateSaveTypeFromHints(); } @@ -62,11 +62,11 @@ namespace keepass2android.services.AutofillBase { //TODO future add savetypes for W3cHints SaveType = 0; - if (AutofillHints == null) + if (AutofillCanonicalHints == null) { return; } - foreach (var hint in AutofillHints) + foreach (var hint in AutofillCanonicalHints) { switch (hint) { diff --git a/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs b/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs index d34fe3f7..94a3db79 100644 --- a/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs +++ b/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs @@ -12,17 +12,19 @@ namespace keepass2android.services.AutofillBase public class AutofillFieldMetadataCollection { List AutofillIds = new List(); - Dictionary> AutofillHintsToFieldsMap = new Dictionary>(); - public List AllAutofillHints { get; } - public List FocusedAutofillHints { get; } + + Dictionary> AutofillCanonicalHintsToFieldsMap = new Dictionary>(); + + public List AllAutofillCanonicalHints { get; } + public List FocusedAutofillCanonicalHints { get; } int Size = 0; public SaveDataType SaveType { get; set; } public AutofillFieldMetadataCollection() { SaveType = 0; - FocusedAutofillHints = new List(); - AllAutofillHints = new List(); + FocusedAutofillCanonicalHints = new List(); + AllAutofillCanonicalHints = new List(); } public void Add(AutofillFieldMetadata autofillFieldMetadata) @@ -30,19 +32,19 @@ namespace keepass2android.services.AutofillBase SaveType |= autofillFieldMetadata.SaveType; Size++; AutofillIds.Add(autofillFieldMetadata.AutofillId); - var hintsList = autofillFieldMetadata.AutofillHints; - AllAutofillHints.AddRange(hintsList); + var hintsList = autofillFieldMetadata.AutofillCanonicalHints; + AllAutofillCanonicalHints.AddRange(hintsList); if (autofillFieldMetadata.Focused) { - FocusedAutofillHints.AddRange(hintsList); + FocusedAutofillCanonicalHints.AddRange(hintsList); } - foreach (var hint in autofillFieldMetadata.AutofillHints) + foreach (var hint in autofillFieldMetadata.AutofillCanonicalHints) { - if (!AutofillHintsToFieldsMap.ContainsKey(hint)) + if (!AutofillCanonicalHintsToFieldsMap.ContainsKey(hint)) { - AutofillHintsToFieldsMap.Add(hint, new List()); + AutofillCanonicalHintsToFieldsMap.Add(hint, new List()); } - AutofillHintsToFieldsMap[hint].Add(autofillFieldMetadata); + AutofillCanonicalHintsToFieldsMap[hint].Add(autofillFieldMetadata); } } @@ -51,9 +53,17 @@ namespace keepass2android.services.AutofillBase return AutofillIds.ToArray(); } - public List GetFieldsForHint(String hint) + /// + /// returns the fields for the given hint or an empty list. + /// + public List GetFieldsForHint(String canonicalHint) { - return AutofillHintsToFieldsMap[hint]; + List result; + if (!AutofillCanonicalHintsToFieldsMap.TryGetValue(canonicalHint, out result)) + { + result = new List(); + } + return result; } diff --git a/src/keepass2android/services/AutofillBase/AutofillHintsHelper.cs b/src/keepass2android/services/AutofillBase/AutofillHintsHelper.cs index ea2c8730..cc8ad0a0 100644 --- a/src/keepass2android/services/AutofillBase/AutofillHintsHelper.cs +++ b/src/keepass2android/services/AutofillBase/AutofillHintsHelper.cs @@ -16,16 +16,76 @@ namespace keepass2android.services.AutofillBase { class AutofillHintsHelper { - private static readonly HashSet validHints = new HashSet() + private static readonly HashSet _allSupportedHints = new HashSet(StringComparer.OrdinalIgnoreCase) { - View.AutofillHintUsername, + View.AutofillHintCreditCardExpirationDate, + View.AutofillHintCreditCardExpirationDay, + View.AutofillHintCreditCardExpirationMonth, + View.AutofillHintCreditCardExpirationYear, + View.AutofillHintCreditCardNumber, + View.AutofillHintCreditCardSecurityCode, + View.AutofillHintEmailAddress, + View.AutofillHintPhone, + View.AutofillHintName, View.AutofillHintPassword, + View.AutofillHintPostalAddress, + View.AutofillHintPostalCode, + View.AutofillHintUsername, + W3cHints.HONORIFIC_PREFIX, + W3cHints.NAME, + W3cHints.GIVEN_NAME, + W3cHints.ADDITIONAL_NAME, + W3cHints.FAMILY_NAME, + W3cHints.HONORIFIC_SUFFIX, W3cHints.USERNAME, + W3cHints.NEW_PASSWORD, W3cHints.CURRENT_PASSWORD, - W3cHints.NEW_PASSWORD + W3cHints.ORGANIZATION_TITLE, + W3cHints.ORGANIZATION, + W3cHints.STREET_ADDRESS, + W3cHints.ADDRESS_LINE1, + W3cHints.ADDRESS_LINE2, + W3cHints.ADDRESS_LINE3, + W3cHints.ADDRESS_LEVEL4, + W3cHints.ADDRESS_LEVEL3, + W3cHints.ADDRESS_LEVEL2, + W3cHints.ADDRESS_LEVEL1, + W3cHints.COUNTRY, + W3cHints.COUNTRY_NAME, + W3cHints.POSTAL_CODE, + W3cHints.CC_NAME, + W3cHints.CC_GIVEN_NAME, + W3cHints.CC_ADDITIONAL_NAME, + W3cHints.CC_FAMILY_NAME, + W3cHints.CC_NUMBER, + W3cHints.CC_EXPIRATION, + W3cHints.CC_EXPIRATION_MONTH, + W3cHints.CC_EXPIRATION_YEAR, + W3cHints.CC_CSC, + W3cHints.CC_TYPE, + W3cHints.TRANSACTION_CURRENCY, + W3cHints.TRANSACTION_AMOUNT, + W3cHints.LANGUAGE, + W3cHints.BDAY, + W3cHints.BDAY_DAY, + W3cHints.BDAY_MONTH, + W3cHints.BDAY_YEAR, + W3cHints.SEX, + W3cHints.URL, + W3cHints.PHOTO, + W3cHints.TEL, + W3cHints.TEL_COUNTRY_CODE, + W3cHints.TEL_NATIONAL, + W3cHints.TEL_AREA_CODE, + W3cHints.TEL_LOCAL, + W3cHints.TEL_LOCAL_PREFIX, + W3cHints.TEL_LOCAL_SUFFIX, + W3cHints.TEL_EXTENSION, + W3cHints.EMAIL, + W3cHints.IMPP, }; - private static readonly Dictionary hintReplacements= new Dictionary() + private static readonly Dictionary hintToCanonicalReplacement= new Dictionary(StringComparer.OrdinalIgnoreCase) { {W3cHints.EMAIL, View.AutofillHintEmailAddress}, {W3cHints.USERNAME, View.AutofillHintUsername}, @@ -41,9 +101,9 @@ namespace keepass2android.services.AutofillBase }; - public static bool IsValidHint(string hint) + public static bool IsSupportedHint(string hint) { - return validHints.Contains(hint); + return _allSupportedHints.Contains(hint); } @@ -53,7 +113,7 @@ namespace keepass2android.services.AutofillBase int i = 0; foreach (var hint in hints) { - if (IsValidHint(hint)) + if (IsSupportedHint(hint)) { filteredHints[i++] = hint; } @@ -69,16 +129,18 @@ namespace keepass2android.services.AutofillBase - public static List ConvertToStoredHints(string[] supportedHints) + /// + /// transforms hints by replacing some W3cHints by their Android counterparts and transforming everything to lowercase + /// + public static List ConvertToCanonicalHints(string[] supportedHints) { List result = new List(); foreach (string hint in supportedHints) { - string storedHint = hint; - if (hintReplacements.ContainsKey(hint)) - storedHint = hintReplacements[hint]; - result.Add(storedHint); - + string canonicalHint; + if (!hintToCanonicalReplacement.TryGetValue(hint, out canonicalHint)) + canonicalHint = hint; + result.Add(canonicalHint.ToLower()); } return result; diff --git a/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs b/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs index 7ae92034..3afe0495 100644 --- a/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs +++ b/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs @@ -1,25 +1,89 @@ -using Android.App.Assist; +using System.Collections.Generic; +using Android.App.Assist; using Android.Views.Autofill; namespace keepass2android.services.AutofillBase.model { public class FilledAutofillField { - public string TextValue { get; set; } + private string[] _autofillHints; + public string TextValue { get; set; } public long? DateValue { get; set; } public bool? ToggleValue { get; set; } - - public string[] AutofillHints { get; set; } - - public FilledAutofillField() + /// + /// returns the autofill hints for the filled field. These are always lowercased for simpler string comparison. + /// + public string[] AutofillHints + { + get + { + return _autofillHints; + } + set + { + _autofillHints = value; + for (int i = 0; i < _autofillHints.Length; i++) + _autofillHints[i] = _autofillHints[i].ToLower(); + } + } + + public bool Protected { get; set; } + + + public FilledAutofillField() {} + + public FilledAutofillField(AssistStructure.ViewNode viewNode) + { + + string[] rawHints = AutofillHintsHelper.FilterForSupportedHints(viewNode.GetAutofillHints()); + List hintList = new List(); + + string nextHint = null; + for (int i = 0; i < rawHints.Length; i++) + { + string hint = rawHints[i]; + if (i < rawHints.Length - 1) + { + nextHint = rawHints[i + 1]; + } + // First convert the compound W3C autofill hints + if (W3cHints.isW3cSectionPrefix(hint) && i < rawHints.Length - 1) + { + hint = rawHints[++i]; + CommonUtil.logd($"Hint is a W3C section prefix; using {hint} instead"); + if (i < rawHints.Length - 1) + { + nextHint = rawHints[i + 1]; + } + } + if (W3cHints.isW3cTypePrefix(hint) && nextHint != null && W3cHints.isW3cTypeHint(nextHint)) + { + hint = nextHint; + i++; + CommonUtil.logd($"Hint is a W3C type prefix; using {hint} instead"); + } + if (W3cHints.isW3cAddressType(hint) && nextHint != null) + { + hint = nextHint; + i++; + CommonUtil.logd($"Hint is a W3C address prefix; using {hint} instead"); + } - /*public FilledAutofillField(AssistStructure.ViewNode viewNode) - { - AutofillHintsHelper = AutofillHelper.FilterForSupportedHints(viewNode.GetAutofillHints()); + // Then check if the "actual" hint is supported. + if (AutofillHintsHelper.IsSupportedHint(hint)) + { + hintList.Add(hint); + } + else + { + CommonUtil.loge($"Invalid hint: {rawHints[i]}"); + } + } + AutofillHints = hintList.ToArray(); - //TODO port updated FilledAutofillField? + //TODO port updated FilledAutofillField AutofillValue autofillValue = viewNode.AutofillValue; if (autofillValue != null) { @@ -41,9 +105,9 @@ namespace keepass2android.services.AutofillBase.model TextValue = autofillValue.TextValue; } } - }*/ + } - public bool IsNull() + public bool IsNull() { return TextValue == null && DateValue == null && ToggleValue == null; } diff --git a/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs b/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs index 9be62df6..9a2391f5 100644 --- a/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs +++ b/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Android.Service.Autofill; using Android.Util; using Android.Views; @@ -12,121 +13,48 @@ namespace keepass2android.services.AutofillBase.model /// public class FilledAutofillFieldCollection { - public Dictionary HintMap { get; set; } + public Dictionary HintMap { get; } public string DatasetName { get; set; } public FilledAutofillFieldCollection(Dictionary hintMap, string datasetName = "") { - HintMap = hintMap; + //recreate hint map making sure we compare case insensitive + HintMap = BuildHintMap(); + foreach (var p in hintMap) + HintMap.Add(p.Key, p.Value); DatasetName = datasetName; } - public FilledAutofillFieldCollection() : this(new Dictionary()) + public FilledAutofillFieldCollection() : this(BuildHintMap()) {} - /// + private static Dictionary BuildHintMap() + { + return new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + /// /// Adds a filledAutofillField to the collection, indexed by all of its hints. /// /// The add. /// Filled autofill field. public void Add(FilledAutofillField filledAutofillField) { - string[] autofillHints = filledAutofillField.AutofillHints; - - string nextHint = null; - for (int i = 0; i < autofillHints.Length; i++) - { - string hint = autofillHints[i]; - if (i < autofillHints.Length - 1) - { - nextHint = autofillHints[i + 1]; - } - // First convert the compound W3C autofill hints - if (isW3cSectionPrefix(hint) && i < autofillHints.Length - 1) - { - hint = autofillHints[++i]; - CommonUtil.logd($"Hint is a W3C section prefix; using {hint} instead"); - if (i < autofillHints.Length - 1) - { - nextHint = autofillHints[i + 1]; - } - } - if (isW3cTypePrefix(hint) && nextHint != null && isW3cTypeHint(nextHint)) - { - hint = nextHint; - i++; - CommonUtil.logd($"Hint is a W3C type prefix; using {hint} instead"); - } - if (isW3cAddressType(hint) && nextHint != null) - { - hint = nextHint; - i++; - CommonUtil.logd($"Hint is a W3C address prefix; using {hint} instead"); - } - - // Then check if the "actual" hint is supported. - if (AutofillHintsHelper.IsValidHint(hint)) + foreach (string hint in filledAutofillField.AutofillHints) + { + if (AutofillHintsHelper.IsSupportedHint(hint)) { HintMap.Add(hint, filledAutofillField); } else { - CommonUtil.loge($"Invalid hint: {autofillHints[i]}"); + CommonUtil.loge($"Invalid hint: {hint}"); } } } - private static bool isW3cSectionPrefix(string hint) - { - return hint.StartsWith(W3cHints.PREFIX_SECTION); - } - - private static bool isW3cAddressType(string hint) - { - switch (hint) - { - case W3cHints.SHIPPING: - case W3cHints.BILLING: - return true; - } - return false; - } - - private static bool isW3cTypePrefix(string hint) - { - switch (hint) - { - case W3cHints.PREFIX_WORK: - case W3cHints.PREFIX_FAX: - case W3cHints.PREFIX_HOME: - case W3cHints.PREFIX_PAGER: - return true; - } - return false; - } - - private static bool isW3cTypeHint(string hint) - { - switch (hint) - { - case W3cHints.TEL: - case W3cHints.TEL_COUNTRY_CODE: - case W3cHints.TEL_NATIONAL: - case W3cHints.TEL_AREA_CODE: - case W3cHints.TEL_LOCAL: - case W3cHints.TEL_LOCAL_PREFIX: - case W3cHints.TEL_LOCAL_SUFFIX: - case W3cHints.TEL_EXTENSION: - case W3cHints.EMAIL: - case W3cHints.IMPP: - return true; - } - Log.Warn(CommonUtil.Tag, "Invalid W3C type hint: " + hint); - return false; - } - /// /// Populates a Dataset.Builder with appropriate values for each AutofillId /// in a AutofillFieldMetadataCollection. @@ -142,16 +70,10 @@ namespace keepass2android.services.AutofillBase.model public bool ApplyToFields(AutofillFieldMetadataCollection autofillFieldMetadataCollection, Dataset.Builder datasetBuilder) { bool setValueAtLeastOnce = false; - List allHints = autofillFieldMetadataCollection.AllAutofillHints; - for (int hintIndex = 0; hintIndex < allHints.Count; hintIndex++) + + foreach (string hint in autofillFieldMetadataCollection.AllAutofillCanonicalHints) { - string hint = allHints[hintIndex]; - List fillableAutofillFields = autofillFieldMetadataCollection.GetFieldsForHint(hint); - if (fillableAutofillFields == null) - { - continue; - } - foreach (AutofillFieldMetadata autofillFieldMetadata in fillableAutofillFields) + foreach (AutofillFieldMetadata autofillFieldMetadata in autofillFieldMetadataCollection.GetFieldsForHint(hint)) { FilledAutofillField filledAutofillField; if (!HintMap.TryGetValue(hint, out filledAutofillField) || (filledAutofillField == null)) diff --git a/src/keepass2android/services/AutofillBase/model/W3cHints.cs b/src/keepass2android/services/AutofillBase/model/W3cHints.cs index fb96fad6..8078ddfc 100644 --- a/src/keepass2android/services/AutofillBase/model/W3cHints.cs +++ b/src/keepass2android/services/AutofillBase/model/W3cHints.cs @@ -1,4 +1,6 @@ -namespace keepass2android.services.AutofillBase.model +using Android.Util; + +namespace keepass2android.services.AutofillBase.model { public class W3cHints { @@ -70,5 +72,56 @@ private W3cHints() { } + + + + public static bool isW3cSectionPrefix(string hint) + { + return hint.ToLower().StartsWith(W3cHints.PREFIX_SECTION); + } + + public static bool isW3cAddressType(string hint) + { + switch (hint.ToLower()) + { + case W3cHints.SHIPPING: + case W3cHints.BILLING: + return true; + } + return false; + } + + public static bool isW3cTypePrefix(string hint) + { + switch (hint.ToLower()) + { + case W3cHints.PREFIX_WORK: + case W3cHints.PREFIX_FAX: + case W3cHints.PREFIX_HOME: + case W3cHints.PREFIX_PAGER: + return true; + } + return false; + } + + public static bool isW3cTypeHint(string hint) + { + switch (hint.ToLower()) + { + case W3cHints.TEL: + case W3cHints.TEL_COUNTRY_CODE: + case W3cHints.TEL_NATIONAL: + case W3cHints.TEL_AREA_CODE: + case W3cHints.TEL_LOCAL: + case W3cHints.TEL_LOCAL_PREFIX: + case W3cHints.TEL_LOCAL_SUFFIX: + case W3cHints.TEL_EXTENSION: + case W3cHints.EMAIL: + case W3cHints.IMPP: + return true; + } + Log.Warn(CommonUtil.Tag, "Invalid W3C type hint: " + hint); + return false; + } } } \ No newline at end of file diff --git a/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs b/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs index 65a758d7..25ca1f01 100644 --- a/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs +++ b/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs @@ -41,32 +41,44 @@ namespace keepass2android.services.Kp2aAutofill if (!App.Kp2a.GetDb().Loaded || (App.Kp2a.QuickLocked)) return null; - string username = App.Kp2a.GetDb().LastOpenedEntry.Entry.Strings.ReadSafe(PwDefs.UserNameField); - string password = App.Kp2a.GetDb().LastOpenedEntry.Entry.Strings.ReadSafe(PwDefs.PasswordField); - - FilledAutofillField pwdField = - new FilledAutofillField - { - AutofillHints = new[] {View.AutofillHintPassword}, - TextValue = password - }; - - FilledAutofillField userField = new FilledAutofillField - { - AutofillHints = new[] {View.AutofillHintUsername}, - TextValue = username - }; - FilledAutofillFieldCollection fieldCollection = new FilledAutofillFieldCollection(); - fieldCollection.HintMap = new Dictionary(); - fieldCollection.Add(userField); - fieldCollection.Add(pwdField); - fieldCollection.DatasetName = App.Kp2a.GetDb().LastOpenedEntry.Entry.Strings.ReadSafe(PwDefs.TitleField); + var pwEntry = App.Kp2a.GetDb().LastOpenedEntry.Entry; + foreach (string key in pwEntry.Strings.GetKeys()) + { + FilledAutofillField field = + new FilledAutofillField + { + AutofillHints = new[] { GetCanonicalHintFromKp2aField(pwEntry, key) }, + TextValue = pwEntry.Strings.ReadSafe(key), + Protected = pwEntry.Strings.Get(key).IsProtected + }; + fieldCollection.Add(field); + } + //TODO add support for Keepass templates + //TODO add values like expiration? + //TODO if cc-exp is there, also set cc-exp-month etc. + + fieldCollection.DatasetName = pwEntry.Strings.ReadSafe(PwDefs.TitleField); return fieldCollection; } + private static readonly Dictionary keyToHint = new Dictionary() + { + {PwDefs.UserNameField, View.AutofillHintUsername }, + {PwDefs.PasswordField, View.AutofillHintPassword }, + {PwDefs.UrlField, W3cHints.URL }, + }; + + private string GetCanonicalHintFromKp2aField(PwEntry pwEntry, string key) + { + if (!keyToHint.TryGetValue(key, out string result)) + result = key; + result = result.ToLower(); + return result; + } + public override IAutofillIntentBuilder IntentBuilder => new Kp2aAutofillIntentBuilder(); } From 577febe3b70eacf262c64102c99df1e2cc7738c7 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Fri, 29 Dec 2017 07:31:20 +0100 Subject: [PATCH 22/50] unregister screen off broadcast receiver in PasswordActivity.OnDestroy() --- src/keepass2android/PasswordActivity.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/keepass2android/PasswordActivity.cs b/src/keepass2android/PasswordActivity.cs index 84c44f6a..187e777c 100644 --- a/src/keepass2android/PasswordActivity.cs +++ b/src/keepass2android/PasswordActivity.cs @@ -841,6 +841,8 @@ namespace keepass2android behavior.OnNestedFling(FindViewById(Resource.Id.main_content), appbarLayout, null, 0, 200, true); } + + protected override void OnCreate(Bundle savedInstanceState) { _activityDesign.ApplyTheme(); @@ -2055,6 +2057,7 @@ namespace keepass2android protected override void OnDestroy() { + UnregisterReceiver(_intentReceiver); base.OnDestroy(); if (_killOnDestroy) Process.KillProcess(Process.MyPid()); From d87b8f7652b52056b3f1c44dee2e9b846f5cb974 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Fri, 29 Dec 2017 17:37:14 +0100 Subject: [PATCH 23/50] improve autofill service (#9): introduce partitioning of Autofill data --- .../AutofillBase/AutofillHintsHelper.cs | 107 ++++++++++++++++++ .../ChooseForAutofillActivityBase.cs | 6 +- 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/src/keepass2android/services/AutofillBase/AutofillHintsHelper.cs b/src/keepass2android/services/AutofillBase/AutofillHintsHelper.cs index cc8ad0a0..b4862f34 100644 --- a/src/keepass2android/services/AutofillBase/AutofillHintsHelper.cs +++ b/src/keepass2android/services/AutofillBase/AutofillHintsHelper.cs @@ -85,6 +85,78 @@ namespace keepass2android.services.AutofillBase W3cHints.IMPP, }; + private static readonly List> partitionsOfCanonicalHints = new List>() + { + + new HashSet(StringComparer.OrdinalIgnoreCase) + { + View.AutofillHintEmailAddress, + View.AutofillHintPhone, + View.AutofillHintName, + View.AutofillHintPassword, + View.AutofillHintUsername, + W3cHints.HONORIFIC_PREFIX, + W3cHints.NAME, + W3cHints.GIVEN_NAME, + W3cHints.ADDITIONAL_NAME, + W3cHints.FAMILY_NAME, + W3cHints.HONORIFIC_SUFFIX, + W3cHints.ORGANIZATION_TITLE, + W3cHints.ORGANIZATION, + W3cHints.LANGUAGE, + W3cHints.BDAY, + W3cHints.BDAY_DAY, + W3cHints.BDAY_MONTH, + W3cHints.BDAY_YEAR, + W3cHints.SEX, + W3cHints.URL, + W3cHints.PHOTO, + W3cHints.TEL, + W3cHints.TEL_COUNTRY_CODE, + W3cHints.TEL_NATIONAL, + W3cHints.TEL_AREA_CODE, + W3cHints.TEL_LOCAL, + W3cHints.TEL_LOCAL_PREFIX, + W3cHints.TEL_LOCAL_SUFFIX, + W3cHints.TEL_EXTENSION, + W3cHints.IMPP, + }, + new HashSet(StringComparer.OrdinalIgnoreCase) + { + View.AutofillHintPostalAddress, + View.AutofillHintPostalCode, + + W3cHints.STREET_ADDRESS, + W3cHints.ADDRESS_LINE1, + W3cHints.ADDRESS_LINE2, + W3cHints.ADDRESS_LINE3, + W3cHints.ADDRESS_LEVEL4, + W3cHints.ADDRESS_LEVEL3, + W3cHints.ADDRESS_LEVEL2, + W3cHints.ADDRESS_LEVEL1, + W3cHints.COUNTRY, + W3cHints.COUNTRY_NAME + }, + new HashSet(StringComparer.OrdinalIgnoreCase) + { + View.AutofillHintCreditCardExpirationDate, + View.AutofillHintCreditCardExpirationDay, + View.AutofillHintCreditCardExpirationMonth, + View.AutofillHintCreditCardExpirationYear, + View.AutofillHintCreditCardNumber, + View.AutofillHintCreditCardSecurityCode, + + W3cHints.CC_NAME, + W3cHints.CC_GIVEN_NAME, + W3cHints.CC_ADDITIONAL_NAME, + W3cHints.CC_FAMILY_NAME, + W3cHints.CC_TYPE, + W3cHints.TRANSACTION_CURRENCY, + W3cHints.TRANSACTION_AMOUNT, + }, + + }; + private static readonly Dictionary hintToCanonicalReplacement= new Dictionary(StringComparer.OrdinalIgnoreCase) { {W3cHints.EMAIL, View.AutofillHintEmailAddress}, @@ -145,5 +217,40 @@ namespace keepass2android.services.AutofillBase return result; } + + public static int GetPartitionIndex(string hint) + { + for (int i = 0; i < partitionsOfCanonicalHints.Count; i++) + { + if (partitionsOfCanonicalHints[i].Contains(hint)) + { + return i; + } + } + return -1; + } + + public static FilledAutofillFieldCollection FilterForPartition(FilledAutofillFieldCollection autofillFields, int partitionIndex) + { + FilledAutofillFieldCollection filteredCollection = + new FilledAutofillFieldCollection {DatasetName = autofillFields.DatasetName}; + + if (partitionIndex == -1) + return filteredCollection; + + foreach (var field in autofillFields.HintMap.Values.Distinct()) + { + foreach (var hint in field.AutofillHints) + { + if (GetPartitionIndex(hint) == partitionIndex) + { + filteredCollection.Add(field); + break; + } + } + } + + return filteredCollection; + } } } \ No newline at end of file diff --git a/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs b/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs index 2c6dfe1b..ae360703 100644 --- a/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs +++ b/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs @@ -11,6 +11,7 @@ using Android.Widget; using Java.Util; using keepass2android.services.AutofillBase.model; using System.Collections.Generic; +using System.Linq; namespace keepass2android.services.AutofillBase { @@ -84,8 +85,11 @@ namespace keepass2android.services.AutofillBase StructureParser parser = new StructureParser(this, structure); parser.ParseForFill(); AutofillFieldMetadataCollection autofillFields = parser.AutofillFields; + int partitionIndex = AutofillHintsHelper.GetPartitionIndex(autofillFields.FocusedAutofillCanonicalHints.First()); + FilledAutofillFieldCollection partitionData = + AutofillHintsHelper.FilterForPartition(clientFormDataMap, partitionIndex); ReplyIntent = new Intent(); - SetDatasetIntent(AutofillHelper.NewDataset(this, autofillFields, clientFormDataMap, false, IntentBuilder)); + SetDatasetIntent(AutofillHelper.NewDataset(this, autofillFields, partitionData, false, IntentBuilder)); } protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) From 6c69119d0910abae81b4d88de9bde49e316aa1be Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Sat, 30 Dec 2017 03:41:02 +0100 Subject: [PATCH 24/50] allow autofill (#9) for apps/websites without explicit autofill hints by detecting password fields and falling back to filling username/password if no autofill hint is available --- .../AutofillBase/AutofillFieldMetadata.cs | 11 +++- .../AutofillFieldMetadataCollection.cs | 7 ++- .../AutofillBase/AutofillServiceBase.cs | 15 ++++-- .../services/AutofillBase/StructureParser.cs | 53 ++++++++++++++++++- 4 files changed, 76 insertions(+), 10 deletions(-) diff --git a/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs b/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs index 965af5fe..47c4f175 100644 --- a/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs +++ b/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs @@ -22,13 +22,20 @@ namespace keepass2android.services.AutofillBase string[] AutofillOptions { get; } public bool Focused { get; } - public AutofillFieldMetadata(AssistStructure.ViewNode view) + public AutofillFieldMetadata(AssistStructure.ViewNode view) + : this(view, view.GetAutofillHints()) + { + + } + + + public AutofillFieldMetadata(AssistStructure.ViewNode view, string[] autofillHints) { AutofillId = view.AutofillId; AutofillType = view.AutofillType; AutofillOptions = view.GetAutofillOptions(); Focused = view.IsFocused; - var supportedHints = AutofillHintsHelper.FilterForSupportedHints(view.GetAutofillHints()); + var supportedHints = AutofillHintsHelper.FilterForSupportedHints(autofillHints); var canonicalHints = AutofillHintsHelper.ConvertToCanonicalHints(supportedHints); SetHints(canonicalHints.ToArray()); diff --git a/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs b/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs index 94a3db79..9e8457f6 100644 --- a/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs +++ b/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs @@ -20,7 +20,12 @@ namespace keepass2android.services.AutofillBase int Size = 0; public SaveDataType SaveType { get; set; } - public AutofillFieldMetadataCollection() + public bool Empty + { + get { return Size == 0; } + } + + public AutofillFieldMetadataCollection() { SaveType = 0; FocusedAutofillCanonicalHints = new List(); diff --git a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs index bc71915f..9dd8089d 100644 --- a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs +++ b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs @@ -31,13 +31,13 @@ namespace keepass2android.services.AutofillBase public override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) { - Log.Debug(CommonUtil.Tag, "onFillRequest"); + CommonUtil.logd( "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)); + CommonUtil.logd( "onFillRequest(): data=" + CommonUtil.BundleToString(clientState)); cancellationSignal.CancelEvent += (sender, e) => { @@ -62,7 +62,7 @@ namespace keepass2android.services.AutofillBase bool responseAuth = true; var autofillIds = autofillFields.GetAutofillIds(); - if (responseAuth && autofillIds.Length != 0) + if (responseAuth && autofillIds.Length != 0 && CanAutofill(query)) { var responseBuilder = new FillResponse.Builder(); @@ -89,6 +89,11 @@ namespace keepass2android.services.AutofillBase } } + private bool CanAutofill(string query) + { + return !(query == "androidapp://android" || query == "androidapp://"+this.PackageName); + } + public override void OnSaveRequest(SaveRequest request, SaveCallback callback) { //TODO implement @@ -98,12 +103,12 @@ namespace keepass2android.services.AutofillBase public override void OnConnected() { - Log.Debug(CommonUtil.Tag, "onConnected"); + CommonUtil.logd( "onConnected"); } public override void OnDisconnected() { - Log.Debug(CommonUtil.Tag, "onDisconnected"); + CommonUtil.logd( "onDisconnected"); } public abstract IAutofillIntentBuilder IntentBuilder{get;} diff --git a/src/keepass2android/services/AutofillBase/StructureParser.cs b/src/keepass2android/services/AutofillBase/StructureParser.cs index 6c0f751d..ef309b85 100644 --- a/src/keepass2android/services/AutofillBase/StructureParser.cs +++ b/src/keepass2android/services/AutofillBase/StructureParser.cs @@ -1,7 +1,11 @@ using System; +using System.Collections.Generic; +using System.Linq; using Android.App.Assist; using Android.Content; +using Android.Text; using Android.Util; +using Android.Views; using FilledAutofillFieldCollection = keepass2android.services.AutofillBase.model.FilledAutofillFieldCollection; namespace keepass2android.services.AutofillBase @@ -16,7 +20,8 @@ namespace keepass2android.services.AutofillBase public Context mContext { get; } public AutofillFieldMetadataCollection AutofillFields { get; set; } AssistStructure Structure; - public FilledAutofillFieldCollection ClientFormData { get; set; } + private List _editTextsWithoutHint = new List(); + public FilledAutofillFieldCollection ClientFormData { get; set; } public StructureParser(Context context, AssistStructure structure) { @@ -46,12 +51,52 @@ namespace keepass2android.services.AutofillBase var nodes = Structure.WindowNodeCount; ClientFormData = new FilledAutofillFieldCollection(); String webDomain = null; - for (int i = 0; i < nodes; i++) + _editTextsWithoutHint.Clear(); + + for (int i = 0; i < nodes; i++) { var node = Structure.GetWindowNodeAt(i); var view = node.RootViewNode; ParseLocked(forFill, view, ref webDomain); } + + if (AutofillFields.Empty) + { + var passwordFields = _editTextsWithoutHint + .Where(f => + (!f.IdEntry?.ToLowerInvariant().Contains("search") ?? true) && + (!f.Hint?.ToLowerInvariant().Contains("search") ?? true) && + ( + f.InputType.HasFlag(InputTypes.TextVariationPassword) || + f.InputType.HasFlag(InputTypes.TextVariationVisiblePassword) || + f.InputType.HasFlag(InputTypes.TextVariationWebPassword) || + (f.HtmlInfo?.Attributes.Any(p => p.First.ToString() == "type" && p.Second.ToString() == "password") ?? false) + ) + ).ToList(); + if (!_editTextsWithoutHint.Any()) + { + passwordFields = _editTextsWithoutHint.Where(f => + (f.IdEntry?.ToLowerInvariant().Contains("password") ?? false) + || (f.Hint?.ToLowerInvariant().Contains("password") ?? false)).ToList(); + + + } + foreach (var passwordField in passwordFields) + { + AutofillFields.Add(new AutofillFieldMetadata(passwordField, new[] { View.AutofillHintPassword })); + var usernameField = _editTextsWithoutHint.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault(); + if (usernameField != null) + { + AutofillFields.Add(new AutofillFieldMetadata(usernameField, new[] {View.AutofillHintUsername})); + } + } + + + + + } + + String packageName = Structure.ActivityComponent.PackageName; if (!string.IsNullOrEmpty(webDomain)) { @@ -103,6 +148,10 @@ namespace keepass2android.services.AutofillBase //ClientFormData.Add(new FilledAutofillField(viewNode)); } } + else if (viewNode.ClassName == "android.widget.EditText" || viewNode?.HtmlInfo?.Tag == "input") + { + _editTextsWithoutHint.Add(viewNode); + } var childrenSize = viewNode.ChildCount; if (childrenSize > 0) { From c150d248436d3534311ee08660e6084819c6309d Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Sat, 30 Dec 2017 20:36:27 +0100 Subject: [PATCH 25/50] allow webdomains for browsers only, otherwise use package name --- .../AutofillBase/AutofillServiceBase.cs | 6 ++--- .../Kp2aDigitalAssetLinksDataSource.cs | 22 ++++++++++++++++--- .../services/AutofillBase/StructureParser.cs | 11 +++++----- .../AutofillBase/model/FilledAutofillField.cs | 2 +- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs index 9dd8089d..66df9138 100644 --- a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs +++ b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs @@ -34,7 +34,7 @@ namespace keepass2android.services.AutofillBase CommonUtil.logd( "onFillRequest"); var structure = request.FillContexts[request.FillContexts.Count - 1].Structure; - //TODO package signature verification? + //TODO support package signature verification as soon as this is supported in Keepass storage var clientState = request.ClientState; CommonUtil.logd( "onFillRequest(): data=" + CommonUtil.BundleToString(clientState)); @@ -96,8 +96,8 @@ namespace keepass2android.services.AutofillBase public override void OnSaveRequest(SaveRequest request, SaveCallback callback) { - //TODO implement - callback.OnFailure("not implemented"); + //TODO implement save + callback.OnFailure("Saving data is currently not implemented in Keepass2Android."); } diff --git a/src/keepass2android/services/AutofillBase/Kp2aDigitalAssetLinksDataSource.cs b/src/keepass2android/services/AutofillBase/Kp2aDigitalAssetLinksDataSource.cs index 511edada..c356ba19 100644 --- a/src/keepass2android/services/AutofillBase/Kp2aDigitalAssetLinksDataSource.cs +++ b/src/keepass2android/services/AutofillBase/Kp2aDigitalAssetLinksDataSource.cs @@ -1,4 +1,5 @@ -using Android.Content; +using System.Collections.Generic; +using Android.Content; namespace keepass2android.services.AutofillBase { @@ -23,8 +24,23 @@ namespace keepass2android.services.AutofillBase public bool IsValid(Context context, string webDomain, string packageName) { - //TODO implement - return true; + return (IsTrustedBrowser(packageName)); + + } + + static readonly HashSet _trustedBrowsers = new HashSet + { + "org.mozilla.klar","org.mozilla.focus","org.mozilla.firefox","org.mozilla.firefox_beta","com.microsoft.emmx", + "com.android.chrome","com.chrome.beta","com.android.browser","com.brave.browser","com.opera.browser", + "com.opera.browser.beta","com.opera.mini.native","com.chrome.dev","com.chrome.canary", + "com.google.android.apps.chrome","com.google.android.apps.chrome_dev","com.yandex.browser", + "com.sec.android.app.sbrowser","com.sec.android.app.sbrowser.beta","org.codeaurora.swe.browser", + "com.amazon.cloud9" + }; + + private bool IsTrustedBrowser(string packageName) + { + return _trustedBrowsers.Contains(packageName); } } } \ No newline at end of file diff --git a/src/keepass2android/services/AutofillBase/StructureParser.cs b/src/keepass2android/services/AutofillBase/StructureParser.cs index ef309b85..1c7d9be5 100644 --- a/src/keepass2android/services/AutofillBase/StructureParser.cs +++ b/src/keepass2android/services/AutofillBase/StructureParser.cs @@ -103,13 +103,12 @@ namespace keepass2android.services.AutofillBase bool valid = Kp2aDigitalAssetLinksDataSource.Instance.IsValid(mContext, webDomain, packageName); if (!valid) { - throw new Java.Lang.SecurityException(mContext.GetString( - Resource.String.invalid_link_association, webDomain, packageName)); + CommonUtil.loge($"DAL verification failed for {packageName}/{webDomain}"); + webDomain = null; } - Log.Debug(CommonUtil.Tag, $"Domain {webDomain} is valid for {packageName}"); } - else - { + if (string.IsNullOrEmpty(webDomain)) + { webDomain = "androidapp://" + packageName; Log.Debug(CommonUtil.Tag, "no web domain. Using package name."); } @@ -143,7 +142,7 @@ namespace keepass2android.services.AutofillBase } else { - //TODO implement + //TODO implement save throw new NotImplementedException("TODO: Port and use AutoFill hints"); //ClientFormData.Add(new FilledAutofillField(viewNode)); } diff --git a/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs b/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs index 3afe0495..478abd50 100644 --- a/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs +++ b/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs @@ -83,7 +83,7 @@ namespace keepass2android.services.AutofillBase.model } AutofillHints = hintList.ToArray(); - //TODO port updated FilledAutofillField + //TODO port updated FilledAutofillField for saving AutofillValue autofillValue = viewNode.AutofillValue; if (autofillValue != null) { From e4c6285fab9393e4a801618c052d8a560f97cbaf Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Sat, 30 Dec 2017 21:30:18 +0100 Subject: [PATCH 26/50] improve presentation of Autofill (#9), support credit card datasets created from templates & expiry date --- .../layout/autofill_service_list_item.xml | 5 ++ src/keepass2android/app/App.cs | 9 ++- .../services/AutofillBase/AutofillHelper.cs | 6 +- .../AutofillBase/AutofillServiceBase.cs | 4 +- .../Kp2aAutofill/ChooseForAutofillActivity.cs | 78 +++++++++++++++++-- .../Kp2aAutofill/Kp2aAutofillService.cs | 2 +- .../services/Kp2aAutofillIntentBuilder.cs | 5 ++ 7 files changed, 91 insertions(+), 18 deletions(-) diff --git a/src/keepass2android/Resources/layout/autofill_service_list_item.xml b/src/keepass2android/Resources/layout/autofill_service_list_item.xml index b238ba41..44c1cd74 100644 --- a/src/keepass2android/Resources/layout/autofill_service_list_item.xml +++ b/src/keepass2android/Resources/layout/autofill_service_list_item.xml @@ -36,6 +36,11 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" + + android:maxWidth="28dp" + android:maxHeight="28dp" + android:adjustViewBounds="true" + android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd" android:src="@drawable/ic_launcher" /> \ No newline at end of file diff --git a/src/keepass2android/app/App.cs b/src/keepass2android/app/App.cs index 656ac04c..fc859254 100644 --- a/src/keepass2android/app/App.cs +++ b/src/keepass2android/app/App.cs @@ -69,15 +69,16 @@ namespace keepass2android { #if DEBUG public const string AppName = "@string/app_name_debug"; + public const int AppNameResource = Resource.String.app_name_debug; #else public const string AppName = "@string/app_name"; + public const int AppNameResource = Resource.String.app_name; #endif - public const int AppNameResource = Resource.String.app_name; - public const string AppNameShort = "@string/short_app_name" + "DBG"; - public const string AppLauncherTitle = "@string/app_name" + " Debug"; + + #if DEBUG - public const string PackagePart = "keepass2android_debug"; + public const string PackagePart = "keepass2android_debug"; public const string Searchable = "@xml/searchable_debug"; #else public const string PackagePart = "keepass2android"; diff --git a/src/keepass2android/services/AutofillBase/AutofillHelper.cs b/src/keepass2android/services/AutofillBase/AutofillHelper.cs index d8f83f73..32c09a9e 100644 --- a/src/keepass2android/services/AutofillBase/AutofillHelper.cs +++ b/src/keepass2android/services/AutofillBase/AutofillHelper.cs @@ -33,16 +33,14 @@ namespace keepass2android.services.AutofillBase if (datasetAuth) { datasetBuilder = new Dataset.Builder - (NewRemoteViews(context.PackageName, datasetName, - Resource.Drawable.ic_launcher)); + (NewRemoteViews(context.PackageName, datasetName, intentBuilder.AppIconResource)); IntentSender sender = intentBuilder.GetAuthIntentSenderForDataset(context, datasetName); datasetBuilder.SetAuthentication(sender); } else { datasetBuilder = new Dataset.Builder - (NewRemoteViews(context.PackageName, datasetName, - Resource.Drawable.ic_launcher)); + (NewRemoteViews(context.PackageName, datasetName, intentBuilder.AppIconResource)); } var setValueAtLeastOnce = filledAutofillFieldCollection.ApplyToFields(autofillFields, datasetBuilder); if (setValueAtLeastOnce) diff --git a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs index 66df9138..dbd16e9a 100644 --- a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs +++ b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs @@ -14,6 +14,8 @@ namespace keepass2android.services.AutofillBase IntentSender GetAuthIntentSenderForResponse(Context context, string query); IntentSender GetAuthIntentSenderForDataset(Context context, string dataset); Intent GetRestartAppIntent(Context context); + + int AppIconResource { get; } } public abstract class AutofillServiceBase: AutofillService @@ -67,7 +69,7 @@ namespace keepass2android.services.AutofillBase var responseBuilder = new FillResponse.Builder(); var sender = IntentBuilder.GetAuthIntentSenderForResponse(this, query); - RemoteViews presentation = AutofillHelper.NewRemoteViews(PackageName, GetString(Resource.String.autofill_sign_in_prompt),Resource.Drawable.ic_launcher); + RemoteViews presentation = AutofillHelper.NewRemoteViews(PackageName, GetString(Resource.String.autofill_sign_in_prompt), AppNames.LauncherIcon); var datasetBuilder = new Dataset.Builder(presentation); datasetBuilder.SetAuthentication(sender); diff --git a/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs b/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs index 25ca1f01..c51dad86 100644 --- a/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs +++ b/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs @@ -14,6 +14,7 @@ using keepass2android.services.AutofillBase; using keepass2android.services.AutofillBase.model; using Keepass2android.Pluginsdk; using KeePassLib; +using KeePassLib.Utility; namespace keepass2android.services.Kp2aAutofill { @@ -55,21 +56,82 @@ namespace keepass2android.services.Kp2aAutofill }; fieldCollection.Add(field); } - //TODO add support for Keepass templates - //TODO add values like expiration? - //TODO if cc-exp is there, also set cc-exp-month etc. + if (IsCreditCard(pwEntry) && pwEntry.Expires) + { + DateTime expTime = pwEntry.ExpiryTime; + FilledAutofillField field = + new FilledAutofillField + { + AutofillHints = new[] { View.AutofillHintCreditCardExpirationDate }, + DateValue = (long)(1000*TimeUtil.SerializeUnix(expTime)), + Protected = false + }; + fieldCollection.Add(field); + + field = + new FilledAutofillField + { + AutofillHints = new[] { View.AutofillHintCreditCardExpirationDay }, + TextValue = expTime.Day.ToString(), + Protected = false + }; + fieldCollection.Add(field); + + field = + new FilledAutofillField + { + AutofillHints = new[] { View.AutofillHintCreditCardExpirationMonth }, + TextValue = expTime.Month.ToString(), + Protected = false + }; + fieldCollection.Add(field); + + field = + new FilledAutofillField + { + AutofillHints = new[] { View.AutofillHintCreditCardExpirationYear }, + TextValue = expTime.Year.ToString(), + Protected = false + }; + fieldCollection.Add(field); + } + + fieldCollection.DatasetName = pwEntry.Strings.ReadSafe(PwDefs.TitleField); return fieldCollection; } - private static readonly Dictionary keyToHint = new Dictionary() + private bool IsCreditCard(PwEntry pwEntry) { - {PwDefs.UserNameField, View.AutofillHintUsername }, - {PwDefs.PasswordField, View.AutofillHintPassword }, - {PwDefs.UrlField, W3cHints.URL }, - }; + return pwEntry.Strings.Exists("cc-number") + || pwEntry.Strings.Exists("cc-csc") + || pwEntry.Strings.Exists(GetString(Resource.String.TemplateField_CreditCard_CVV)); + } + + private static readonly Dictionary keyToHint = BuildKeyToHint(); + + private static Dictionary BuildKeyToHint() + { + var result = new Dictionary + { + {PwDefs.UserNameField, View.AutofillHintUsername}, + {PwDefs.PasswordField, View.AutofillHintPassword}, + {PwDefs.UrlField, W3cHints.URL}, + { + Android.App.Application.Context.GetString(Resource.String.TemplateField_CreditCard_CVV), + View.AutofillHintCreditCardSecurityCode + }, + { + Android.App.Application.Context.GetString(Resource.String.TemplateField_CreditCard_Owner), + W3cHints.CC_NAME + }, + {Android.App.Application.Context.GetString(Resource.String.TemplateField_Number), View.AutofillHintCreditCardNumber}, + {Android.App.Application.Context.GetString(Resource.String.TemplateField_IdCard_Name), View.AutofillHintName}, + }; + return result; + } private string GetCanonicalHintFromKp2aField(PwEntry pwEntry, string key) { diff --git a/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs b/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs index fdb81185..90220048 100644 --- a/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs +++ b/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs @@ -8,7 +8,7 @@ using AutofillServiceBase = keepass2android.services.AutofillBase.AutofillServic namespace keepass2android.services { - [Service(Label = "Keepass2Android Autofill Service", Permission=Manifest.Permission.BindAutofillService)] + [Service(Label = AppNames.AppName, Permission=Manifest.Permission.BindAutofillService)] [IntentFilter(new [] {"android.service.autofill.AutofillService"})] [MetaData("android.autofill", Resource = "@xml/autofillservice")] [Register("keepass2android.services.Kp2aAutofillService")] diff --git a/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs b/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs index 010e5f4a..aca990fd 100644 --- a/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs +++ b/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs @@ -36,5 +36,10 @@ namespace keepass2android.services intent.AddFlags(ActivityFlags.ForwardResult); return intent; } + + public int AppIconResource + { + get { return AppNames.LauncherIcon; } + } } } \ No newline at end of file From 1857dd72b9be73ee8d642fd50f241e22a412ff49 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Sun, 31 Dec 2017 10:52:40 +0100 Subject: [PATCH 27/50] allow to fill single inputs (or autofill=off) with autofill (#9) --- .../AutofillFieldMetadataCollection.cs | 18 ++- .../services/AutofillBase/AutofillHelper.cs | 38 ++---- .../AutofillBase/AutofillServiceBase.cs | 10 +- .../ChooseForAutofillActivityBase.cs | 11 +- .../services/AutofillBase/StructureParser.cs | 115 ++++++++++++------ .../services/Kp2aAutofillIntentBuilder.cs | 13 +- 6 files changed, 119 insertions(+), 86 deletions(-) diff --git a/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs b/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs index 9e8457f6..30b44756 100644 --- a/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs +++ b/src/keepass2android/services/AutofillBase/AutofillFieldMetadataCollection.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Android.Service.Autofill; using Android.Views.Autofill; @@ -11,9 +12,9 @@ namespace keepass2android.services.AutofillBase /// public class AutofillFieldMetadataCollection { - List AutofillIds = new List(); - - Dictionary> AutofillCanonicalHintsToFieldsMap = new Dictionary>(); + readonly List AutofillIds = new List(); + + readonly Dictionary> AutofillCanonicalHintsToFieldsMap = new Dictionary>(); public List AllAutofillCanonicalHints { get; } public List FocusedAutofillCanonicalHints { get; } @@ -34,16 +35,21 @@ namespace keepass2android.services.AutofillBase public void Add(AutofillFieldMetadata autofillFieldMetadata) { - SaveType |= autofillFieldMetadata.SaveType; + var hintsList = autofillFieldMetadata.AutofillCanonicalHints; + if (!hintsList.Any()) + return; + if (AutofillIds.Contains(autofillFieldMetadata.AutofillId)) + return; + SaveType |= autofillFieldMetadata.SaveType; Size++; AutofillIds.Add(autofillFieldMetadata.AutofillId); - var hintsList = autofillFieldMetadata.AutofillCanonicalHints; + AllAutofillCanonicalHints.AddRange(hintsList); if (autofillFieldMetadata.Focused) { FocusedAutofillCanonicalHints.AddRange(hintsList); } - foreach (var hint in autofillFieldMetadata.AutofillCanonicalHints) + foreach (var hint in hintsList) { if (!AutofillCanonicalHintsToFieldsMap.ContainsKey(hint)) { diff --git a/src/keepass2android/services/AutofillBase/AutofillHelper.cs b/src/keepass2android/services/AutofillBase/AutofillHelper.cs index 32c09a9e..d1203afb 100644 --- a/src/keepass2android/services/AutofillBase/AutofillHelper.cs +++ b/src/keepass2android/services/AutofillBase/AutofillHelper.cs @@ -14,34 +14,22 @@ namespace keepass2android.services.AutofillBase /// 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, - AutofillFieldMetadataCollection autofillFields, FilledAutofillFieldCollection filledAutofillFieldCollection, bool datasetAuth, IAutofillIntentBuilder intentBuilder) + /// + /// 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. + public static Dataset NewDataset(Context context, + AutofillFieldMetadataCollection autofillFields, FilledAutofillFieldCollection filledAutofillFieldCollection, IAutofillIntentBuilder intentBuilder) { var datasetName = filledAutofillFieldCollection.DatasetName; if (datasetName != null) { - Dataset.Builder datasetBuilder; - if (datasetAuth) - { - datasetBuilder = new Dataset.Builder - (NewRemoteViews(context.PackageName, datasetName, intentBuilder.AppIconResource)); - IntentSender sender = intentBuilder.GetAuthIntentSenderForDataset(context, datasetName); - datasetBuilder.SetAuthentication(sender); - } - else - { - datasetBuilder = new Dataset.Builder - (NewRemoteViews(context.PackageName, datasetName, intentBuilder.AppIconResource)); - } + var datasetBuilder = new Dataset.Builder(NewRemoteViews(context.PackageName, datasetName, intentBuilder.AppIconResource)); + var setValueAtLeastOnce = filledAutofillFieldCollection.ApplyToFields(autofillFields, datasetBuilder); if (setValueAtLeastOnce) { @@ -80,7 +68,7 @@ namespace keepass2android.services.AutofillBase var filledAutofillFieldCollection = clientFormDataMap[datasetName]; if (filledAutofillFieldCollection != null) { - var dataset = NewDataset(context, autofillFields, filledAutofillFieldCollection, datasetAuth, intentBuilder); + var dataset = NewDataset(context, autofillFields, filledAutofillFieldCollection, intentBuilder); if (dataset != null) { responseBuilder.AddDataset(dataset); diff --git a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs index dbd16e9a..f363c96f 100644 --- a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs +++ b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs @@ -11,8 +11,7 @@ namespace keepass2android.services.AutofillBase { public interface IAutofillIntentBuilder { - IntentSender GetAuthIntentSenderForResponse(Context context, string query); - IntentSender GetAuthIntentSenderForDataset(Context context, string dataset); + IntentSender GetAuthIntentSenderForResponse(Context context, string query, bool isManualRequest); Intent GetRestartAppIntent(Context context); int AppIconResource { get; } @@ -33,7 +32,8 @@ namespace keepass2android.services.AutofillBase public override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) { - CommonUtil.logd( "onFillRequest"); + bool isManual = (request.Flags & FillRequest.FlagManualRequest) != 0; + CommonUtil.logd( "onFillRequest " + (isManual ? "manual" : "auto")); var structure = request.FillContexts[request.FillContexts.Count - 1].Structure; //TODO support package signature verification as soon as this is supported in Keepass storage @@ -50,7 +50,7 @@ namespace keepass2android.services.AutofillBase var parser = new StructureParser(this, structure); try { - query = parser.ParseForFill(); + query = parser.ParseForFill(isManual); } catch (Java.Lang.SecurityException e) @@ -68,7 +68,7 @@ namespace keepass2android.services.AutofillBase { var responseBuilder = new FillResponse.Builder(); - var sender = IntentBuilder.GetAuthIntentSenderForResponse(this, query); + var sender = IntentBuilder.GetAuthIntentSenderForResponse(this, query, isManual); RemoteViews presentation = AutofillHelper.NewRemoteViews(PackageName, GetString(Resource.String.autofill_sign_in_prompt), AppNames.LauncherIcon); var datasetBuilder = new Dataset.Builder(presentation); diff --git a/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs b/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs index ae360703..1f92f1ef 100644 --- a/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs +++ b/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs @@ -22,6 +22,7 @@ namespace keepass2android.services.AutofillBase public static string ExtraQueryString => "EXTRA_QUERY_STRING"; + public static string ExtraIsManualRequest => "EXTRA_IS_MANUAL_REQUEST"; public int RequestCodeQuery => 6245; @@ -78,18 +79,18 @@ namespace keepass2android.services.AutofillBase ReplyIntent = null; } - protected void OnSuccess(FilledAutofillFieldCollection clientFormDataMap) + protected void OnSuccess(FilledAutofillFieldCollection clientFormDataMap, bool isManual) { var intent = Intent; AssistStructure structure = (AssistStructure)intent.GetParcelableExtra(AutofillManager.ExtraAssistStructure); StructureParser parser = new StructureParser(this, structure); - parser.ParseForFill(); + parser.ParseForFill(isManual); AutofillFieldMetadataCollection autofillFields = parser.AutofillFields; - int partitionIndex = AutofillHintsHelper.GetPartitionIndex(autofillFields.FocusedAutofillCanonicalHints.First()); + int partitionIndex = AutofillHintsHelper.GetPartitionIndex(autofillFields.FocusedAutofillCanonicalHints.FirstOrDefault()); FilledAutofillFieldCollection partitionData = AutofillHintsHelper.FilterForPartition(clientFormDataMap, partitionIndex); ReplyIntent = new Intent(); - SetDatasetIntent(AutofillHelper.NewDataset(this, autofillFields, partitionData, false, IntentBuilder)); + SetDatasetIntent(AutofillHelper.NewDataset(this, autofillFields, partitionData, IntentBuilder)); } protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) @@ -99,7 +100,7 @@ namespace keepass2android.services.AutofillBase if (requestCode == RequestCodeQuery) { if (resultCode == ExpectedActivityResult) - OnSuccess(GetDataset(data)); + OnSuccess(GetDataset(data), Intent.GetBooleanExtra(ExtraIsManualRequest, false)); else OnFailure(); Finish(); diff --git a/src/keepass2android/services/AutofillBase/StructureParser.cs b/src/keepass2android/services/AutofillBase/StructureParser.cs index 1c7d9be5..c4cbfef8 100644 --- a/src/keepass2android/services/AutofillBase/StructureParser.cs +++ b/src/keepass2android/services/AutofillBase/StructureParser.cs @@ -30,22 +30,23 @@ namespace keepass2android.services.AutofillBase AutofillFields = new AutofillFieldMetadataCollection(); } - public string ParseForFill() + public string ParseForFill(bool isManual) { - return Parse(true); + return Parse(true, isManual); } public string ParseForSave() { - return Parse(false); + return Parse(false, true); } - /// - /// Traverse AssistStructure and add ViewNode metadata to a flat list. - /// - /// The parse. - /// If set to true for fill. - string Parse(bool forFill) + /// + /// Traverse AssistStructure and add ViewNode metadata to a flat list. + /// + /// The parse. + /// If set to true for fill. + /// + string Parse(bool forFill, bool isManualRequest) { Log.Debug(CommonUtil.Tag, "Parsing structure for " + Structure.ActivityComponent); var nodes = Structure.WindowNodeCount; @@ -57,44 +58,52 @@ namespace keepass2android.services.AutofillBase { var node = Structure.GetWindowNodeAt(i); var view = node.RootViewNode; - ParseLocked(forFill, view, ref webDomain); + ParseLocked(forFill, isManualRequest, view, ref webDomain); } + + if (AutofillFields.Empty) { var passwordFields = _editTextsWithoutHint - .Where(f => - (!f.IdEntry?.ToLowerInvariant().Contains("search") ?? true) && - (!f.Hint?.ToLowerInvariant().Contains("search") ?? true) && - ( - f.InputType.HasFlag(InputTypes.TextVariationPassword) || - f.InputType.HasFlag(InputTypes.TextVariationVisiblePassword) || - f.InputType.HasFlag(InputTypes.TextVariationWebPassword) || - (f.HtmlInfo?.Attributes.Any(p => p.First.ToString() == "type" && p.Second.ToString() == "password") ?? false) - ) - ).ToList(); - if (!_editTextsWithoutHint.Any()) + .Where(IsPassword).ToList(); + if (!passwordFields.Any()) { - passwordFields = _editTextsWithoutHint.Where(f => - (f.IdEntry?.ToLowerInvariant().Contains("password") ?? false) - || (f.Hint?.ToLowerInvariant().Contains("password") ?? false)).ToList(); - - + passwordFields = _editTextsWithoutHint.Where(HasPasswordHint).ToList(); } foreach (var passwordField in passwordFields) { - AutofillFields.Add(new AutofillFieldMetadata(passwordField, new[] { View.AutofillHintPassword })); + AutofillFields.Add(new AutofillFieldMetadata(passwordField, new[] { View.AutofillHintPassword })); var usernameField = _editTextsWithoutHint.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault(); if (usernameField != null) { AutofillFields.Add(new AutofillFieldMetadata(usernameField, new[] {View.AutofillHintUsername})); } } + //for some pages with two-step login, we don't see a password field and don't display the autofill for non-manual requests. But if the user forces autofill, + //let's assume it is a username field: + if (isManualRequest && !passwordFields.Any() && _editTextsWithoutHint.Count == 1) + { + AutofillFields.Add(new AutofillFieldMetadata(_editTextsWithoutHint.First(), new[] { View.AutofillHintUsername })); - - - + } + } + + //force focused fields to be included in autofill fields when request was triggered manually. This allows to fill fields which are "off" or don't have a hint (in case there are hints) + if (isManualRequest) + { + foreach (AssistStructure.ViewNode editText in _editTextsWithoutHint) + { + if (editText.IsFocused) + { + AutofillFields.Add(new AutofillFieldMetadata(editText, new[] { IsPassword(editText) || HasPasswordHint(editText) ? View.AutofillHintPassword : View.AutofillHintUsername })); + break; + } + + } + } + String packageName = Structure.ActivityComponent.PackageName; @@ -115,7 +124,26 @@ namespace keepass2android.services.AutofillBase return webDomain; } - void ParseLocked(bool forFill, AssistStructure.ViewNode viewNode, ref string validWebdomain) + private static bool HasPasswordHint(AssistStructure.ViewNode f) + { + return (f.IdEntry?.ToLowerInvariant().Contains("password") ?? false) + || (f.Hint?.ToLowerInvariant().Contains("password") ?? false); + } + + private static bool IsPassword(AssistStructure.ViewNode f) + { + return + (!f.IdEntry?.ToLowerInvariant().Contains("search") ?? true) && + (!f.Hint?.ToLowerInvariant().Contains("search") ?? true) && + ( + f.InputType.HasFlag(InputTypes.TextVariationPassword) || + f.InputType.HasFlag(InputTypes.TextVariationVisiblePassword) || + f.InputType.HasFlag(InputTypes.TextVariationWebPassword) || + (f.HtmlInfo?.Attributes.Any(p => p.First.ToString() == "type" && p.Second.ToString() == "password") ?? false) + ); + } + + void ParseLocked(bool forFill, bool isManualRequest, AssistStructure.ViewNode viewNode, ref string validWebdomain) { String webDomain = viewNode.WebDomain; if (webDomain != null) @@ -134,7 +162,19 @@ namespace keepass2android.services.AutofillBase } } - if (viewNode.GetAutofillHints() != null && viewNode.GetAutofillHints().Length > 0) + string[] viewHints = viewNode.GetAutofillHints(); + if (viewHints != null && viewHints.Length == 1 && viewHints.First() == "off" && viewNode.IsFocused && + isManualRequest) + viewHints[0] = "on"; + CommonUtil.logd("viewHints=" + viewHints); + CommonUtil.logd("class=" + viewNode.ClassName); + CommonUtil.logd("tag=" + (viewNode?.HtmlInfo?.Tag ?? "(null)")); + if (viewNode?.HtmlInfo?.Tag == "input") + { + foreach (var p in viewNode.HtmlInfo.Attributes) + CommonUtil.logd("attr="+p.First + "/" + p.Second); + } + if (viewHints != null && viewHints.Length > 0 && viewHints.First() != "on" /*if hint is "on", treat as if there is no hint*/) { if (forFill) { @@ -147,16 +187,21 @@ namespace keepass2android.services.AutofillBase //ClientFormData.Add(new FilledAutofillField(viewNode)); } } - else if (viewNode.ClassName == "android.widget.EditText" || viewNode?.HtmlInfo?.Tag == "input") + else { - _editTextsWithoutHint.Add(viewNode); + + if (viewNode.ClassName == "android.widget.EditText" || viewNode?.HtmlInfo?.Tag == "input") + { + _editTextsWithoutHint.Add(viewNode); + } + } var childrenSize = viewNode.ChildCount; if (childrenSize > 0) { for (int i = 0; i < childrenSize; i++) { - ParseLocked(forFill, viewNode.GetChildAt(i), ref validWebdomain); + ParseLocked(forFill, isManualRequest, viewNode.GetChildAt(i), ref validWebdomain); } } } diff --git a/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs b/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs index aca990fd..26578e22 100644 --- a/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs +++ b/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Text; using Android.App; @@ -16,20 +15,14 @@ namespace keepass2android.services class Kp2aAutofillIntentBuilder: IAutofillIntentBuilder { - public IntentSender GetAuthIntentSenderForResponse(Context context, string query) + public IntentSender GetAuthIntentSenderForResponse(Context context, string query, bool isManualRequest) { Intent intent = new Intent(context, typeof(ChooseForAutofillActivity)); intent.PutExtra(ChooseForAutofillActivityBase.ExtraQueryString, query); + intent.PutExtra(ChooseForAutofillActivityBase.ExtraIsManualRequest, isManualRequest); return PendingIntent.GetActivity(context, 0, intent, PendingIntentFlags.CancelCurrent).IntentSender; } - public IntentSender GetAuthIntentSenderForDataset(Context context, string dataset) - { - //TODO implement - //return GetAuthIntentSenderForResponse(context, null); - throw new NotImplementedException(); - } - public Intent GetRestartAppIntent(Context context) { var intent = new Intent(context, typeof(FileSelectActivity)); From 8e45d6462f799363fda4c3806b640d6296ecc3e7 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Sun, 31 Dec 2017 11:21:41 +0100 Subject: [PATCH 28/50] make sure Kp2a is closed after creating an entry for an autofill (#9) fill request --- src/keepass2android/ShareUrlResults.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keepass2android/ShareUrlResults.cs b/src/keepass2android/ShareUrlResults.cs index 9f3f8ef6..a5083b18 100644 --- a/src/keepass2android/ShareUrlResults.cs +++ b/src/keepass2android/ShareUrlResults.cs @@ -159,7 +159,7 @@ namespace keepass2android createUrlEntry.Visibility = ViewStates.Visible; createUrlEntry.Click += (sender, e) => { - GroupActivity.Launch(this, new CreateEntryThenCloseTask { Url = url }); + GroupActivity.Launch(this, new CreateEntryThenCloseTask { Url = url, ShowUserNotifications = (AppTask as SelectEntryTask)?.ShowUserNotifications ?? true }); Toast.MakeText(this, GetString(Resource.String.select_group_then_add, new Java.Lang.Object[] { GetString(Resource.String.add_entry) }), ToastLength.Long).Show(); }; } From e4eb0c701975a7d66ce29f59909d2e07fa5f7f1f Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Sun, 31 Dec 2017 11:21:57 +0100 Subject: [PATCH 29/50] remove deleted entries to avoid "finding" them when SearchForUrl --- src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs b/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs index 5fa72baa..50fed440 100644 --- a/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs +++ b/src/Kp2aBusinessLogic/database/edit/DeleteRunnable.cs @@ -181,6 +181,7 @@ namespace keepass2android PwDeletedObject pdo = new PwDeletedObject(pe.Uuid, dtNow); pd.DeletedObjects.Add(pdo); touchedGroups.Add(pgParent); + Db.Entries.Remove(pe.Uuid); } else // Recycle { From 332ce12eb5e915eda552fb04e6d4cf15f6a05e61 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Sun, 31 Dec 2017 11:23:22 +0100 Subject: [PATCH 30/50] ignore .vs folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index fd69d363..b170c7b1 100644 --- a/.gitignore +++ b/.gitignore @@ -157,3 +157,4 @@ adbprompt.ps1 src/java/KP2ASoftkeyboard_AS/build/generated/mockable-android-23.jar *.rawproto src/java/Keepass2AndroidPluginSDK2/build/generated/mockable-Google-Inc.-Google-APIs-23.jar +/src/.vs From ff94d188db1c1129e2dbe7ca90533042fdcba4a6 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Sun, 31 Dec 2017 11:47:31 +0100 Subject: [PATCH 31/50] version 1.04-pre1 --- src/keepass2android/ChangeLog.cs | 3 ++- src/keepass2android/Properties/AndroidManifest_net.xml | 4 ++-- src/keepass2android/Resources/values/strings.xml | 8 +++++++- src/keepass2android/keepass2android.csproj | 6 +++--- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/keepass2android/ChangeLog.cs b/src/keepass2android/ChangeLog.cs index 70a7d0c8..635749b6 100644 --- a/src/keepass2android/ChangeLog.cs +++ b/src/keepass2android/ChangeLog.cs @@ -26,7 +26,8 @@ namespace keepass2android AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(ctx, Android.Resource.Style.ThemeHoloLightDialog)); builder.SetTitle(ctx.GetString(Resource.String.ChangeLog_title)); List changeLog = new List{ - ctx.GetString(Resource.String.ChangeLog_1_03), + ctx.GetString(Resource.String.ChangeLog_1_04), + ctx.GetString(Resource.String.ChangeLog_1_03), ctx.GetString(Resource.String.ChangeLog_1_02), #if !NoNet ctx.GetString(Resource.String.ChangeLog_1_01g), diff --git a/src/keepass2android/Properties/AndroidManifest_net.xml b/src/keepass2android/Properties/AndroidManifest_net.xml index 11be5674..5bf30ca9 100644 --- a/src/keepass2android/Properties/AndroidManifest_net.xml +++ b/src/keepass2android/Properties/AndroidManifest_net.xml @@ -1,7 +1,7 @@  diff --git a/src/keepass2android/Resources/values/strings.xml b/src/keepass2android/Resources/values/strings.xml index 97491d7d..7783b63a 100644 --- a/src/keepass2android/Resources/values/strings.xml +++ b/src/keepass2android/Resources/values/strings.xml @@ -688,7 +688,13 @@ Show soft keyboard for password input when fingerprint scan is active. - + + Version 1.04\n + * Added Autofill service for Android 8.0 and later.\n + * Upgraded libraries, build tools and Target SDK version.\n + + + Version 1.03\n * Removed accessibility service for AutoFill as requested by Google. Please see password access settings to find a plugin replicating the previous functionality.\n * Added third party apps as storage option again\n diff --git a/src/keepass2android/keepass2android.csproj b/src/keepass2android/keepass2android.csproj index ee598bb5..56db5cb9 100644 --- a/src/keepass2android/keepass2android.csproj +++ b/src/keepass2android/keepass2android.csproj @@ -69,11 +69,11 @@ System.Core%3b True SdkOnly - False + false False Xamarin - False - False + false + false False true False From 11330b608bf1f9a9f8c4d4e6b183ce91bf198ec1 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Tue, 2 Jan 2018 13:07:14 +0100 Subject: [PATCH 32/50] use CommonUtil for logging of Autofill service everywhere --- src/keepass2android/services/AutofillBase/AutofillHelper.cs | 2 +- .../services/AutofillBase/AutofillHintsHelper.cs | 2 +- .../services/AutofillBase/ChooseForAutofillActivityBase.cs | 4 ++-- .../services/AutofillBase/StructureParser.cs | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/keepass2android/services/AutofillBase/AutofillHelper.cs b/src/keepass2android/services/AutofillBase/AutofillHelper.cs index d1203afb..1684e4e4 100644 --- a/src/keepass2android/services/AutofillBase/AutofillHelper.cs +++ b/src/keepass2android/services/AutofillBase/AutofillHelper.cs @@ -86,7 +86,7 @@ namespace keepass2android.services.AutofillBase } else { - Log.Debug(CommonUtil.Tag, "These fields are not meant to be saved by autofill."); + CommonUtil.logd("These fields are not meant to be saved by autofill."); return null; } } diff --git a/src/keepass2android/services/AutofillBase/AutofillHintsHelper.cs b/src/keepass2android/services/AutofillBase/AutofillHintsHelper.cs index b4862f34..b0536bf4 100644 --- a/src/keepass2android/services/AutofillBase/AutofillHintsHelper.cs +++ b/src/keepass2android/services/AutofillBase/AutofillHintsHelper.cs @@ -191,7 +191,7 @@ namespace keepass2android.services.AutofillBase } else { - Log.Debug(CommonUtil.Tag, "Invalid autofill hint: " + hint); + CommonUtil.logd("Invalid autofill hint: " + hint); } } var finalFilteredHints = new string[i]; diff --git a/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs b/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs index 1f92f1ef..18c2c733 100644 --- a/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs +++ b/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs @@ -59,7 +59,7 @@ namespace keepass2android.services.AutofillBase Finish(); } - + /* public override void Finish() { if (ReplyIntent != null) @@ -71,7 +71,7 @@ namespace keepass2android.services.AutofillBase SetResult(Result.Canceled); } base.Finish(); - } + }*/ void OnFailure() { diff --git a/src/keepass2android/services/AutofillBase/StructureParser.cs b/src/keepass2android/services/AutofillBase/StructureParser.cs index c4cbfef8..a6d38bec 100644 --- a/src/keepass2android/services/AutofillBase/StructureParser.cs +++ b/src/keepass2android/services/AutofillBase/StructureParser.cs @@ -48,7 +48,7 @@ namespace keepass2android.services.AutofillBase /// string Parse(bool forFill, bool isManualRequest) { - Log.Debug(CommonUtil.Tag, "Parsing structure for " + Structure.ActivityComponent); + CommonUtil.logd("Parsing structure for " + Structure.ActivityComponent); var nodes = Structure.WindowNodeCount; ClientFormData = new FilledAutofillFieldCollection(); String webDomain = null; @@ -119,7 +119,7 @@ namespace keepass2android.services.AutofillBase if (string.IsNullOrEmpty(webDomain)) { webDomain = "androidapp://" + packageName; - Log.Debug(CommonUtil.Tag, "no web domain. Using package name."); + CommonUtil.logd("no web domain. Using package name."); } return webDomain; } @@ -148,7 +148,7 @@ namespace keepass2android.services.AutofillBase String webDomain = viewNode.WebDomain; if (webDomain != null) { - Log.Debug(CommonUtil.Tag, $"child web domain: {webDomain}"); + CommonUtil.logd($"child web domain: {webDomain}"); if (!string.IsNullOrEmpty(validWebdomain)) { if (webDomain == validWebdomain) From 8287232866f417830acd6f6d5389cfe3792cf7be Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Tue, 2 Jan 2018 13:59:24 +0100 Subject: [PATCH 33/50] display the last opened entry as an additional dataset, helps to fill paypal app and helps with partitioned data --- src/Kp2aBusinessLogic/database/Database.cs | 1 + .../database/PwEntryOutput.cs | 5 ++ src/keepass2android/ShareUrlResults.cs | 9 ++-- src/keepass2android/app/AppTask.cs | 26 ++++++++-- .../AutofillBase/AutofillServiceBase.cs | 50 ++++++++++++++----- .../ChooseForAutofillActivityBase.cs | 13 ++--- .../Kp2aAutofill/ChooseForAutofillActivity.cs | 35 +++++++------ .../Kp2aAutofill/Kp2aAutofillService.cs | 10 ++++ .../services/Kp2aAutofillIntentBuilder.cs | 3 +- 9 files changed, 110 insertions(+), 42 deletions(-) diff --git a/src/Kp2aBusinessLogic/database/Database.cs b/src/Kp2aBusinessLogic/database/Database.cs index c795e082..c236a806 100644 --- a/src/Kp2aBusinessLogic/database/Database.cs +++ b/src/Kp2aBusinessLogic/database/Database.cs @@ -289,6 +289,7 @@ namespace keepass2android CanWrite = true; _reloadRequested = false; OtpAuxFileIoc = null; + LastOpenedEntry = null; } public void MarkAllGroupsAsDirty() { diff --git a/src/Kp2aBusinessLogic/database/PwEntryOutput.cs b/src/Kp2aBusinessLogic/database/PwEntryOutput.cs index 8ab57d0b..26d0ea18 100644 --- a/src/Kp2aBusinessLogic/database/PwEntryOutput.cs +++ b/src/Kp2aBusinessLogic/database/PwEntryOutput.cs @@ -56,5 +56,10 @@ namespace keepass2android { get { return _entry; } } + + /// + /// if the entry was selected by searching for a URL, the query URL is returned here. + /// + public string SearchUrl { get; set; } } } \ No newline at end of file diff --git a/src/keepass2android/ShareUrlResults.cs b/src/keepass2android/ShareUrlResults.cs index a5083b18..c1703a59 100644 --- a/src/keepass2android/ShareUrlResults.cs +++ b/src/keepass2android/ShareUrlResults.cs @@ -80,8 +80,9 @@ namespace keepass2android _db = App.Kp2a.GetDb(); if (App.Kp2a.DatabaseIsUnlocked) { - String searchUrl = ((SearchUrlTask)AppTask).UrlToSearchFor; - Query(searchUrl); + var searchUrlTask = ((SearchUrlTask)AppTask); + String searchUrl = searchUrlTask.UrlToSearchFor; + Query(searchUrl, searchUrlTask.AutoReturnFromQuery); } // else: LockCloseListActivity.OnResume will trigger a broadcast (LockDatabase) which will cause the activity to be finished. @@ -93,7 +94,7 @@ namespace keepass2android AppTask.ToBundle(outState); } - private void Query(String url) + private void Query(string url, bool autoReturnFromQuery) { try { @@ -125,7 +126,7 @@ namespace keepass2android } //if there is exactly one match: open the entry - if (Group.Entries.Count() == 1) + if ((Group.Entries.Count() == 1) && autoReturnFromQuery) { LaunchActivityForEntry(Group.Entries.Single(),0); return; diff --git a/src/keepass2android/app/AppTask.cs b/src/keepass2android/app/AppTask.cs index d868344c..d407b50e 100644 --- a/src/keepass2android/app/AppTask.cs +++ b/src/keepass2android/app/AppTask.cs @@ -361,6 +361,7 @@ namespace keepass2android { base.Setup(b); UrlToSearchFor = b.GetString(UrlToSearchKey); + AutoReturnFromQuery = b.GetBoolean(AutoReturnFromQueryKey, true); } public override IEnumerable Extras { @@ -369,9 +370,15 @@ namespace keepass2android foreach (IExtra e in base.Extras) yield return e; yield return new StringExtra { Key=UrlToSearchKey, Value = UrlToSearchFor }; - } + yield return new BoolExtra { Key = AutoReturnFromQueryKey, Value = AutoReturnFromQuery }; + } } - public override void AfterUnlockDatabase(PasswordActivity act) + + public const String AutoReturnFromQueryKey = "AutoReturnFromQuery"; + + public bool AutoReturnFromQuery { get; set; } + + public override void AfterUnlockDatabase(PasswordActivity act) { if (String.IsNullOrEmpty(UrlToSearchFor)) { @@ -403,6 +410,12 @@ namespace keepass2android base.PopulatePasswordAccessServiceIntent(intent); intent.PutExtra(UrlToSearchKey, UrlToSearchFor); } + + public override void CompleteOnCreateEntryActivity(EntryActivity activity) + { + App.Kp2a.GetDb().LastOpenedEntry.SearchUrl = UrlToSearchFor; + base.CompleteOnCreateEntryActivity(activity); + } } @@ -516,15 +529,18 @@ namespace keepass2android public override void CompleteOnCreateEntryActivity(EntryActivity activity) { - //if the database is readonly (or no URL exists), don't offer to modify the URL - if ((App.Kp2a.GetDb().CanWrite == false) || (String.IsNullOrEmpty(UrlToSearchFor))) + App.Kp2a.GetDb().LastOpenedEntry.SearchUrl = UrlToSearchFor; + + //if the database is readonly (or no URL exists), don't offer to modify the URL + if ((App.Kp2a.GetDb().CanWrite == false) || (String.IsNullOrEmpty(UrlToSearchFor))) { base.CompleteOnCreateEntryActivity(activity); return; } + - AskAddUrlThenCompleteCreate(activity, UrlToSearchFor); + AskAddUrlThenCompleteCreate(activity, UrlToSearchFor); } diff --git a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs index f363c96f..94cf6b1b 100644 --- a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs +++ b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Android.Content; using Android.OS; using Android.Runtime; @@ -6,12 +7,13 @@ using Android.Service.Autofill; using Android.Util; using Android.Views.Autofill; using Android.Widget; +using keepass2android.services.AutofillBase.model; namespace keepass2android.services.AutofillBase { public interface IAutofillIntentBuilder { - IntentSender GetAuthIntentSenderForResponse(Context context, string query, bool isManualRequest); + IntentSender GetAuthIntentSenderForResponse(Context context, string query, bool isManualRequest, bool autoReturnFromQuery); Intent GetRestartAppIntent(Context context); int AppIconResource { get; } @@ -67,19 +69,13 @@ namespace keepass2android.services.AutofillBase if (responseAuth && autofillIds.Length != 0 && CanAutofill(query)) { var responseBuilder = new FillResponse.Builder(); - - var sender = IntentBuilder.GetAuthIntentSenderForResponse(this, query, isManual); - RemoteViews presentation = AutofillHelper.NewRemoteViews(PackageName, GetString(Resource.String.autofill_sign_in_prompt), AppNames.LauncherIcon); - var datasetBuilder = new Dataset.Builder(presentation); - datasetBuilder.SetAuthentication(sender); - //need to add placeholders so we can directly fill after ChooseActivity - foreach (var autofillId in autofillIds) - { - datasetBuilder.SetValue(autofillId, AutofillValue.ForText("PLACEHOLDER")); - } + var entryDataset = AddEntryDataset(query, parser); + bool hasEntryDataset = entryDataset != null; + if (entryDataset != null) + responseBuilder.AddDataset(entryDataset); - responseBuilder.AddDataset(datasetBuilder.Build()); + AddQueryDataset(query, isManual, autofillIds, responseBuilder, !hasEntryDataset); callback.OnSuccess(responseBuilder.Build()); } @@ -91,6 +87,36 @@ namespace keepass2android.services.AutofillBase } } + private Dataset AddEntryDataset(string query, StructureParser parser) + { + var filledAutofillFieldCollection = GetSuggestedEntry(query); + if (filledAutofillFieldCollection == null) + return null; + int partitionIndex = AutofillHintsHelper.GetPartitionIndex(parser.AutofillFields.FocusedAutofillCanonicalHints.FirstOrDefault()); + FilledAutofillFieldCollection partitionData = AutofillHintsHelper.FilterForPartition(filledAutofillFieldCollection, partitionIndex); + + return AutofillHelper.NewDataset(this, parser.AutofillFields, partitionData, IntentBuilder); + } + + protected abstract FilledAutofillFieldCollection GetSuggestedEntry(string query); + + private void AddQueryDataset(string query, bool isManual, AutofillId[] autofillIds, FillResponse.Builder responseBuilder, bool autoReturnFromQuery) + { + var sender = IntentBuilder.GetAuthIntentSenderForResponse(this, query, isManual, autoReturnFromQuery); + RemoteViews presentation = AutofillHelper.NewRemoteViews(PackageName, + GetString(Resource.String.autofill_sign_in_prompt), AppNames.LauncherIcon); + + var datasetBuilder = new Dataset.Builder(presentation); + datasetBuilder.SetAuthentication(sender); + //need to add placeholders so we can directly fill after ChooseActivity + foreach (var autofillId in autofillIds) + { + datasetBuilder.SetValue(autofillId, AutofillValue.ForText("PLACEHOLDER")); + } + + responseBuilder.AddDataset(datasetBuilder.Build()); + } + private bool CanAutofill(string query) { return !(query == "androidapp://android" || query == "androidapp://"+this.PackageName); diff --git a/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs b/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs index 18c2c733..f088cc26 100644 --- a/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs +++ b/src/keepass2android/services/AutofillBase/ChooseForAutofillActivityBase.cs @@ -23,6 +23,7 @@ namespace keepass2android.services.AutofillBase public static string ExtraQueryString => "EXTRA_QUERY_STRING"; public static string ExtraIsManualRequest => "EXTRA_IS_MANUAL_REQUEST"; + public static string ExtraAutoReturnFromQuery => "EXTRA_AUTO_RETURN_FROM_QUERY"; public int RequestCodeQuery => 6245; @@ -46,11 +47,11 @@ namespace keepass2android.services.AutofillBase return; } - var i = GetQueryIntent(requestedUrl); + var i = GetQueryIntent(requestedUrl, Intent.GetBooleanExtra(ExtraAutoReturnFromQuery, true)); StartActivityForResult(i, RequestCodeQuery); } - protected abstract Intent GetQueryIntent(string requestedUrl); + protected abstract Intent GetQueryIntent(string requestedUrl, bool autoReturnFromQuery); protected void RestartApp() { @@ -59,7 +60,7 @@ namespace keepass2android.services.AutofillBase Finish(); } - /* + public override void Finish() { if (ReplyIntent != null) @@ -71,7 +72,7 @@ namespace keepass2android.services.AutofillBase SetResult(Result.Canceled); } base.Finish(); - }*/ + } void OnFailure() { @@ -87,8 +88,7 @@ namespace keepass2android.services.AutofillBase parser.ParseForFill(isManual); AutofillFieldMetadataCollection autofillFields = parser.AutofillFields; int partitionIndex = AutofillHintsHelper.GetPartitionIndex(autofillFields.FocusedAutofillCanonicalHints.FirstOrDefault()); - FilledAutofillFieldCollection partitionData = - AutofillHintsHelper.FilterForPartition(clientFormDataMap, partitionIndex); + FilledAutofillFieldCollection partitionData = AutofillHintsHelper.FilterForPartition(clientFormDataMap, partitionIndex); ReplyIntent = new Intent(); SetDatasetIntent(AutofillHelper.NewDataset(this, autofillFields, partitionData, IntentBuilder)); } @@ -121,6 +121,7 @@ namespace keepass2android.services.AutofillBase public abstract IAutofillIntentBuilder IntentBuilder { get; } + protected void SetResponseIntent(FillResponse fillResponse) { ReplyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, fillResponse); diff --git a/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs b/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs index c51dad86..0dcd33f3 100644 --- a/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs +++ b/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs @@ -24,13 +24,13 @@ namespace keepass2android.services.Kp2aAutofill Permission = "keepass2android." + AppNames.PackagePart + ".permission.Kp2aChooseAutofill")] public class ChooseForAutofillActivity : ChooseForAutofillActivityBase { - protected override Intent GetQueryIntent(string requestedUrl) + protected override Intent GetQueryIntent(string requestedUrl, bool autoReturnFromQuery) { //launch FileSelectActivity (which is root of the stack (exception: we're even below!)) with the appropriate task. //will return the results later Intent i = new Intent(this, typeof(FileSelectActivity)); //don't show user notifications when an entry is opened. - var task = new SearchUrlTask() { UrlToSearchFor = requestedUrl, ShowUserNotifications = false }; + var task = new SearchUrlTask() { UrlToSearchFor = requestedUrl, ShowUserNotifications = false, AutoReturnFromQuery = autoReturnFromQuery }; task.ToIntent(i); return i; } @@ -41,29 +41,37 @@ namespace keepass2android.services.Kp2aAutofill { if (!App.Kp2a.GetDb().Loaded || (App.Kp2a.QuickLocked)) return null; + var entryOutput = App.Kp2a.GetDb().LastOpenedEntry; + return GetFilledAutofillFieldCollectionFromEntry(entryOutput, this); + } + + public static FilledAutofillFieldCollection GetFilledAutofillFieldCollectionFromEntry(PwEntryOutput pwEntryOutput, Context context) + { + if (pwEntryOutput == null) + return null; FilledAutofillFieldCollection fieldCollection = new FilledAutofillFieldCollection(); + var pwEntry = pwEntryOutput.Entry; - var pwEntry = App.Kp2a.GetDb().LastOpenedEntry.Entry; foreach (string key in pwEntry.Strings.GetKeys()) { FilledAutofillField field = new FilledAutofillField { - AutofillHints = new[] { GetCanonicalHintFromKp2aField(pwEntry, key) }, + AutofillHints = new[] {GetCanonicalHintFromKp2aField(pwEntry, key)}, TextValue = pwEntry.Strings.ReadSafe(key), Protected = pwEntry.Strings.Get(key).IsProtected }; fieldCollection.Add(field); } - if (IsCreditCard(pwEntry) && pwEntry.Expires) + if (IsCreditCard(pwEntry, context) && pwEntry.Expires) { DateTime expTime = pwEntry.ExpiryTime; FilledAutofillField field = new FilledAutofillField { - AutofillHints = new[] { View.AutofillHintCreditCardExpirationDate }, - DateValue = (long)(1000*TimeUtil.SerializeUnix(expTime)), + AutofillHints = new[] {View.AutofillHintCreditCardExpirationDate}, + DateValue = (long) (1000 * TimeUtil.SerializeUnix(expTime)), Protected = false }; fieldCollection.Add(field); @@ -71,7 +79,7 @@ namespace keepass2android.services.Kp2aAutofill field = new FilledAutofillField { - AutofillHints = new[] { View.AutofillHintCreditCardExpirationDay }, + AutofillHints = new[] {View.AutofillHintCreditCardExpirationDay}, TextValue = expTime.Day.ToString(), Protected = false }; @@ -80,7 +88,7 @@ namespace keepass2android.services.Kp2aAutofill field = new FilledAutofillField { - AutofillHints = new[] { View.AutofillHintCreditCardExpirationMonth }, + AutofillHints = new[] {View.AutofillHintCreditCardExpirationMonth}, TextValue = expTime.Month.ToString(), Protected = false }; @@ -89,13 +97,12 @@ namespace keepass2android.services.Kp2aAutofill field = new FilledAutofillField { - AutofillHints = new[] { View.AutofillHintCreditCardExpirationYear }, + AutofillHints = new[] {View.AutofillHintCreditCardExpirationYear}, TextValue = expTime.Year.ToString(), Protected = false }; fieldCollection.Add(field); } - fieldCollection.DatasetName = pwEntry.Strings.ReadSafe(PwDefs.TitleField); @@ -103,11 +110,11 @@ namespace keepass2android.services.Kp2aAutofill return fieldCollection; } - private bool IsCreditCard(PwEntry pwEntry) + private static bool IsCreditCard(PwEntry pwEntry, Context context) { return pwEntry.Strings.Exists("cc-number") || pwEntry.Strings.Exists("cc-csc") - || pwEntry.Strings.Exists(GetString(Resource.String.TemplateField_CreditCard_CVV)); + || pwEntry.Strings.Exists(context.GetString(Resource.String.TemplateField_CreditCard_CVV)); } private static readonly Dictionary keyToHint = BuildKeyToHint(); @@ -133,7 +140,7 @@ namespace keepass2android.services.Kp2aAutofill return result; } - private string GetCanonicalHintFromKp2aField(PwEntry pwEntry, string key) + private static string GetCanonicalHintFromKp2aField(PwEntry pwEntry, string key) { if (!keyToHint.TryGetValue(key, out string result)) result = key; diff --git a/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs b/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs index 90220048..7dc24713 100644 --- a/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs +++ b/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs @@ -4,6 +4,8 @@ using Android.App; using Android.Content; using Android.Runtime; using keepass2android.services.AutofillBase; +using keepass2android.services.AutofillBase.model; +using keepass2android.services.Kp2aAutofill; using AutofillServiceBase = keepass2android.services.AutofillBase.AutofillServiceBase; namespace keepass2android.services @@ -24,6 +26,14 @@ namespace keepass2android.services { } + protected override FilledAutofillFieldCollection GetSuggestedEntry(string query) + { + if (App.Kp2a.GetDb()?.LastOpenedEntry?.SearchUrl == query) + return ChooseForAutofillActivity.GetFilledAutofillFieldCollectionFromEntry( + App.Kp2a.GetDb()?.LastOpenedEntry, this); + return null; + } + public override IAutofillIntentBuilder IntentBuilder => new Kp2aAutofillIntentBuilder(); } } \ No newline at end of file diff --git a/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs b/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs index 26578e22..12830a83 100644 --- a/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs +++ b/src/keepass2android/services/Kp2aAutofillIntentBuilder.cs @@ -15,11 +15,12 @@ namespace keepass2android.services class Kp2aAutofillIntentBuilder: IAutofillIntentBuilder { - public IntentSender GetAuthIntentSenderForResponse(Context context, string query, bool isManualRequest) + public IntentSender GetAuthIntentSenderForResponse(Context context, string query, bool isManualRequest, bool autoReturnFromQuery) { Intent intent = new Intent(context, typeof(ChooseForAutofillActivity)); intent.PutExtra(ChooseForAutofillActivityBase.ExtraQueryString, query); intent.PutExtra(ChooseForAutofillActivityBase.ExtraIsManualRequest, isManualRequest); + intent.PutExtra(ChooseForAutofillActivityBase.ExtraAutoReturnFromQuery, autoReturnFromQuery); return PendingIntent.GetActivity(context, 0, intent, PendingIntentFlags.CancelCurrent).IntentSender; } From a929db99399437aa8fbb81c426ddcb4e641c8aaf Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Tue, 2 Jan 2018 14:59:19 +0100 Subject: [PATCH 34/50] fix save popup in QuickUnlock --- .../Resources/layout/QuickUnlock.xml | 2 + .../services/AutofillBase/AutofillHelper.cs | 46 ------------------- .../AutofillBase/AutofillServiceBase.cs | 8 ++-- 3 files changed, 5 insertions(+), 51 deletions(-) diff --git a/src/keepass2android/Resources/layout/QuickUnlock.xml b/src/keepass2android/Resources/layout/QuickUnlock.xml index 5e38b63f..1bcb13a2 100644 --- a/src/keepass2android/Resources/layout/QuickUnlock.xml +++ b/src/keepass2android/Resources/layout/QuickUnlock.xml @@ -135,6 +135,8 @@ android:paddingRight="16dp" android:ems="4" android:layout_below="@id/QuickUnlock_label" android:id="@+id/QuickUnlock_password" + android:singleLine="true" + android:fontFamily="sans-serif" android:textSize="20sp" android:focusable="true" android:focusableInTouchMode="true" /> diff --git a/src/keepass2android/services/AutofillBase/AutofillHelper.cs b/src/keepass2android/services/AutofillBase/AutofillHelper.cs index 1684e4e4..003677ff 100644 --- a/src/keepass2android/services/AutofillBase/AutofillHelper.cs +++ b/src/keepass2android/services/AutofillBase/AutofillHelper.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Android.Content; using Android.Service.Autofill; using Android.Util; @@ -46,50 +45,5 @@ namespace keepass2android.services.AutofillBase 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, 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, intentBuilder); - if (dataset != null) - { - responseBuilder.AddDataset(dataset); - } - } - } - } - if (autofillFields.SaveType != 0) - { - //TODO implement save - var autofillIds = autofillFields.GetAutofillIds(); - responseBuilder.SetSaveInfo - (new SaveInfo.Builder(autofillFields.SaveType, autofillIds).Build()); - return responseBuilder.Build(); - } - else - { - CommonUtil.logd("These fields are not meant to be saved by autofill."); - return null; - } - } - } } diff --git a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs index 94cf6b1b..9b4f2749 100644 --- a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs +++ b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs @@ -64,9 +64,9 @@ namespace keepass2android.services.AutofillBase AutofillFieldMetadataCollection autofillFields = parser.AutofillFields; - bool responseAuth = true; + var autofillIds = autofillFields.GetAutofillIds(); - if (responseAuth && autofillIds.Length != 0 && CanAutofill(query)) + if (autofillIds.Length != 0 && CanAutofill(query)) { var responseBuilder = new FillResponse.Builder(); @@ -81,9 +81,7 @@ namespace keepass2android.services.AutofillBase } else { - var datasetAuth = true; - var response = AutofillHelper.NewResponse(this, datasetAuth, autofillFields, null, IntentBuilder); - callback.OnSuccess(response); + callback.OnSuccess(null); } } From b9e436d56deee6ffbafbee3d9bd350def8604bfa Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Tue, 2 Jan 2018 16:28:54 +0100 Subject: [PATCH 35/50] implemented saving of data from autofill service (#9) --- .../AutofillBase/AutofillFieldMetadata.cs | 71 ++++++++++--------- .../AutofillBase/AutofillServiceBase.cs | 25 ++++++- .../services/AutofillBase/StructureParser.cs | 46 ++++++++---- .../AutofillBase/model/FilledAutofillField.cs | 29 ++++++-- .../model/FilledAutofillFieldCollection.cs | 2 +- .../Kp2aAutofill/ChooseForAutofillActivity.cs | 16 +++-- .../Kp2aAutofill/Kp2aAutofillService.cs | 36 ++++++++++ 7 files changed, 165 insertions(+), 60 deletions(-) diff --git a/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs b/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs index 47c4f175..28c19adc 100644 --- a/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs +++ b/src/keepass2android/services/AutofillBase/AutofillFieldMetadata.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Android.App.Assist; using Android.Service.Autofill; using Android.Views; @@ -65,47 +67,48 @@ namespace keepass2android.services.AutofillBase return -1; } + static readonly HashSet _creditCardHints = new HashSet(StringComparer.OrdinalIgnoreCase) + { + View.AutofillHintCreditCardExpirationDate, + View.AutofillHintCreditCardExpirationDay, + View.AutofillHintCreditCardExpirationMonth, + View.AutofillHintCreditCardExpirationYear, + View.AutofillHintCreditCardNumber, + View.AutofillHintCreditCardSecurityCode + }; + + static readonly HashSet _addressHints = new HashSet(StringComparer.OrdinalIgnoreCase) + { + View.AutofillHintPostalAddress, + View.AutofillHintPostalCode + }; + void UpdateSaveTypeFromHints() { - //TODO future add savetypes for W3cHints SaveType = 0; if (AutofillCanonicalHints == null) { return; } - foreach (var hint in AutofillCanonicalHints) - { - 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; - } - } + if (AutofillCanonicalHints.Any(h => _creditCardHints.Contains(h))) + { + SaveType |= SaveDataType.CreditCard; + } + if (AutofillCanonicalHints.Any(h => h.Equals(View.AutofillHintEmailAddress, StringComparison.OrdinalIgnoreCase))) + SaveType |= SaveDataType.EmailAddress; + if (AutofillCanonicalHints.Any(h => _addressHints.Contains(h))) + { + SaveType |= SaveDataType.Address; + } + if (AutofillCanonicalHints.Any(h => h.Equals(View.AutofillHintUsername, StringComparison.OrdinalIgnoreCase))) + SaveType |= SaveDataType.Username; + + if (AutofillCanonicalHints.Any(h => h.Equals(View.AutofillHintPassword, StringComparison.OrdinalIgnoreCase))) + { + SaveType |= SaveDataType.Password; + SaveType &= ~SaveDataType.EmailAddress; + SaveType &= ~SaveDataType.Username; + } } } } diff --git a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs index 9b4f2749..352bc50e 100644 --- a/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs +++ b/src/keepass2android/services/AutofillBase/AutofillServiceBase.cs @@ -76,6 +76,8 @@ namespace keepass2android.services.AutofillBase responseBuilder.AddDataset(entryDataset); AddQueryDataset(query, isManual, autofillIds, responseBuilder, !hasEntryDataset); + responseBuilder.SetSaveInfo(new SaveInfo.Builder(parser.AutofillFields.SaveType, + parser.AutofillFields.GetAutofillIds()).Build()); callback.OnSuccess(responseBuilder.Build()); } @@ -122,10 +124,29 @@ namespace keepass2android.services.AutofillBase public override void OnSaveRequest(SaveRequest request, SaveCallback callback) { - //TODO implement save - callback.OnFailure("Saving data is currently not implemented in Keepass2Android."); + + var structure = request.FillContexts?.LastOrDefault()?.Structure; + if (structure == null) + { + return; + } + + var parser = new StructureParser(this, structure); + string query = parser.ParseForSave(); + try + { + HandleSaveRequest(parser, query); + callback.OnSuccess(); + } + catch (Exception e) + { + callback.OnFailure(e.Message); + } + } + protected abstract void HandleSaveRequest(StructureParser parser, string query); + public override void OnConnected() { diff --git a/src/keepass2android/services/AutofillBase/StructureParser.cs b/src/keepass2android/services/AutofillBase/StructureParser.cs index a6d38bec..5e914054 100644 --- a/src/keepass2android/services/AutofillBase/StructureParser.cs +++ b/src/keepass2android/services/AutofillBase/StructureParser.cs @@ -6,6 +6,8 @@ using Android.Content; using Android.Text; using Android.Util; using Android.Views; +using Android.Views.Autofill; +using keepass2android.services.AutofillBase.model; using FilledAutofillFieldCollection = keepass2android.services.AutofillBase.model.FilledAutofillFieldCollection; namespace keepass2android.services.AutofillBase @@ -61,32 +63,33 @@ namespace keepass2android.services.AutofillBase ParseLocked(forFill, isManualRequest, view, ref webDomain); } - - if (AutofillFields.Empty) + + List passwordFields = new List(); + List usernameFields = new List(); + if (AutofillFields.Empty) { - var passwordFields = _editTextsWithoutHint - .Where(IsPassword).ToList(); + passwordFields = _editTextsWithoutHint.Where(IsPassword).ToList(); if (!passwordFields.Any()) { passwordFields = _editTextsWithoutHint.Where(HasPasswordHint).ToList(); } + foreach (var passwordField in passwordFields) { - AutofillFields.Add(new AutofillFieldMetadata(passwordField, new[] { View.AutofillHintPassword })); var usernameField = _editTextsWithoutHint.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault(); if (usernameField != null) { - AutofillFields.Add(new AutofillFieldMetadata(usernameField, new[] {View.AutofillHintUsername})); + usernameFields.Add(usernameField); } } //for some pages with two-step login, we don't see a password field and don't display the autofill for non-manual requests. But if the user forces autofill, //let's assume it is a username field: if (isManualRequest && !passwordFields.Any() && _editTextsWithoutHint.Count == 1) { - AutofillFields.Add(new AutofillFieldMetadata(_editTextsWithoutHint.First(), new[] { View.AutofillHintUsername })); - + usernameFields.Add(_editTextsWithoutHint.First()); } + } @@ -97,12 +100,30 @@ namespace keepass2android.services.AutofillBase { if (editText.IsFocused) { - AutofillFields.Add(new AutofillFieldMetadata(editText, new[] { IsPassword(editText) || HasPasswordHint(editText) ? View.AutofillHintPassword : View.AutofillHintUsername })); + if (IsPassword(editText) || HasPasswordHint(editText)) + passwordFields.Add(editText); + else + usernameFields.Add(editText); break; } } } + + if (forFill) + { + foreach (var pf in passwordFields) + AutofillFields.Add(new AutofillFieldMetadata(pf, new[] { View.AutofillHintPassword })); + foreach (var uf in usernameFields) + AutofillFields.Add(new AutofillFieldMetadata(uf, new[] { View.AutofillHintUsername })); + } + else + { + foreach (var pf in passwordFields) + ClientFormData.Add(new FilledAutofillField(pf, new[] { View.AutofillHintPassword })); + foreach (var uf in usernameFields) + ClientFormData.Add(new FilledAutofillField(uf, new[] { View.AutofillHintUsername })); + } @@ -182,10 +203,9 @@ namespace keepass2android.services.AutofillBase } else { - //TODO implement save - throw new NotImplementedException("TODO: Port and use AutoFill hints"); - //ClientFormData.Add(new FilledAutofillField(viewNode)); - } + FilledAutofillField filledAutofillField = new FilledAutofillField(viewNode); + ClientFormData.Add(filledAutofillField); + } } else { diff --git a/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs b/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs index 478abd50..fd5dabd7 100644 --- a/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs +++ b/src/keepass2android/services/AutofillBase/model/FilledAutofillField.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using Android.App.Assist; using Android.Views.Autofill; +using KeePassLib.Utility; namespace keepass2android.services.AutofillBase.model { @@ -11,6 +12,17 @@ namespace keepass2android.services.AutofillBase.model public long? DateValue { get; set; } public bool? ToggleValue { get; set; } + public string ValueToString() + { + if (DateValue != null) + { + return TimeUtil.ConvertUnixTime((long)DateValue / 1000.0).ToLongDateString(); + } + if (ToggleValue != null) + return ToggleValue.ToString(); + return TextValue; + } + /// /// returns the autofill hints for the filled field. These are always lowercased for simpler string comparison. /// @@ -33,11 +45,17 @@ namespace keepass2android.services.AutofillBase.model public FilledAutofillField() {} - - public FilledAutofillField(AssistStructure.ViewNode viewNode) + + public FilledAutofillField(AssistStructure.ViewNode viewNode) + : this(viewNode, viewNode.GetAutofillHints()) + { + + } + + public FilledAutofillField(AssistStructure.ViewNode viewNode, string[] hints) { - string[] rawHints = AutofillHintsHelper.FilterForSupportedHints(viewNode.GetAutofillHints()); + string[] rawHints = AutofillHintsHelper.FilterForSupportedHints(hints); List hintList = new List(); string nextHint = null; @@ -81,9 +99,8 @@ namespace keepass2android.services.AutofillBase.model CommonUtil.loge($"Invalid hint: {rawHints[i]}"); } } - AutofillHints = hintList.ToArray(); - - //TODO port updated FilledAutofillField for saving + AutofillHints = AutofillHintsHelper.ConvertToCanonicalHints(hintList.ToArray()).ToArray(); + AutofillValue autofillValue = viewNode.AutofillValue; if (autofillValue != null) { diff --git a/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs b/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs index 9a2391f5..68d4d3c2 100644 --- a/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs +++ b/src/keepass2android/services/AutofillBase/model/FilledAutofillFieldCollection.cs @@ -44,7 +44,7 @@ namespace keepass2android.services.AutofillBase.model { if (AutofillHintsHelper.IsSupportedHint(hint)) { - HintMap.Add(hint, filledAutofillField); + HintMap.TryAdd(hint, filledAutofillField); } else { diff --git a/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs b/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs index 0dcd33f3..1afe8b27 100644 --- a/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs +++ b/src/keepass2android/services/Kp2aAutofill/ChooseForAutofillActivity.cs @@ -119,6 +119,14 @@ namespace keepass2android.services.Kp2aAutofill private static readonly Dictionary keyToHint = BuildKeyToHint(); + public static string GetKp2aKeyFromHint(string canonicalHint) + { + var key = keyToHint.FirstOrDefault(p => p.Value.Equals(canonicalHint, StringComparison.OrdinalIgnoreCase)).Key; + if (string.IsNullOrWhiteSpace(key)) + return canonicalHint; + return key; + } + private static Dictionary BuildKeyToHint() { var result = new Dictionary @@ -127,15 +135,15 @@ namespace keepass2android.services.Kp2aAutofill {PwDefs.PasswordField, View.AutofillHintPassword}, {PwDefs.UrlField, W3cHints.URL}, { - Android.App.Application.Context.GetString(Resource.String.TemplateField_CreditCard_CVV), + Application.Context.GetString(Resource.String.TemplateField_CreditCard_CVV), View.AutofillHintCreditCardSecurityCode }, { - Android.App.Application.Context.GetString(Resource.String.TemplateField_CreditCard_Owner), + Application.Context.GetString(Resource.String.TemplateField_CreditCard_Owner), W3cHints.CC_NAME }, - {Android.App.Application.Context.GetString(Resource.String.TemplateField_Number), View.AutofillHintCreditCardNumber}, - {Android.App.Application.Context.GetString(Resource.String.TemplateField_IdCard_Name), View.AutofillHintName}, + {Application.Context.GetString(Resource.String.TemplateField_Number), View.AutofillHintCreditCardNumber}, + {Application.Context.GetString(Resource.String.TemplateField_IdCard_Name), View.AutofillHintName}, }; return result; } diff --git a/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs b/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs index 7dc24713..88cd6131 100644 --- a/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs +++ b/src/keepass2android/services/Kp2aAutofill/Kp2aAutofillService.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Android; using Android.App; using Android.Content; @@ -6,6 +7,10 @@ using Android.Runtime; using keepass2android.services.AutofillBase; using keepass2android.services.AutofillBase.model; using keepass2android.services.Kp2aAutofill; +using Keepass2android.Pluginsdk; +using KeePassLib; +using KeePassLib.Utility; +using Org.Json; using AutofillServiceBase = keepass2android.services.AutofillBase.AutofillServiceBase; namespace keepass2android.services @@ -34,6 +39,37 @@ namespace keepass2android.services return null; } + protected override void HandleSaveRequest(StructureParser parser, string query) + { + + + var intent = new Intent(this, typeof(FileSelectActivity)); + + Dictionary outputFields = new Dictionary(); + foreach (var p in parser.ClientFormData.HintMap) + { + CommonUtil.logd(p.Key + " = " + p.Value.ValueToString()); + outputFields.TryAdd(ChooseForAutofillActivity.GetKp2aKeyFromHint(p.Key), p.Value.ValueToString()); + + } + if (query != null) + outputFields.TryAdd(PwDefs.UrlField, query); + + JSONObject jsonOutput = new JSONObject(outputFields); + var jsonOutputStr = jsonOutput.ToString(); + intent.PutExtra(Strings.ExtraEntryOutputData, jsonOutputStr); + + JSONArray jsonProtectedFields = new JSONArray( + (System.Collections.ICollection)new string[]{}); + intent.PutExtra(Strings.ExtraProtectedFieldsList, jsonProtectedFields.ToString()); + + intent.PutExtra(AppTask.AppTaskKey, "CreateEntryThenCloseTask"); + intent.PutExtra(CreateEntryThenCloseTask.ShowUserNotificationsKey, "false"); + + StartActivity(intent); + + } + public override IAutofillIntentBuilder IntentBuilder => new Kp2aAutofillIntentBuilder(); } } \ No newline at end of file From 17b50df1010480751a34b97b7c9ea6d1d77b8c56 Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Wed, 3 Jan 2018 21:19:14 +0100 Subject: [PATCH 36/50] change heuristic to decide what is a username field: treat every field before a password field as username field. Even though this seems to make less sense, it works better with several apps; decode field references for AutoFill; display item in preferences for Autofill (#9) --- .../Resources/values/config.xml | 2 + .../Resources/values/strings.xml | 4 ++ .../Resources/xml/preferences.xml | 6 +++ .../services/AutofillBase/StructureParser.cs | 17 ++++---- .../AutofillBase/model/FilledAutofillField.cs | 2 - .../Kp2aAutofill/ChooseForAutofillActivity.cs | 21 ++++------ .../settings/DatabaseSettingsActivity.cs | 40 ++++++++++++++++++- 7 files changed, 68 insertions(+), 24 deletions(-) diff --git a/src/keepass2android/Resources/values/config.xml b/src/keepass2android/Resources/values/config.xml index 29f65174..86b60f8c 100644 --- a/src/keepass2android/Resources/values/config.xml +++ b/src/keepass2android/Resources/values/config.xml @@ -96,6 +96,8 @@ QuickUnlock_prefs_key FileHandling_prefs_key keyboardswitch_prefs_key + AutoFill_prefs_key + OfflineMode_key diff --git a/src/keepass2android/Resources/values/strings.xml b/src/keepass2android/Resources/values/strings.xml index 7783b63a..8529d6c2 100644 --- a/src/keepass2android/Resources/values/strings.xml +++ b/src/keepass2android/Resources/values/strings.xml @@ -371,6 +371,7 @@ Show separate notifications for copying username and password to clipboard and activating the keyboard. AutoFill Accessibility-Service + AutoFill Service KP2A keyboard notification Make full entry accessible through the KP2A keyboard (recommended). @@ -576,6 +577,9 @@ disabled Find plug-ins online Scopes + + + not enabled %1$s is requesting credentials for %2$s. %1$s is requesting credentials. Please select an entry. diff --git a/src/keepass2android/Resources/xml/preferences.xml b/src/keepass2android/Resources/xml/preferences.xml index 72c4e1ab..5ab3772e 100644 --- a/src/keepass2android/Resources/xml/preferences.xml +++ b/src/keepass2android/Resources/xml/preferences.xml @@ -380,6 +380,12 @@ android:data="https://philippc.github.io/keepass2android/AccServiceAutoFill.html" /> + + + + Date: Thu, 4 Jan 2018 20:48:51 +0100 Subject: [PATCH 37/50] show message in bottom bar if KP2A autofill service is not enabled (#9) --- src/keepass2android/GroupBaseActivity.cs | 132 +++++++++++++++--- .../Resources/layout/group.xml | 34 ++++- .../Resources/values/strings.xml | 2 + src/keepass2android/ShareUrlResults.cs | 23 +-- src/keepass2android/keepass2android.csproj | 1 - src/keepass2android/search/SearchResults.cs | 4 +- src/keepass2android/views/GroupView.cs | 109 --------------- 7 files changed, 162 insertions(+), 143 deletions(-) delete mode 100644 src/keepass2android/views/GroupView.cs diff --git a/src/keepass2android/GroupBaseActivity.cs b/src/keepass2android/GroupBaseActivity.cs index 5a5a57e9..8d2ec443 100644 --- a/src/keepass2android/GroupBaseActivity.cs +++ b/src/keepass2android/GroupBaseActivity.cs @@ -34,7 +34,9 @@ using keepass2android.Io; using keepass2android.database.edit; using keepass2android.view; using Android.Graphics.Drawables; +using Android.Provider; using Android.Support.V4.View; +using Android.Views.Autofill; using CursorAdapter = Android.Support.V4.Widget.CursorAdapter; using Object = Java.Lang.Object; @@ -46,6 +48,17 @@ namespace keepass2android public const String KeyEntry = "entry"; public const String KeyMode = "mode"; + static readonly Dictionary bottomBarElementsPriority = new Dictionary() + { + { Resource.Id.cancel_insert_element, 20 }, + { Resource.Id.insert_element, 20 }, + { Resource.Id.autofill_infotext, 10 }, + { Resource.Id.select_other_entry, 20}, + { Resource.Id.add_url_entry, 20}, + }; + + private readonly HashSet showableBottomBarElements = new HashSet(); + private ActivityDesign _design; public virtual void LaunchActivityForEntry(PwEntry pwEntry, int pos) @@ -87,14 +100,6 @@ namespace keepass2android public void SetNormalButtonVisibility(bool showAddGroup, bool showAddEntry) { - //check for null in the following because the "empty" layouts may not have all views - - if (FindViewById(Resource.Id.bottom_bar) != null) - FindViewById(Resource.Id.bottom_bar).Visibility = BottomBarAlwaysVisible ? ViewStates.Visible : ViewStates.Gone; - - if (FindViewById(Resource.Id.divider2) != null) - FindViewById(Resource.Id.divider2).Visibility = BottomBarAlwaysVisible ? ViewStates.Visible : ViewStates.Gone; - if (FindViewById(Resource.Id.fabCancelAddNew) != null) { FindViewById(Resource.Id.fabCancelAddNew).Visibility = ViewStates.Gone; @@ -104,16 +109,53 @@ namespace keepass2android FindViewById(Resource.Id.fabAddNew).Visibility = (showAddGroup || showAddEntry) ? ViewStates.Visible : ViewStates.Gone; } + UpdateBottomBarElementVisibility(Resource.Id.insert_element, false); + UpdateBottomBarElementVisibility(Resource.Id.cancel_insert_element, false); - } + } - public virtual bool BottomBarAlwaysVisible - { - get { return false; } - } + void UpdateBottomBarVisibility() + { + var bottomBar = FindViewById(Resource.Id.bottom_bar); + //check for null because the "empty" layouts may not have all views + int highestPrio = -1; + HashSet highestPrioElements = new HashSet(); + if (bottomBar != null) + { + for (int i = 0; i < bottomBar.ChildCount; i++) + { + int id = bottomBar.GetChildAt(i).Id; + if (!showableBottomBarElements.Contains(id)) + continue; + int myPrio = bottomBarElementsPriority[id]; + + if (!highestPrioElements.Any() || highestPrio < myPrio) + { + highestPrioElements.Clear(); + highestPrio = myPrio; + } + if (highestPrio == myPrio) + { + highestPrioElements.Add(id); + } + } + + bottomBar.Visibility = highestPrioElements.Any() ? ViewStates.Visible : ViewStates.Gone; + + for (int i = 0; i < bottomBar.ChildCount; i++) + { + int id = bottomBar.GetChildAt(i).Id; + bottomBar.GetChildAt(i).Visibility = + highestPrioElements.Contains(id) ? ViewStates.Visible : ViewStates.Gone; + } + + if (FindViewById(Resource.Id.divider2) != null) + FindViewById(Resource.Id.divider2).Visibility = highestPrioElements.Any() ? ViewStates.Visible : ViewStates.Gone; + } + } - protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) + protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) { base.OnActivityResult(requestCode, resultCode, data); @@ -194,7 +236,9 @@ namespace keepass2android AppTask.StartInGroupActivity(this); AppTask.SetupGroupBaseActivityButtons(this); - RefreshIfDirty(); + UpdateAutofillInfo(); + + RefreshIfDirty(); } public override bool OnSearchRequested() @@ -244,10 +288,20 @@ namespace keepass2android _prefs = PreferenceManager.GetDefaultSharedPreferences(this); + + SetContentView(ContentResourceId); - SetContentView(ContentResourceId); + if (FindViewById(Resource.Id.enable_autofill) != null) + { + FindViewById(Resource.Id.enable_autofill).Click += (sender, args) => + { + var intent = new Intent(Settings.ActionRequestSetAutofillService); + intent.SetData(Android.Net.Uri.Parse("package:" + PackageName)); + StartActivity(intent); + }; + } - if (FindViewById(Resource.Id.fabCancelAddNew) != null) + if (FindViewById(Resource.Id.fabCancelAddNew) != null) { FindViewById(Resource.Id.fabAddNew).Click += (sender, args) => { @@ -276,6 +330,10 @@ namespace keepass2android Util.MoveBottomBarButtons(Resource.Id.cancel_insert_element, Resource.Id.insert_element, Resource.Id.bottom_bar, this); } + + + + SetResult(KeePass.ExitNormal); @@ -283,7 +341,40 @@ namespace keepass2android } - protected virtual int ContentResourceId + private void UpdateAutofillInfo() + { + bool canShowAutofillInfo = false; + + if (!((Android.OS.Build.VERSION.SdkInt < Android.OS.BuildVersionCodes.O) || + !((AutofillManager) GetSystemService(Java.Lang.Class.FromType(typeof(AutofillManager)))) + .IsAutofillSupported)) + { + const string autofillservicewasenabled = "AutofillServiceWasEnabled"; + if (!((AutofillManager) GetSystemService(Java.Lang.Class.FromType(typeof(AutofillManager)))) + .HasEnabledAutofillServices) + { + //if (!_prefs.GetBoolean(autofillservicewasenabled, false)) + canShowAutofillInfo = true; + } + else + { + _prefs.Edit().PutBoolean(autofillservicewasenabled, true).Commit(); + + } + } + UpdateBottomBarElementVisibility(Resource.Id.autofill_infotext, canShowAutofillInfo); + } + + protected void UpdateBottomBarElementVisibility(int resourceId, bool canShow) + { + if (canShow) + showableBottomBarElements.Add(resourceId); + else + showableBottomBarElements.Remove(resourceId); + UpdateBottomBarVisibility(); + } + + protected virtual int ContentResourceId { get { return Resource.Layout.group; } } @@ -732,8 +823,9 @@ namespace keepass2android FindViewById(Resource.Id.fabAddNewEntry).Visibility = ViewStates.Gone; FindViewById(Resource.Id.fabAddNew).Visibility = ViewStates.Gone; - FindViewById(Resource.Id.bottom_bar).Visibility = ViewStates.Visible; - FindViewById(Resource.Id.divider2).Visibility = ViewStates.Visible; + UpdateBottomBarElementVisibility(Resource.Id.insert_element, true); + UpdateBottomBarElementVisibility(Resource.Id.cancel_insert_element, true); + } public void StopMovingElements() diff --git a/src/keepass2android/Resources/layout/group.xml b/src/keepass2android/Resources/layout/group.xml index d2247feb..96d0f834 100644 --- a/src/keepass2android/Resources/layout/group.xml +++ b/src/keepass2android/Resources/layout/group.xml @@ -10,6 +10,7 @@ android:layout_width="match_parent" android:layout_height="0dp" android:orientation="horizontal" /> + +