Compare commits
63 Commits
PhilippC-a
...
v1.09e-r5b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
554f88c723 | ||
|
|
4cd32d30c6 | ||
|
|
d0da83182f | ||
|
|
ec5f26e0cd | ||
|
|
6110166af8 | ||
|
|
6f10a04589 | ||
|
|
e0c003fcb2 | ||
|
|
944f44bc4b | ||
|
|
58047d5386 | ||
|
|
c0a06c9f3a | ||
|
|
d0c041a0e2 | ||
|
|
df060e2f4b | ||
|
|
aea55dad45 | ||
|
|
5442dbf441 | ||
|
|
317476d9b5 | ||
|
|
ad0acb7a69 | ||
|
|
b66ae5d264 | ||
|
|
d87706fa43 | ||
|
|
cb25d12709 | ||
|
|
dce536009e | ||
|
|
656e785214 | ||
|
|
35d50a6eb0 | ||
|
|
786bb646c2 | ||
|
|
72cc6ff768 | ||
|
|
404e07e5c0 | ||
|
|
1c7159ede9 | ||
|
|
2378cd0d7c | ||
|
|
b149bab761 | ||
|
|
5ebd8e5e33 | ||
|
|
db6b266a59 | ||
|
|
7de28c5aba | ||
|
|
ed79df0c6d | ||
|
|
bddef6442c | ||
|
|
ac5f3c9ca5 | ||
|
|
93e1cf1147 | ||
|
|
a805787a95 | ||
|
|
ca5f6dc43c | ||
|
|
0d4955622d | ||
|
|
886daa6b27 | ||
|
|
8fa0803474 | ||
|
|
4cad70e750 | ||
|
|
c29b789a2b | ||
|
|
cd34896661 | ||
|
|
1e02db86d6 | ||
|
|
994741cbf5 | ||
|
|
58844be6eb | ||
|
|
2d899fa067 | ||
|
|
060bf6a6ee | ||
|
|
890f1bd704 | ||
|
|
139abcaec6 | ||
|
|
78a48b75b8 | ||
|
|
3918b06b1f | ||
|
|
40847ebe31 | ||
|
|
34cac86a9b | ||
|
|
d8598a53e0 | ||
|
|
92d9eb1512 | ||
|
|
1be7b33f6b | ||
|
|
8464fa4f29 | ||
|
|
eff9a96bd5 | ||
|
|
bd4e321b0e | ||
|
|
47aaedbfb5 | ||
|
|
3043f8981d | ||
|
|
632121f3ec |
12
Makefile
12
Makefile
@@ -314,6 +314,18 @@ clean_KP2AKdbLibrary:
|
||||
cd src/java/KP2AKdbLibrary && $(GRADLEW) clean
|
||||
clean_PluginQR:
|
||||
cd src/java/PluginQR && $(GRADLEW) clean
|
||||
clean_rm:
|
||||
rm -rf src/*/obj
|
||||
rm -rf src/*/bin
|
||||
rm -rf src/java/*/app/build
|
||||
rm -rf src/java/argon2/obj
|
||||
rm -rf src/java/argon2/libs
|
||||
rm -rf src/packages
|
||||
rm -rf src/java/KP2AKdbLibrary/app/.cxx
|
||||
rm -rf src/java/KP2ASoftkeyboard_AS/app/.cxx
|
||||
rm -rf src/SamsungPass/Xamarin.SamsungPass/SamsungPass/bin
|
||||
rm -rf src/SamsungPass/Xamarin.SamsungPass/SamsungPass/obj
|
||||
|
||||
|
||||
# https://learn.microsoft.com/en-us/nuget/consume-packages/package-restore-troubleshooting#other-potential-conditions
|
||||
clean_nuget:
|
||||
|
||||
@@ -43,7 +43,7 @@ By using the command line, you can build on Windows, macOS or Linux.
|
||||
- On Debian, after having added the repo from above, install with `apt install -t <repo_name> mono-devel msbuild`. A value for `<repo_name>` could be `stable-buster` for example, depending on which one you chose. You could also install the `mono-complete` package if you prefer.
|
||||
|
||||
- Install Xamarin.Android
|
||||
- Option 1: Use the mono-project [CI builds](https://dev.azure.com/xamarin/public/_build/latest?definitionId=48&branchName=main&stageName=Linux)
|
||||
- ~~Option 1: Use the mono-project [CI builds](https://dev.azure.com/xamarin/public/_build/latest?definitionId=48&branchName=main&stageName=Linux)~~ **NOTE:** KP2A now requires Xamarin.Android v13, which is newer than the current CI build; until a more recent CI build is available, this option is unfortunately no longer viable.
|
||||
- Option 2: [Build it from source](https://github.com/xamarin/xamarin-android/blob/master/Documentation/README.md#building-from-source)
|
||||
|
||||
- Install NuGet package of your distribution
|
||||
@@ -64,9 +64,11 @@ This is done on the command line and requires the Android SDK & NDK and Java JDK
|
||||
### On Windows
|
||||
- Setup your environment:
|
||||
- Set these environment variables for Android's SDK & NDK
|
||||
- `ANDROID_HOME` (for example `set ANDROID_HOME=C:\PATH\TO\android-sdk\`)
|
||||
- `ANDROID_SDK_ROOT` (for example `set ANDROID_SDK_ROOT=C:\PATH\TO\android-sdk\`)
|
||||
- `ANDROID_NDK_ROOT` (for example `set ANDROID_NDK_ROOT=C:\PATH\TO\android-sdk\ndk\version\`)
|
||||
- `ANDROID_HOME` (for example `set ANDROID_HOME=C:\PATH\TO\android-sdk`)
|
||||
- `ANDROID_SDK_ROOT` (for example `set ANDROID_SDK_ROOT=C:\PATH\TO\android-sdk`)
|
||||
- `ANDROID_NDK_ROOT` (for example `set ANDROID_NDK_ROOT=C:\PATH\TO\android-sdk\ndk\version`)
|
||||
|
||||
**Note:** Care must be taken when setting the above variables to **not** include a trailing backslash in the path. A trailing backslash may cause `make` to fail.
|
||||
|
||||
**Note**: If the path to the Android SDK contains spaces, you **must** do one of these:
|
||||
- either put the Android SDK into a path without spaces.
|
||||
@@ -103,6 +105,10 @@ This is done on the command line and requires the Android SDK & NDK and Java JDK
|
||||
|
||||
- For building the java parts, it is suggested to keep a short name (e.g. "c:\projects\keepass2android") for the root project directory. Otherwise the Windows path length limit might be hit when building.
|
||||
- Before building the java parts, make sure you have set the ANDROID_HOME variable or create a local.properties file inside the directories with a gradlew file. It is recommended to use the same SDK location as that of the Xamarin build.
|
||||
- On some environments, `make` can fail to properly use the detected `MSBUILD` tools. This seems to be due to long pathnames and/or spaces in pathnames. It may be required to explicitly set the `MSBUILD` path using 8.3 "short" path notation:
|
||||
- Determine the location of `MSBUILD` (e.g. `C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe`)
|
||||
- [Generate the "short" path](https://superuser.com/a/728792) of that location (e.g.: `C:\PROGRA~1\MICROS~2\2022\COMMUN~1\MSBuild\Current\Bin\MSBuild.exe`)
|
||||
- When running `make` specify the location of ``MSBUILD` explicitly (e.g.: `make MSBUILD="C:\PROGRA~1\MICROS~2\2022\COMMUN~1\MSBuild\Current\Bin\MSBuild.exe`
|
||||
|
||||
|
||||
### On Linux/macOS
|
||||
|
||||
@@ -134,10 +134,10 @@ namespace Kp2aAutofillParser
|
||||
/// </summary>
|
||||
public class FilledAutofillFieldCollection<FieldT> where FieldT:InputField
|
||||
{
|
||||
public Dictionary<string, FilledAutofillField<FieldT>> HintMap { get; }
|
||||
public Dictionary<string, FilledAutofillField> HintMap { get; }
|
||||
public string DatasetName { get; set; }
|
||||
|
||||
public FilledAutofillFieldCollection(Dictionary<string, FilledAutofillField<FieldT>> hintMap, string datasetName = "")
|
||||
public FilledAutofillFieldCollection(Dictionary<string, FilledAutofillField> hintMap, string datasetName = "")
|
||||
{
|
||||
//recreate hint map making sure we compare case insensitive
|
||||
HintMap = BuildHintMap();
|
||||
@@ -149,9 +149,9 @@ namespace Kp2aAutofillParser
|
||||
public FilledAutofillFieldCollection() : this(BuildHintMap())
|
||||
{ }
|
||||
|
||||
private static Dictionary<string, FilledAutofillField<FieldT>> BuildHintMap()
|
||||
private static Dictionary<string, FilledAutofillField> BuildHintMap()
|
||||
{
|
||||
return new Dictionary<string, FilledAutofillField<FieldT>>(StringComparer.OrdinalIgnoreCase);
|
||||
return new Dictionary<string, FilledAutofillField>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -159,7 +159,7 @@ namespace Kp2aAutofillParser
|
||||
/// </summary>
|
||||
/// <returns>The add.</returns>
|
||||
/// <param name="filledAutofillField">Filled autofill field.</param>
|
||||
public void Add(FilledAutofillField<FieldT> filledAutofillField)
|
||||
public void Add(FilledAutofillField filledAutofillField)
|
||||
{
|
||||
foreach (string hint in filledAutofillField.AutofillHints)
|
||||
{
|
||||
@@ -323,6 +323,7 @@ namespace Kp2aAutofillParser
|
||||
AutofillHintPassword,
|
||||
AutofillHintUsername,
|
||||
W3cHints.HONORIFIC_PREFIX,
|
||||
W3cHints.EMAIL,
|
||||
W3cHints.NAME,
|
||||
W3cHints.GIVEN_NAME,
|
||||
W3cHints.ADDITIONAL_NAME,
|
||||
@@ -430,20 +431,26 @@ namespace Kp2aAutofillParser
|
||||
/// <summary>
|
||||
/// transforms hints by replacing some W3cHints by their Android counterparts and transforming everything to lowercase
|
||||
/// </summary>
|
||||
public static List<string> ConvertToCanonicalHints(string[] supportedHints)
|
||||
public static List<string> ConvertToCanonicalLowerCaseHints(string[] supportedHints)
|
||||
{
|
||||
List<string> result = new List<string>();
|
||||
foreach (string hint in supportedHints)
|
||||
{
|
||||
string canonicalHint;
|
||||
if (!hintToCanonicalReplacement.TryGetValue(hint, out canonicalHint))
|
||||
canonicalHint = hint;
|
||||
var canonicalHint = ToCanonicalHint(hint);
|
||||
result.Add(canonicalHint.ToLower());
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
public static string ToCanonicalHint(string hint)
|
||||
{
|
||||
string canonicalHint;
|
||||
if (!hintToCanonicalReplacement.TryGetValue(hint, out canonicalHint))
|
||||
canonicalHint = hint;
|
||||
return canonicalHint;
|
||||
}
|
||||
|
||||
public static int GetPartitionIndex(string hint)
|
||||
{
|
||||
for (int i = 0; i < partitionsOfCanonicalHints.Count; i++)
|
||||
@@ -565,7 +572,7 @@ namespace Kp2aAutofillParser
|
||||
}
|
||||
}
|
||||
|
||||
public class FilledAutofillField<FieldT> where FieldT : InputField
|
||||
public class FilledAutofillField
|
||||
{
|
||||
private string[] _autofillHints;
|
||||
public string TextValue { get; set; }
|
||||
@@ -604,13 +611,13 @@ namespace Kp2aAutofillParser
|
||||
public FilledAutofillField()
|
||||
{ }
|
||||
|
||||
public FilledAutofillField(FieldT inputField)
|
||||
public FilledAutofillField(InputField inputField)
|
||||
: this(inputField, inputField.AutofillHints)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public FilledAutofillField(FieldT inputField, string[] hints)
|
||||
public FilledAutofillField(InputField inputField, string[] hints)
|
||||
{
|
||||
|
||||
string[] rawHints = AutofillHintsHelper.FilterForSupportedHints(hints);
|
||||
@@ -657,7 +664,8 @@ namespace Kp2aAutofillParser
|
||||
|
||||
}
|
||||
}
|
||||
AutofillHints = AutofillHintsHelper.ConvertToCanonicalHints(hintList.ToArray()).ToArray();
|
||||
AutofillHints = AutofillHintsHelper.ConvertToCanonicalLowerCaseHints(hintList.ToArray()).ToArray();
|
||||
inputField.FillFilledAutofillValue(this);
|
||||
|
||||
|
||||
}
|
||||
@@ -672,7 +680,7 @@ namespace Kp2aAutofillParser
|
||||
if (this == obj) return true;
|
||||
if (obj == null || GetType() != obj.GetType()) return false;
|
||||
|
||||
FilledAutofillField<FieldT> that = (FilledAutofillField<FieldT>)obj;
|
||||
FilledAutofillField that = (FilledAutofillField)obj;
|
||||
|
||||
if (!TextValue?.Equals(that.TextValue) ?? that.TextValue != null)
|
||||
return false;
|
||||
@@ -699,8 +707,8 @@ namespace Kp2aAutofillParser
|
||||
/// </summary>
|
||||
public abstract class InputField
|
||||
{
|
||||
public string IdEntry { get; set; }
|
||||
public string Hint { get; set; }
|
||||
public string? IdEntry { get; set; }
|
||||
public string? Hint { get; set; }
|
||||
public string ClassName { get; set; }
|
||||
public string[] AutofillHints { get; set; }
|
||||
public bool IsFocused { get; set; }
|
||||
@@ -710,6 +718,8 @@ namespace Kp2aAutofillParser
|
||||
public string HtmlInfoTag { get; set; }
|
||||
public string HtmlInfoTypeAttribute { get; set; }
|
||||
|
||||
public abstract void FillFilledAutofillValue(FilledAutofillField filledField);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -819,7 +829,7 @@ namespace Kp2aAutofillParser
|
||||
// * if there is no such autofill hint, we use IsPassword to
|
||||
|
||||
HashSet<string> autofillHintsOfAllFields = autofillView.InputFields.Where(f => f.AutofillHints != null)
|
||||
.SelectMany(f => f.AutofillHints).ToHashSet();
|
||||
.SelectMany(f => f.AutofillHints).Select(AutofillHintsHelper.ToCanonicalHint).ToHashSet();
|
||||
bool hasLoginAutofillHints = autofillHintsOfAllFields.Intersect(_autofillHintsForLogin).Any();
|
||||
|
||||
if (hasLoginAutofillHints)
|
||||
@@ -829,9 +839,9 @@ namespace Kp2aAutofillParser
|
||||
string[] viewHints = viewNode.AutofillHints;
|
||||
if (viewHints == null)
|
||||
continue;
|
||||
if (viewHints.Intersect(_autofillHintsForLogin).Any())
|
||||
if (viewHints.Select(AutofillHintsHelper.ToCanonicalHint).Intersect(_autofillHintsForLogin).Any())
|
||||
{
|
||||
FieldsMappedToHints.Add(viewNode, viewHints);
|
||||
AddFieldToHintMap(viewNode, viewHints.Select(AutofillHintsHelper.ToCanonicalHint).ToHashSet().ToArray());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -839,6 +849,7 @@ namespace Kp2aAutofillParser
|
||||
else
|
||||
{
|
||||
//determine password fields, first by type, then by hint:
|
||||
List<FieldT> editTexts = autofillView.InputFields.Where(f => IsEditText(f)).ToList();
|
||||
List<FieldT> passwordFields = autofillView.InputFields.Where(f => IsEditText(f) && IsPassword(f)).ToList();
|
||||
if (!passwordFields.Any())
|
||||
{
|
||||
@@ -851,8 +862,10 @@ namespace Kp2aAutofillParser
|
||||
{
|
||||
foreach (var passwordField in passwordFields)
|
||||
{
|
||||
var lastInputBeforePassword = autofillView.InputFields
|
||||
.TakeWhile(f => IsEditText(f) && f != passwordField && !passwordFields.Contains(f)).LastOrDefault();
|
||||
|
||||
var lastInputBeforePassword = autofillView.InputFields.Where(IsEditText)
|
||||
.TakeWhile(f => f != passwordField && !passwordFields.Contains(f)).LastOrDefault();
|
||||
|
||||
if (lastInputBeforePassword != null)
|
||||
usernameFields.Add(lastInputBeforePassword);
|
||||
}
|
||||
@@ -863,9 +876,9 @@ namespace Kp2aAutofillParser
|
||||
if (passwordFields.Concat(usernameFields).Any(f => f.IsFocused))
|
||||
{
|
||||
foreach (var uf in usernameFields)
|
||||
FieldsMappedToHints.Add(uf, new string[] { AutofillHintsHelper.AutofillHintUsername });
|
||||
foreach (var pf in passwordFields)
|
||||
FieldsMappedToHints.Add(pf, new string[] { AutofillHintsHelper.AutofillHintPassword });
|
||||
AddFieldToHintMap(uf, new string[] { AutofillHintsHelper.AutofillHintUsername });
|
||||
foreach (var pf in passwordFields.Except(usernameFields))
|
||||
AddFieldToHintMap(pf, new string[] { AutofillHintsHelper.AutofillHintPassword });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -885,6 +898,18 @@ namespace Kp2aAutofillParser
|
||||
return result;
|
||||
}
|
||||
|
||||
private void AddFieldToHintMap(FieldT field, string[] hints)
|
||||
{
|
||||
if (FieldsMappedToHints.ContainsKey(field))
|
||||
{
|
||||
FieldsMappedToHints[field] = FieldsMappedToHints[field].Concat(hints).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
FieldsMappedToHints[field] = hints;
|
||||
}
|
||||
}
|
||||
|
||||
public bool LogAutofillView { get; set; }
|
||||
|
||||
private bool IsEditText(FieldT f)
|
||||
@@ -894,23 +919,22 @@ namespace Kp2aAutofillParser
|
||||
|| f.HtmlInfoTag == "input");
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> _passwordHints = new HashSet<string> { "password", "passwort"
|
||||
/*, "passwordAuto", "pswd"*/ };
|
||||
private static readonly HashSet<string> _passwordHints = new HashSet<string> { "password", "passwort", "passwordAuto", "pswd" };
|
||||
private static bool HasPasswordHint(InputField f)
|
||||
{
|
||||
return IsAny(f.IdEntry, _passwordHints) ||
|
||||
IsAny(f.Hint, _passwordHints);
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> _usernameHints = new HashSet<string> { "email", "e-mail", "username" };
|
||||
private static readonly HashSet<string> _usernameHints = new HashSet<string> { "email", "e-mail", "username", "user id" };
|
||||
|
||||
private static bool HasUsernameHint(InputField f)
|
||||
{
|
||||
return IsAny(f.IdEntry, _usernameHints) ||
|
||||
IsAny(f.Hint, _usernameHints);
|
||||
return IsAny(f.IdEntry?.ToLower(), _usernameHints) ||
|
||||
IsAny(f.Hint?.ToLower(), _usernameHints);
|
||||
}
|
||||
|
||||
private static bool IsAny(string value, IEnumerable<string> terms)
|
||||
private static bool IsAny(string? value, IEnumerable<string> terms)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
|
||||
@@ -18,6 +18,9 @@ namespace Kp2aAutofillParserTest
|
||||
class TestInputField: InputField
|
||||
{
|
||||
public string[] ExpectedAssignedHints { get; set; }
|
||||
public override void FillFilledAutofillValue(FilledAutofillField filledField)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -41,6 +44,27 @@ namespace Kp2aAutofillParserTest
|
||||
RunTestFromAutofillInput(resourceName, "org.mozilla.firefox", "www.amazon.it");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanDetectFieldsWithoutAutofillHints()
|
||||
{
|
||||
var resourceName = "Kp2aAutofillParserTest.chrome-android10-amazon-it.json";
|
||||
RunTestFromAutofillInput(resourceName, "com.android.chrome", "www.amazon.it");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DetectsUsernameFieldDespitePasswordAutoHint()
|
||||
{
|
||||
var resourceName = "Kp2aAutofillParserTest.com-ifs-banking-fiid3364-android13.json";
|
||||
RunTestFromAutofillInput(resourceName, "com.ifs.banking.fiid3364", null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DetectsEmailAutofillHint()
|
||||
{
|
||||
var resourceName = "Kp2aAutofillParserTest.com-expressvpn-vpn-android13.json";
|
||||
RunTestFromAutofillInput(resourceName, "com.expressvpn.vpn", null);
|
||||
}
|
||||
|
||||
private void RunTestFromAutofillInput(string resourceName, string expectedPackageName = null, string expectedWebDomain = null)
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="chrome-android10-amazon-it.json" />
|
||||
<None Remove="com-expressvpn-vpn-android13.json" />
|
||||
<None Remove="com-ifs-banking-fiid3364-android13.json" />
|
||||
<None Remove="com-servicenet-mobile-focused.json" />
|
||||
<None Remove="com-servicenet-mobile-no-focus.json" />
|
||||
<None Remove="firefox-amazon-it.json" />
|
||||
@@ -32,6 +35,15 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="chrome-android10-amazon-it.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="com-expressvpn-vpn-android13.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="com-ifs-banking-fiid3364-android13.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="firefox-amazon-it.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
|
||||
2018
src/Kp2aAutofillParserTest/chrome-android10-amazon-it.json
Normal file
2018
src/Kp2aAutofillParserTest/chrome-android10-amazon-it.json
Normal file
File diff suppressed because it is too large
Load Diff
226
src/Kp2aAutofillParserTest/com-expressvpn-vpn-android13.json
Normal file
226
src/Kp2aAutofillParserTest/com-expressvpn-vpn-android13.json
Normal file
@@ -0,0 +1,226 @@
|
||||
{
|
||||
"InputFields": [
|
||||
{
|
||||
"IdEntry": null,
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.FrameLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "action_bar_root",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.LinearLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "action_mode_bar_stub",
|
||||
"Hint": null,
|
||||
"ClassName": "android.view.View",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "layout",
|
||||
"Hint": null,
|
||||
"ClassName": "android.view.ViewGroup",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "textView2",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.TextView",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "emailLayout",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.LinearLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "email",
|
||||
"Hint": "E-Mail",
|
||||
"ClassName": "android.widget.EditText",
|
||||
"AutofillHints": [
|
||||
"email"
|
||||
],
|
||||
"IsFocused": true,
|
||||
"InputType": 33,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null,
|
||||
"ExpectedAssignedHints": [ "emailAddress" ]
|
||||
},
|
||||
{
|
||||
"IdEntry": null,
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.LinearLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": null,
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.LinearLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "passwordLayout",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.LinearLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "password",
|
||||
"Hint": "Passwort",
|
||||
"ClassName": "android.widget.EditText",
|
||||
"AutofillHints": [
|
||||
"password",
|
||||
"passwordAuto"
|
||||
],
|
||||
"IsFocused": false,
|
||||
"InputType": 129,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null,
|
||||
"ExpectedAssignedHints": [
|
||||
"password",
|
||||
"passwordAuto"
|
||||
]
|
||||
},
|
||||
{
|
||||
"IdEntry": null,
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.LinearLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": null,
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.LinearLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "textinput_suffix_text",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.TextView",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "forgotPassword",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.Button",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "amazonInfo",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.FrameLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "signInButtonBarrier",
|
||||
"Hint": null,
|
||||
"ClassName": "android.view.View",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "signIn",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.Button",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "newUser",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.Button",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "focusThief",
|
||||
"Hint": null,
|
||||
"ClassName": "android.view.View",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "activatingContainer",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.FrameLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
}
|
||||
],
|
||||
"PackageId": "com.expressvpn.vpn",
|
||||
"WebDomain": null
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
{
|
||||
"InputFields": [
|
||||
{
|
||||
"IdEntry": null,
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.FrameLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "action_bar_root",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.LinearLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "action_mode_bar_stub",
|
||||
"Hint": null,
|
||||
"ClassName": "android.view.View",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "loginParent",
|
||||
"Hint": null,
|
||||
"ClassName": "android.view.ViewGroup",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "rooted_device_error_screen",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.LinearLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "scroll",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.ScrollView",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "login_box_layout",
|
||||
"Hint": null,
|
||||
"ClassName": "android.view.ViewGroup",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "loginFragment_container_view",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.FrameLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "login_box",
|
||||
"Hint": null,
|
||||
"ClassName": "android.view.ViewGroup",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "Edt_UserId",
|
||||
"Hint": "User ID",
|
||||
"ClassName": "android.widget.EditText",
|
||||
"AutofillHints": [
|
||||
"passwordAuto"
|
||||
],
|
||||
"IsFocused": true,
|
||||
"InputType": 145,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null,
|
||||
"ExpectedAssignedHints": [ "username" ]
|
||||
},
|
||||
{
|
||||
"IdEntry": "login_save_userid_switch",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.CompoundButton",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "Edt_Password_layout",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.LinearLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "Edt_Password",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.EditText",
|
||||
"AutofillHints": [
|
||||
"passwordAuto"
|
||||
],
|
||||
"IsFocused": false,
|
||||
"InputType": 129,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null,
|
||||
"ExpectedAssignedHints": [ "password" ]
|
||||
},
|
||||
{
|
||||
"IdEntry": null,
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.LinearLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "textinput_prefix_text",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.TextView",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": null,
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.LinearLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "textinput_suffix_text",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.TextView",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "textinput_placeholder",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.TextView",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "forgot_login_btn",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.Button",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "Btn_Login",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.Button",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "login_fab_fragment_container",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.FrameLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "biometric_fragment_container",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.FrameLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "biometricLayout",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.LinearLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "login_menu_container",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.LinearLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "deposit_insurance_systems_textview",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.TextView",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "login_menu",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.LinearLayout",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "login_menu_item_border_right",
|
||||
"Hint": null,
|
||||
"ClassName": "android.view.View",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "login_menu_item_border_left",
|
||||
"Hint": null,
|
||||
"ClassName": "android.view.View",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "sign_up_link",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.Button",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "locations_link",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.Button",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
},
|
||||
{
|
||||
"IdEntry": "more_link",
|
||||
"Hint": null,
|
||||
"ClassName": "android.widget.Button",
|
||||
"AutofillHints": null,
|
||||
"IsFocused": false,
|
||||
"InputType": 0,
|
||||
"HtmlInfoTag": null,
|
||||
"HtmlInfoTypeAttribute": null
|
||||
}
|
||||
],
|
||||
"PackageId": "com.ifs.banking.fiid3364",
|
||||
"WebDomain": null
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import com.jcraft.jsch.UserInfo;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
public class SftpStorage extends JavaFileStorageBase {
|
||||
|
||||
@@ -323,6 +324,8 @@ public class SftpStorage extends JavaFileStorageBase {
|
||||
jsch = new JSch();
|
||||
ConnectionInfo ci = splitStringToConnectionInfo(filename);
|
||||
|
||||
Log.d("KP2AJFS", "init SFTP");
|
||||
|
||||
String base_dir = getBaseDir();
|
||||
jsch.setKnownHosts(base_dir + "/known_hosts");
|
||||
|
||||
@@ -340,7 +343,9 @@ public class SftpStorage extends JavaFileStorageBase {
|
||||
|
||||
}
|
||||
|
||||
Log.e("KP2AJFS[thread]", "getting session...");
|
||||
Session session = jsch.getSession(ci.username, ci.host, ci.port);
|
||||
Log.e("KP2AJFS", "creating SftpUserInfo");
|
||||
UserInfo ui = new SftpUserInfo(ci.password,_appContext);
|
||||
session.setUserInfo(ui);
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ public class SftpUserInfo implements UserInfo {
|
||||
builder.setContentText("SFTP prompt");
|
||||
builder.setSmallIcon(R.drawable.ic_logo_green_foreground);
|
||||
|
||||
|
||||
Handler h = new Handler() {
|
||||
public void handleMessage(Message M) {
|
||||
msg.copyFrom(M);
|
||||
@@ -51,8 +50,12 @@ public class SftpUserInfo implements UserInfo {
|
||||
intent.putExtra("keepass2android.sftp.prompt", text);
|
||||
intent.setData((Uri.parse("suckit://"+SystemClock.elapsedRealtime())));
|
||||
|
||||
|
||||
Log.e("KP2AJFS[thread]", "built after 2023-03-14");
|
||||
|
||||
int flags = 0;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
|
||||
Log.e("KP2AJFS[thread]", "Setting mutable flag...");
|
||||
flags |= PendingIntent.FLAG_MUTABLE;
|
||||
}
|
||||
PendingIntent contentIntent = PendingIntent.getActivity(_appContext, 0, intent, flags);
|
||||
|
||||
1
src/java/JavaFileStorage/settings.gradle
Normal file
1
src/java/JavaFileStorage/settings.gradle
Normal file
@@ -0,0 +1 @@
|
||||
include ':app'
|
||||
@@ -25,7 +25,7 @@
|
||||
<!-- Title for Latin keyboard input options dialog -->
|
||||
<string name="english_ime_input_options">Eingabeoptionen</string>
|
||||
<!-- Option to provide vibrate/haptic feedback on keypress -->
|
||||
<string name="vibrate_on_keypress">Vibrieren b. Tastendruck</string>
|
||||
<string name="vibrate_on_keypress">Bei Tastendruck vibrieren</string>
|
||||
<!-- Option to play back sound on keypress in soft keyboard -->
|
||||
<string name="sound_on_keypress">Ton bei Tastendruck</string>
|
||||
<!-- Option to pop up the character with a larger font above soft keyboard -->
|
||||
@@ -120,13 +120,13 @@
|
||||
<!-- Tutorial tip 2 - Touch and hold a key to view accents (examples) -->
|
||||
<string name="tip_to_view_accents"><b>\"Halten Sie eine Taste gedrückt, um Akzente anzuzeigen\"\n\"(ø, ö, ô, ó usw.).\"</b></string>
|
||||
<!-- Tutorial tip 3 - How to switch to number/symbol keyboard -->
|
||||
<string name="tip_to_open_symbols"><b>\"Wechseln Sie zu Ziffern und Symbolen, indem Sie diese Taste berühren.\"</b></string>
|
||||
<string name="tip_to_open_symbols"><b>„Wechseln Sie zu Ziffern und Symbolen, indem Sie diese Taste berühren.“</b></string>
|
||||
<!-- Tutorial tip 4 - How to switch back to alphabet keyboard -->
|
||||
<string name="tip_to_close_symbols"><b>\"Durch erneutes Drücken dieser Taste gelangen Sie zurück zu den Buchstaben.\"</b></string>
|
||||
<string name="tip_to_close_symbols"><b>„Durch erneutes Drücken dieser Taste gelangen Sie zurück zu den Buchstaben.“</b></string>
|
||||
<!-- Tutorial tip 5 - How to launch keyboard settings -->
|
||||
<string name="tip_to_launch_settings"><b>\"Halten Sie diese Taste gedrückt, um die Tastatureinstellungen, wie beispielsweise die automatische Vervollständigung, zu ändern.\"</b></string>
|
||||
<string name="tip_to_launch_settings"><b>„Halten Sie diese Taste gedrückt, um die Tastatureinstellungen, wie beispielsweise die automatische Vervollständigung, zu ändern.“</b></string>
|
||||
<!-- Tutorial tip 6 - Done with the tutorial -->
|
||||
<string name="tip_to_start_typing"><b>\"Probieren Sie es aus!\"</b></string>
|
||||
<string name="tip_to_start_typing"><b>„Probieren Sie es aus!“</b></string>
|
||||
<!-- Label for soft enter key when it performs GO action. Must be short to fit on key! -->
|
||||
<string name="label_go_key">Los</string>
|
||||
<!-- Label for soft enter key when it performs NEXT action. Must be short to fit on key! -->
|
||||
@@ -166,7 +166,7 @@
|
||||
<string name="voice_working">Vorgang läuft</string>
|
||||
<!-- Short message shown before the user should speak. -->
|
||||
<!-- Short message shown when a generic error occurs. -->
|
||||
<string name="voice_error">Fehler. Versuchen Sie es erneut..</string>
|
||||
<string name="voice_error">Fehler. Versuchen Sie es erneut.</string>
|
||||
<!-- Short message shown for a network error. -->
|
||||
<string name="voice_network_error">Keine Verbindung</string>
|
||||
<!-- Short message shown for a network error where the utterance was really long,
|
||||
@@ -184,9 +184,9 @@
|
||||
search is not installed. -->
|
||||
<string name="voice_not_installed">Sprachsuche nicht installiert</string>
|
||||
<!-- Short hint shown in candidate view to explain voice input. -->
|
||||
<string name="voice_swipe_hint"><b>\"Hinweis:\"</b>\" Ziehen Sie zum Sprechen den Finger über die Tastatur.\"</string>
|
||||
<string name="voice_swipe_hint"><b>„Hinweis:“</b>„ Ziehen Sie zum Sprechen den Finger über die Tastatur.“</string>
|
||||
<!-- Short hint shown in candidate view to explain that user can speak punctuation. -->
|
||||
<string name="voice_punctuation_hint"><b>\"Hinweis:\"</b>\" Versuchen Sie beim nächsten Mal, Satzzeichen wie \"Punkt\", \"Komma\" oder \"Fragezeichen\" per Sprachbefehl einzugeben.\"</string>
|
||||
<string name="voice_punctuation_hint"><b>„Hinweis:“</b>„ Versuchen Sie beim nächsten Mal, Satzzeichen wie „Punkt“, „Komma“ oder „Fragezeichen“ per Sprachbefehl einzugeben.“</string>
|
||||
<!-- Label on button to stop recognition. Must be short to fit on button. -->
|
||||
<string name="cancel">Abbrechen</string>
|
||||
<!-- Label on button when an error occurs -->
|
||||
@@ -214,13 +214,13 @@
|
||||
<string name="auto_submit_summary">Drücken Sie auf die Eingabetaste, wenn Sie einen Suchvorgang durchführen oder zum nächsten Feld wechseln.</string>
|
||||
<!-- IME Tutorial screen (ROMAN) -->
|
||||
<!-- appears above image showing the user to click on a TextView to show the IME -->
|
||||
<string name="open_the_keyboard"><font size="17"><b>\"Tastatur öffnen\"\n</b></font><font size="3">\n</font>\"Berühren Sie ein beliebiges Textfeld.\"</string>
|
||||
<string name="open_the_keyboard"><font size="17"><b>„Tastatur öffnen“\n</b></font><font size="3">\n</font>„Berühren Sie ein beliebiges Textfeld.“</string>
|
||||
<!-- appears above the image showing the back button used to close the keyboard -->
|
||||
<string name="close_the_keyboard"><font size="17"><b>\"Tastatur schließen\"\n</b></font><font size="3">\n</font>\"Drücken Sie die Zurücktaste.\"</string>
|
||||
<string name="close_the_keyboard"><font size="17"><b>„Tastatur schließen“\n</b></font><font size="3">\n</font>„Drücken Sie die Zurücktaste.“</string>
|
||||
<!-- appears above image showing how to use touch and hold -->
|
||||
<string name="touch_and_hold"><font size="17"><b>\"Für Optionen eine Taste berühren und gedrückt halten\"\n</b></font><font size="3">\n</font>\"Greifen Sie auf Satzzeichen und Akzente zu.\"</string>
|
||||
<string name="touch_and_hold"><font size="17"><b>„Für Optionen eine Taste berühren und gedrückt halten“\n</b></font><font size="3">\n</font>„Greifen Sie auf Satzzeichen und Akzente zu.“</string>
|
||||
<!-- appears above image showing how to access keyboard settings -->
|
||||
<string name="keyboard_settings"><font size="17"><b>\"Tastatureinstellungen\"\n</b></font><font size="3">\n</font>\"Berühren und halten Sie die Taste \"<b>\"?123\"</b>\" gedrückt.\"</string>
|
||||
<string name="keyboard_settings"><font size="17"><b>„Tastatureinstellungen“\n</b></font><font size="3">\n</font>„Berühren und halten Sie die Taste „<b>\"?123\"</b>“ gedrückt.“</string>
|
||||
<!-- popular web domains for the locale - most popular, displayed on the keyboard -->
|
||||
<string name="popular_domain_0">".com"</string>
|
||||
<!-- popular web domains for the locale - item 1, displayed in the popup -->
|
||||
|
||||
@@ -530,7 +530,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
|
||||
* parameters.
|
||||
*/
|
||||
private MatrixCursor doRetrieveFileInfo(Uri uri) {
|
||||
Log.d(CLASSNAME, "retrieve file info "+uri.toString());
|
||||
|
||||
MatrixCursor matrixCursor = BaseFileProviderUtils.newBaseFileCursor();
|
||||
|
||||
String filename = extractFile(uri);
|
||||
|
||||
@@ -56,15 +56,15 @@
|
||||
<string name="afc_title_sort_by">Sortieren nach…</string>
|
||||
<string name="afc_yesterday">Gestern</string>
|
||||
<plurals name="afc_title_choose_directories">
|
||||
<item quantity="one">Verzeichnis wählen…</item>
|
||||
<item quantity="one">Ordner wählen …</item>
|
||||
<item quantity="other">Verzeichnisse wählen…</item>
|
||||
</plurals>
|
||||
<plurals name="afc_title_choose_files">
|
||||
<item quantity="one">Datei wählen…</item>
|
||||
<item quantity="other">Dateien wählen…</item>
|
||||
<item quantity="other">Dateien wählen …</item>
|
||||
</plurals>
|
||||
<plurals name="afc_title_choose_files_directories">
|
||||
<item quantity="one">Datei/Ordner wählen…</item>
|
||||
<item quantity="other">Dateien/Ordner wählen…</item>
|
||||
<item quantity="one">Datei/Ordner wählen …</item>
|
||||
<item quantity="other">Dateien/Ordner wählen …</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:versionCode="190"
|
||||
android:versionName="1.09e-r2"
|
||||
android:versionCode="194"
|
||||
android:versionName="1.09e-r5b"
|
||||
package="keepass2android.keepass2android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:installLocation="auto">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:versionCode="190"
|
||||
android:versionName="1.09e-r2"
|
||||
android:versionCode="193"
|
||||
android:versionName="1.09e-r5"
|
||||
package="keepass2android.keepass2android_nonet"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:installLocation="auto">
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background_blue"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background_blue"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background_green"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background_green"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
<string name="AboutText">Keepass2Android er en adgangskodehåndterings-app, der giver læse-/skriveadgang til KeePass 2.x-databaser på Android.</string>
|
||||
<string name="CreditsText">Brugerfladen er baseret på en portering af KeePassDroid, udviklet af Brian Pellin. Koden til databaseoperationerne baserer sig på KeePass af Dominik Reichl. Android-robotten er reproduceret eller ændret fra arbejde skabt og delt af Google og anvendt jf. betingelserne beskrevet i \"Creative Commons 3.0 Attribution License\".</string>
|
||||
<string name="CreditsTextSFTP">SFTP-understøttelse er implementeret vha. JSch-biblioteket under BSD-licensen, skabt af JCraft, Inc.</string>
|
||||
<string name="CreditsIcons">Hammerikonet er kreéret af John Caserta fra Noun Project. Pingvinikonet er kreéret af Adriano Emerick fra Noun Project. Fjerikonet er kreéret af Jon Testa fra Noun Project. Æbleikonet er kreéret af Ava Rowell fra Noun Project. Billedikonet stammer fra https://icons8.com/icon/5570/Picture.</string>
|
||||
<string name="CreditsIcons">Hammerikonet er lavet af John Caserta fra Noun Project. Pingvinikonet er lavet af Adriano Emerick fra Noun Project. Fjerikonet er lavet af Jon Testa fra Noun Project. Æbleikonet er lavet af Ava Rowell fra Noun Project. Billedikonet er fra https://icons8.com/icon/5570/Picture.</string>
|
||||
<string name="accept">Acceptér</string>
|
||||
<string name="deny">Afvis</string>
|
||||
<string name="add_entry">Tilføj post</string>
|
||||
<string name="edit_entry">Redigér post</string>
|
||||
<string name="add_url_entry">Opret indtastning for URL</string>
|
||||
<string name="add_url_entry">Opret post for webadresse</string>
|
||||
<string name="add_group">Tilføj gruppe</string>
|
||||
<string name="add_group_title">Tilføj Gruppe</string>
|
||||
<string name="edit_group_title">Redigér gruppe</string>
|
||||
<string name="edit_group_title">Rediger gruppe</string>
|
||||
<string name="algorithm">Algoritme</string>
|
||||
<string name="algorithm_colon">Algoritme</string>
|
||||
<string name="app_name">Keepass2Android</string>
|
||||
@@ -32,31 +32,31 @@
|
||||
<string name="NavigationToGroupCompleted_message">Visningsgruppe er nu: %1$s</string>
|
||||
<string name="AutofillDisabledQueriesPreference_title">Deaktivér Autoudfyldmål</string>
|
||||
<string name="AutofillDisabledQueriesPreference_summary">Viser en liste over apps og websteder, hvor Autoudfyld er blevet deaktiveret</string>
|
||||
<string name="OfferSaveCredentials_summary">Hvis aktiveret, spørger Android, om du vil gemme akkreditiver, når du manuelt har angivet data i autofyldbare felter.</string>
|
||||
<string name="OfferSaveCredentials_summary">Hvis aktiveret spørger Android, om du vil gemme akkreditiver, når du manuelt har angivet data i autofyldbare felter.</string>
|
||||
<string name="OfferSaveCredentials_title">Tilbyd at gemme akkreditiver</string>
|
||||
<string name="ShowGroupInEntry_title">Vis gruppenavn i indtastningsvisning</string>
|
||||
<string name="unknown_uri_scheme">Beklager! Keepass2Android kan ikke håndtere den returnerede URI %1$s. Kontakt udvikleren!</string>
|
||||
<string name="unknown_uri_scheme">Beklager! Keepass2Android kan ikke håndtere den returnerede URI %1$s. Kontakt venligst udvikleren!</string>
|
||||
<string name="Entry_singular">Én indtastning</string>
|
||||
<string name="Entry_plural">%1$d poster</string>
|
||||
<string name="IconSet_title">Ikonsæt</string>
|
||||
<string name="IconSet_install">Find flere...</string>
|
||||
<string name="security_prefs">Sikkerhed</string>
|
||||
<string name="display_prefs">Visning</string>
|
||||
<string name="password_access_prefs">Adgangskodetilgang</string>
|
||||
<string name="password_access_prefs">Adgang til adgangskoden</string>
|
||||
<string name="QuickUnlock_prefs">Hurtigoplåsning</string>
|
||||
<string name="FileHandling_prefs">Filhåndtering</string>
|
||||
<string name="keyboard_prefs">Tastatur</string>
|
||||
<string name="export_prefs">Eksportere database...</string>
|
||||
<string name="export_prefs">Eksporter database…</string>
|
||||
<string name="fingerprint_prefs">Biometrisk oplåsning</string>
|
||||
<string name="import_db_prefs">Importere database til intern mappe</string>
|
||||
<string name="import_db_prefs">Importer database til intern mappe</string>
|
||||
<string name="import_keyfile_prefs">Importer nøglefilen til intern mappe</string>
|
||||
<string name="export_keyfile_prefs">Eksporter nøglefil fra intern mappe</string>
|
||||
<string name="keyboardswitch_prefs">Tastaturskiftning</string>
|
||||
<string name="keyboardswitch_prefs">Tastaturskifte</string>
|
||||
<string name="OnlyAvailableForLocalFiles">Kun tilgængelig for lokale filer.</string>
|
||||
<string name="FileIsInInternalDirectory">Fil lagres i den interne mappe.</string>
|
||||
<string name="DatabaseFileMoved">Databasefilen blev kopieret til den interne mappe. Tryk på OK for at åbne den fra den nye placering. Bemærk: Husk regelmæssigt at eksportere databasen til et sikkert lagermedie!</string>
|
||||
<string name="KeyfileMoved">Nøglefilen blev kopieret til den interne mappe. Inden du sletter den fra dens nuværende placering, så tjek at du har en god sikkerhedskopi!</string>
|
||||
<string name="KeyfileMoveRequiresRememberKeyfile">Kan ikke benytte den interne mappe, når nøglefilens placering ikke er husket. Ændr sikkerhedsindstillingerne.</string>
|
||||
<string name="FileIsInInternalDirectory">Fil er gemt i intern mappe.</string>
|
||||
<string name="DatabaseFileMoved">Databasefil blev kopieret til intern mappe. Tryk OK for at åbne fra den nye placering. Bemærk: Husk regelmæssigt at eksportere databasen til et sikkert lagermedie!</string>
|
||||
<string name="KeyfileMoved">Nøglefil blev kopieret til interne mappe. Før du sletter den fra den nuværende placering, så tjek at du har en god sikkerhedskopi!</string>
|
||||
<string name="KeyfileMoveRequiresRememberKeyfile">Kan ikke bruge intern mappe når nøglefilens placering ikke er husket. Ændr sikkerhedsindstillingerne.</string>
|
||||
<string name="unlock_database_button">Oplås</string>
|
||||
<string name="unlock_database_title">Oplås database</string>
|
||||
<string name="brackets">Parenteser</string>
|
||||
@@ -73,8 +73,8 @@
|
||||
<string name="available_through_keyboard">Post er tilgængelig via KP2A-tastaturet</string>
|
||||
<string name="app_language_pref_title">App-sprog</string>
|
||||
<string name="entry_is_available">er tilgængelig</string>
|
||||
<string name="not_possible_im_picker">Kunne ikke åbne dialogboksen til valg af inputmetode. Aktivér tastaturet manuelt.</string>
|
||||
<string name="please_activate_keyboard">Aktivér Keepass2Android-tastaturet i systemindstillingerne.</string>
|
||||
<string name="not_possible_im_picker">Kunne ikke åbne dialogboksen for valg af inputmetode. Aktiver venligst tastaturet manuelt.</string>
|
||||
<string name="please_activate_keyboard">Aktiver venligst Keepass2Android-tastaturet i systemindstillingerne.</string>
|
||||
<string name="creating_db_key">Opretter databasenøgle…</string>
|
||||
<string name="current_group">Aktuel Gruppe</string>
|
||||
<string name="current_group_root">Aktuel gruppe: Root</string>
|
||||
@@ -82,7 +82,7 @@
|
||||
<string name="digits">Cifre</string>
|
||||
<string name="disclaimer_formal">Keepass2Android leveres ABSOLUT UDEN GARANTI. Det er gratis software, og du er velkommen til at videredistribuere det jf. betingelserne i GPL version 2 eller senere.</string>
|
||||
<string name="ellipsis">\u2026</string>
|
||||
<string name="copy_to_clipboard">Kopiér til Upklipsholder</string>
|
||||
<string name="copy_to_clipboard">Kopiér til udklipsholder</string>
|
||||
<string name="SystemLanguage">Systemsprog</string>
|
||||
<string name="fingerprint_description">Verificér for at fortsætte</string>
|
||||
<string name="fingerprint_fatal">Kan ikke opsætte biometrisk oplåsning:</string>
|
||||
@@ -90,20 +90,20 @@
|
||||
<string name="fingerprint_success">Biometrisk verifikation lykkedes</string>
|
||||
<string name="fingerprint_os_error">Biometrisk oplåsning kræver Android 6.0 eller nyere.</string>
|
||||
<string name="fingerprint_hardware_error">Ingen biometrisk hardware fundet.</string>
|
||||
<string name="fingerprint_no_enrolled">Du har ikke konfigureret biometrisk verifikation på denne enhed. Gå til systemindstillinger.</string>
|
||||
<string name="fingerprint_no_enrolled">Du har ikke konfigureret biometrisk verifikation på denne enhed. Gå til systemindstillinger først.</string>
|
||||
<string name="disable_fingerprint_unlock">Deaktivér biometrisk oplåsning</string>
|
||||
<string name="enable_fingerprint_unlock">Aktivér fuld biometrisk oplåsning</string>
|
||||
<string name="enable_fingerprint_quickunlock">Aktivér biometrisk oplåsning for hurtig oplåsning</string>
|
||||
<string name="fingerprint_unlock_failed">Biometrisk oplåsning mislykkedes. Dekrypteringsnøglen blev ugyldiggjort af Android OS\'et. Dette sker sædvanligvis, hvis en biometrik godkendelse eller sikkerhedsindstillingerne ændres. </string>
|
||||
<string name="fingerprint_disabled_wrong_masterkey">Databaseoplåsning mislykkedes: Ugyldig kombinøgle. Biometrisk oplåsning blev deaktiveret, da den lagrede hovedadgangskode tilsyneladende ikke længere er gyldig. </string>
|
||||
<string name="fingerprint_reenable">Genaktivér biometrisk oplåsning for den nye hovedadgangskode.</string>
|
||||
<string name="fingerprint_unlock_failed">Biometrisk oplåsning mislykkedes. Dekrypteringsnøglen blev ugyldiggjort af Android OS. Det sker normalt, hvis en biometrik godkendelse eller sikkerhedsindstillinger ændres.</string>
|
||||
<string name="fingerprint_disabled_wrong_masterkey">Databaseoplåsning mislykkedes: Ugyldig kombinøgle. Biometrisk oplåsning blev deaktiveret, da den lagrede hovedadgangskode tilsyneladende ikke længere er gyldig.</string>
|
||||
<string name="fingerprint_reenable">Genaktivér venligst biometrisk oplåsning for den nye hovedadgangskode.</string>
|
||||
<string name="fingerprint_reenable2">Oplås med din adgangskode og genaktivér så biometrisk oplåsning i databaseindstillingerne.</string>
|
||||
<string name="FingerprintInitFailed">Kunne ikke initialisere biometrisk verifikation. </string>
|
||||
<string name="FingerprintSetupFailed">Mislykkedes at kryptere data. Dette kan ske, hvis du tilføjer eller fjerner fingeraftryk i systemindstillingerne, mens Keepass2Android moniterer for brug af fingeraftryk.</string>
|
||||
<string name="enable_fingerprint_unlock_Info">Dette gemmer din hovedadgangskode på denne enhed, krypteret med Android Keystore og beskyttet af biometrisk verifikation. Tillader dig at oplåse din database alene via biometri.</string>
|
||||
<string name="FingerprintInitFailed">Kunne ikke initialisere biometrisk verifikation.</string>
|
||||
<string name="FingerprintSetupFailed">Kryptering af data fejlede. Dette kan ske, hvis du tilføjer eller fjerner fingeraftryk i systemindstillingerne, mens Keepass2Android moniterer for brug af fingeraftryk.</string>
|
||||
<string name="enable_fingerprint_unlock_Info">Dette gemmer hovedadgangskoden på denne enhed, krypteret med Android Keystore og beskyttet af biometrisk verifikation. Tillader oplåsning af databasen alene med biometri.</string>
|
||||
<string name="enable_fingerprint_quickunlock_Info">Tillader brug af biometrisk verifikation i stedet for hurtigoplåsningskoden. Gemmer ingen information relateret til din hovedadgangskode.</string>
|
||||
<string name="enter_filename">Angiv databasefilnavn</string>
|
||||
<string name="entry_accessed">Tilgået</string>
|
||||
<string name="enter_filename">Angiv navn på databasefil</string>
|
||||
<string name="entry_accessed">Åbnet</string>
|
||||
<string name="entry_cancel">Annullér</string>
|
||||
<string name="entry_comment">Notater</string>
|
||||
<string name="entry_tags">Tags</string>
|
||||
@@ -120,27 +120,27 @@
|
||||
<string name="entry_title">Navn</string>
|
||||
<string name="entry_url">URL</string>
|
||||
<string name="entry_user_name">Brugernavn</string>
|
||||
<string name="entry_extra_strings">Ekstra strengfelter</string>
|
||||
<string name="entry_binaries">Filvedhæftninger</string>
|
||||
<string name="entry_extra_strings">Ekstra felter</string>
|
||||
<string name="entry_binaries">Vedhæftede filer</string>
|
||||
<string name="entry_history">Tidligere versioner</string>
|
||||
<string name="error_can_not_handle_uri">Keepass2Android kan ikke håndtere denne URI.</string>
|
||||
<string name="error_could_not_create_group">Fejl under gruppeoprettelse.</string>
|
||||
<string name="error_could_not_create_group">Fejl under oprettelse af gruppe.</string>
|
||||
<string name="error_could_not_create_parent">Overordnet mappe kunne ikke oprettes.</string>
|
||||
<string name="error_database_exists">Filen findes allerede.</string>
|
||||
<string name="error_database_exists">Denne fil eksisterer allerede.</string>
|
||||
<string name="error_database_settinoverrgs">Mislykkedes at bestemme databaseindstillinger.</string>
|
||||
<string name="error_failed_to_launch_link">Mislykkedes at åbne link.</string>
|
||||
<string name="error_filename_required">Et filnavn er obligatorisk.</string>
|
||||
<string name="error_filename_required">Et filnavn er påkrævet.</string>
|
||||
<string name="error_file_not_create">Kunne ikke oprette fil</string>
|
||||
<string name="error_invalid_db">Ugyldig database.</string>
|
||||
<string name="error_invalid_path">Ugyldig sti.</string>
|
||||
<string name="error_no_name">Et navn er obligatorisk.</string>
|
||||
<string name="error_nopass">En adgangskode eller nøglefil er obligatorisk.</string>
|
||||
<string name="error_no_name">Et navn er påkrævet.</string>
|
||||
<string name="error_nopass">En adgangskode eller nøglefil er påkrævet.</string>
|
||||
<string name="error_pass_gen_type">Mindst én adgangskodegenereringstype skal vælges</string>
|
||||
<string name="error_pass_match">Adgangskoder matcher ikke.</string>
|
||||
<string name="error_pass_match">Adgangskoder stemmer ikke overens.</string>
|
||||
<string name="error_rounds_not_number">Gentagelser skal udgøre et tal.</string>
|
||||
<string name="error_param_not_number">Parameter skal udgøre et tal.</string>
|
||||
<string name="error_title_required">En titel er obligatorisk.</string>
|
||||
<string name="error_wrong_length">Angiv et positivt heltal i længdefeltet</string>
|
||||
<string name="error_title_required">En titel er påkrævet.</string>
|
||||
<string name="error_wrong_length">Angiv et positivt helt tal i længdefeltet</string>
|
||||
<string name="FileNotFound">Fil ikke fundet.</string>
|
||||
<string name="file_browser">Filbrowser</string>
|
||||
<string name="generate_password">Generér adgangskode</string>
|
||||
@@ -216,7 +216,7 @@
|
||||
<string name="master_key_type">Vælg type af hovednøgle:</string>
|
||||
<string name="progress_create">Opretter ny database…</string>
|
||||
<string name="create_database">Opret database</string>
|
||||
<string name="progress_title">Behandler…</string>
|
||||
<string name="progress_title">Arbejder…</string>
|
||||
<string name="remember_keyfile_summary">Husker placeringen af nøglefiler</string>
|
||||
<string name="remember_keyfile_title">Gem nøglefil</string>
|
||||
<string name="remove_from_filelist">Fjern</string>
|
||||
@@ -228,7 +228,7 @@
|
||||
<string name="KeyDerivFunc">Nøgleafledningsfunktion</string>
|
||||
<string name="rounds">Krypteringsgentagelser</string>
|
||||
<string name="rounds_explaination">Flere krypteringsgentagelser giver øget beskyttelse imod brute force-angreb, men kan reduceredownload- og lagringstigheden mærkbart.</string>
|
||||
<string name="rounds_hint">repetitioner</string>
|
||||
<string name="rounds_hint">gentagelser</string>
|
||||
<string name="argon2memory">Hukommelse til Argon 2 (bytes)</string>
|
||||
<string name="argon2parallelism">Parallelisme til Argon 2</string>
|
||||
<string name="database_name">Databasenavn</string>
|
||||
@@ -240,10 +240,10 @@
|
||||
<string name="space">Mellemrum</string>
|
||||
<string name="search_label">Søg</string>
|
||||
<string name="show_password">Vis adgangskode</string>
|
||||
<string name="sort_menu">Sortér efter...</string>
|
||||
<string name="sort_name">Sortér efter navn</string>
|
||||
<string name="sort_menu">Sorter efter...</string>
|
||||
<string name="sort_name">Sorter efter navn</string>
|
||||
<string name="sort_db">Sortér efter oprettelsestidspunkt</string>
|
||||
<string name="sort_moddate">Sortér efter ændringsdato</string>
|
||||
<string name="sort_moddate">Sorter efter ændringsdato</string>
|
||||
<string name="sort_default">Behold standardrækkefølgen</string>
|
||||
<string name="special">Speciel</string>
|
||||
<string name="special_extended">Udvidet Speciel</string>
|
||||
@@ -685,6 +685,12 @@
|
||||
<string name="EntryChannel_desc">Notificering til forenkelse af adgang til den aktuelt valgte indtastning.</string>
|
||||
<string name="CloseDbAfterFailedAttempts">Luk database efter tre mislykkede forsøg med biometrisk oplåsning.</string>
|
||||
<string name="WarnFingerprintInvalidated">Advarsel! Biometrisk godkendelse kan ugyldiggøres af Android, f.eks. efter tilføjelse af et nyt fingeraftryk i dine enhedsindstillinger. Sørg for, at du altid ved, hvordan du låser op med din hovedadgangskode!</string>
|
||||
<string-array name="ChangeLog_1_09e">
|
||||
<item>Fejlrettelse til nedbrud og uventede log-outs</item>
|
||||
<item>Skift til ny SFTP-implementering, som understøtter moderne offentlige nøglealgoritmer såsom rsa-sha2-256</item>
|
||||
<item>Markér adgangskoder som følsomme ved kopiering til udklipsholder (Android 13)</item>
|
||||
<item>Autofill improvements</item>
|
||||
</string-array>
|
||||
<string-array name="ChangeLog_1_09d">
|
||||
<item>Tilføjet understøttelse af visning, fjernelse og gendannelse af sikkerhedskopierede poster</item>
|
||||
<item>Implementeret understøttelse af MEGA-skylager</item>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -301,6 +301,8 @@
|
||||
<string name="NoDalVerification_summary">Απενεργοποιεί τον έλεγχο αν ταιριάζει ο τομέας και το πακέτο εφαρμογής</string>
|
||||
<string name="InlineSuggestions_title">Ενσωμάτωση με πληκτρολόγιο</string>
|
||||
<string name="InlineSuggestions_summary">Δείχνει τις προτάσεις αυτόματης συμπλήρωσης ως γραμμές μέσα στο πρηκτρολόγιο</string>
|
||||
<string name="LogAutofillView_title">Προβολή αυτόματης συμπλήρωσης αρχείου καταγραφής</string>
|
||||
<string name="LogAutofillView_summary">Εγγραφή λεπτομερειών σχετικά με την προβολή αυτόματης συμπλήρωσης στο αρχείο καταγραφής αποσφαλμάτωσης (αν η καταγραφή αποσφαλμάτωσης είναι ενεργοποιημένη). Αυτές οι λεπτομέρειες μπορούν να σταλούν στον προγραμματιστή αν η αυτόματη συμπλήρωση δε λειτουργεί όπως αναμενόταν.</string>
|
||||
<string name="requires_android11">Απαιτείται Android 11 ή νεότερη έκδοση</string>
|
||||
<string name="kp2a_findUrl">Εύρεση συνθηματικού</string>
|
||||
<string name="excludeExpiredEntries">Εξαίρεση ληγμένων εγγραφών</string>
|
||||
@@ -682,6 +684,12 @@
|
||||
<string name="EntryChannel_desc">Ειδοποίηση για απλοποιημένη πρόσβαση στην τρέχουσα καταχώριση.</string>
|
||||
<string name="CloseDbAfterFailedAttempts">Κλείσιμο της βάσης δεδομένων μετά από 3 ανεπιτυχείς προσπάθειες βιομετρικού ξεκλειδώματος.</string>
|
||||
<string name="WarnFingerprintInvalidated">Προσοχή! Ο βιομετρικός έλεγχος ταυτότητας μπορεί να ακυρωθεί από το Android, π.χ. μετά την προσθήκη ενός νέου δακτυλικού αποτυπώματος στις ρυθμίσεις της συσκευής σας. Βεβαιωθείτε ότι ξέρετε πάντα πώς να ξεκλειδώσετε με τον κύριο κωδικό πρόσβασης!</string>
|
||||
<string-array name="ChangeLog_1_09e">
|
||||
<item>Διόρθωση σφάλματος για απότομα κλεισίματα εφαρμογής και μη αναμενόμενες αποσυνδέσεις</item>
|
||||
<item>Μετάβαση σε νέα υλοποίηση SFTP, υποστηρίζοντας σύγχρονους αλγόριθμους δημόσιου κλειδιού όπως rsa-sha2-256</item>
|
||||
<item>Μαρκάρισμα κωδικών πρόσβασης ως ευαίσθητοι κατά την αντιγραφή στο πρόχειρο (Android 13)</item>
|
||||
<item>Autofill improvements</item>
|
||||
</string-array>
|
||||
<string-array name="ChangeLog_1_09d">
|
||||
<item>Υποστηρίζεται πλέον προβολή, διαγραφή και ανάκτηση των διαγραμμένων εγγραφών</item>
|
||||
<item>Υλοποιήθηκε υποστήριξη για αποθήκευση στο νέφος MEGA </item>
|
||||
|
||||
@@ -687,6 +687,7 @@ Voici quelques conseils qui pourraient aider à diagnostiquer le problème : \n
|
||||
<item>Correction de bugs pour les plantages et les déconnexions inattendues</item>
|
||||
<item>Passage à une nouvelle implémentation SFTP, prenant en charge les algorithmes à clé publique modernes tels que rsa-sha2-256</item>
|
||||
<item>Marquer les mots de passe comme sensibles lors de la copie dans le presse-papiers (Android 13)</item>
|
||||
<item>Autofill improvements</item>
|
||||
</string-array>
|
||||
<string-array name="ChangeLog_1_09d">
|
||||
<item>Ajout du support pour la visualisation, la suppression et la restauration des sauvegardes d\'entrée</item>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<string name="ShowGroupInEntry_title">エントリー画面にグループ名を表示</string>
|
||||
<string name="unknown_uri_scheme">申し訳ありません! Keepass2Android は、返された URI %1$s を処理できませんでした。開発者にお問い合わせください!</string>
|
||||
<string name="Entry_singular">1 エントリー</string>
|
||||
<string name="Entry_plural">%1$d エントリー</string>
|
||||
<string name="Entry_plural">%1$d 件のエントリー</string>
|
||||
<string name="IconSet_title">アイコンセット</string>
|
||||
<string name="IconSet_install">さらに探す…</string>
|
||||
<string name="security_prefs">セキュリティ</string>
|
||||
@@ -690,6 +690,7 @@
|
||||
<item>クラッシュと突発的にログアウトするバグを修正</item>
|
||||
<item>rsa-sha2-256 などの最新の公開鍵アルゴリズムをサポートする、新しい SFTP 実装に切り替え</item>
|
||||
<item>パスワードをクリップボードにコピーしたとき、機密コンテンツとしてマークするよう変更 (Android 13)</item>
|
||||
<item>Autofill improvements</item>
|
||||
</string-array>
|
||||
<string-array name="ChangeLog_1_09d">
|
||||
<item>エントリーバックアップの表示、削除、および復元に対応</item>
|
||||
|
||||
@@ -690,6 +690,7 @@
|
||||
<item>Naprawa błędów awarii i nieoczekiwanych wylogowań</item>
|
||||
<item>Przełącz się na nową implementację SFTP, wspierając nowoczesne algorytmy klucza publicznego, takie jak rsa-sha2-256</item>
|
||||
<item>Oznacz hasła jako wrażliwe podczas kopiowania do schowka (Android 13)</item>
|
||||
<item>Autofill improvements</item>
|
||||
</string-array>
|
||||
<string-array name="ChangeLog_1_09d">
|
||||
<item>Dodano wsparcie dla przeglądania, usuwania i przywracania kopii zapasowych wpisów</item>
|
||||
|
||||
@@ -303,6 +303,8 @@
|
||||
<string name="NoDalVerification_summary">Desativa a checagem se domínio e pacote do app correspondem.</string>
|
||||
<string name="InlineSuggestions_title">Integrar com o teclado</string>
|
||||
<string name="InlineSuggestions_summary">Mostra sugestões de Autopreenchimento como opção na linha no teclado (se suportado pelo método de entrada)</string>
|
||||
<string name="LogAutofillView_title">Registrar exibição de preenchimento automático</string>
|
||||
<string name="LogAutofillView_summary">Escreva detalhes sobre a exibição de preenchimento automático para registro de depuração (se o registro de depuração estiver habilitado). Esses detalhes podem ser enviados ao desenvolvedor se o preenchimento automático não funcionar conforme o esperado.</string>
|
||||
<string name="requires_android11">Requer Android 11 ou posterior</string>
|
||||
<string name="kp2a_findUrl">Keepass2Android: Buscar senha</string>
|
||||
<string name="excludeExpiredEntries">Excluir entradas expiradas</string>
|
||||
@@ -690,6 +692,7 @@
|
||||
<item>Correção de bugs em falhas e logouts inesperados</item>
|
||||
<item>Mudar para nova implementação SFTP, suportando algoritmos de chave pública modernos como rsa-sha2-256</item>
|
||||
<item>Marcar senhas como confidenciais ao copiar para a área de transferência (Android 13)</item>
|
||||
<item>Autofill improvements</item>
|
||||
</string-array>
|
||||
<string-array name="ChangeLog_1_09d">
|
||||
<item>Adicionado suporte para visualização, remoção e restauração de backups de entrada</item>
|
||||
|
||||
@@ -686,6 +686,12 @@
|
||||
<string name="EntryChannel_desc">Notifikácia na zjednodušenie prístupu k práve vybranému záznamu.</string>
|
||||
<string name="CloseDbAfterFailedAttempts">Zavrieť databázu po troch neúspešných odomykaniach s biometriou</string>
|
||||
<string name="WarnFingerprintInvalidated">Varovanie! Biometrická autentifikácia môže byť zneplatnená systémom Android, nap. po pridaní nového odtlačku prsta do nastavení zariadenia. Vždy sa uistite, že viete ako odomknúť zariadenia primárnym heslom.</string>
|
||||
<string-array name="ChangeLog_1_09e">
|
||||
<item>Oprava chyby vedúcej k pádom a neočakávaným odhláseniam</item>
|
||||
<item>Prepnutie na novú implementáciu SFTP, s podporou pre moderné algoritmy verejných kľúčov, ako je rsa-sha2-256</item>
|
||||
<item>Označiť heslá ako citlivé údaje pri kopírovaní do schránky (Android 13)</item>
|
||||
<item>Autofill improvements</item>
|
||||
</string-array>
|
||||
<string-array name="ChangeLog_1_09d">
|
||||
<item>Pridaná podpora prehliadania, odstraňovania a obnovovania záloh záznamu</item>
|
||||
<item>Implementovaná podpora pre cloudové úložisko MEGA</item>
|
||||
|
||||
@@ -690,6 +690,7 @@
|
||||
<item>Popravek napak pri zrušitvah in nepričakovanih odjavah</item>
|
||||
<item>Preklopite na novo izvedbo SFTP, ki podpira sodobne algoritme javnih ključev, kot je rsa-sha2-256</item>
|
||||
<item>Označi gesla kot občutljiva pri kopiranju v odložišče (Android 13)</item>
|
||||
<item>Autofill improvements</item>
|
||||
</string-array>
|
||||
<string-array name="ChangeLog_1_09d">
|
||||
<item>Dodana podpora za ogled, odstranjevanje in obnavljanje varnostnih kopij vnosov</item>
|
||||
|
||||
@@ -690,6 +690,7 @@
|
||||
<item>Виправлено помилку зі збоями та несподіваними виходами</item>
|
||||
<item>Перехід на нову реалізацію SFTP з підтримкою сучасних алгоритмів публічного ключа, як-от rsa-sha2-256</item>
|
||||
<item>Позначати паролі як вразливі під час копіювання до буфера обміну (Android 13)</item>
|
||||
<item>Autofill improvements</item>
|
||||
</string-array>
|
||||
<string-array name="ChangeLog_1_09d">
|
||||
<item>Додано підтримку перегляду, видалення та відновлення резервних копій записів</item>
|
||||
|
||||
@@ -690,6 +690,7 @@
|
||||
<item>修正當機和意外登出的錯誤</item>
|
||||
<item>切換成新 SFTP 實作,支援現代公鑰演算法,如 rsa-sha2-256</item>
|
||||
<item>複製密碼到剪貼簿時設為機密 (Android 13)</item>
|
||||
<item>Autofill improvements</item>
|
||||
</string-array>
|
||||
<string-array name="ChangeLog_1_09d">
|
||||
<item>支援查看、移除和還原項目備份</item>
|
||||
|
||||
@@ -301,6 +301,8 @@
|
||||
<string name="NoDalVerification_summary">不检查域名和应用程序包是否匹配</string>
|
||||
<string name="InlineSuggestions_title">与键盘集成</string>
|
||||
<string name="InlineSuggestions_summary">将自动填充建议显示为键盘中的内联选项(如果输入法支持的话)</string>
|
||||
<string name="LogAutofillView_title">记录自动填充视图</string>
|
||||
<string name="LogAutofillView_summary">将自动填充视图的详情写入调试日志 (如果启用了调试日志)。 如果自动填充无法正常工作,这些细节可以发送给开发者。</string>
|
||||
<string name="requires_android11">需要 Android 11 或更高版本</string>
|
||||
<string name="kp2a_findUrl">找回密码</string>
|
||||
<string name="excludeExpiredEntries">排除过期的条目</string>
|
||||
@@ -689,6 +691,7 @@
|
||||
<item>修复崩溃和意外注销的 bug</item>
|
||||
<item>切换到新的 SFTP 实现,支持现代公钥算法,例如 rsa-sha2-256</item>
|
||||
<item>复制到剪贴板时将密码标记为敏感 (Android 13)</item>
|
||||
<item>Autofill improvements</item>
|
||||
</string-array>
|
||||
<string-array name="ChangeLog_1_09d">
|
||||
<item>新增查看、移除和恢复条目备份的功能</item>
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
<string name="AlwaysMergeOnConflict_key">AlwaysMergeOnConflict</string>
|
||||
<string name="NoDalVerification_key">NoDalVerification_key</string>
|
||||
<string name="InlineSuggestions_key">InlineSuggestions_key</string>
|
||||
<string name="LogAutofillView_key">LogAutofillView_key</string>
|
||||
<string name="algorithm_key">algorithm</string>
|
||||
<string name="app_key">app</string>
|
||||
<string name="app_timeout_key">app_timeout_key</string>
|
||||
|
||||
@@ -332,6 +332,9 @@
|
||||
|
||||
<string name="InlineSuggestions_title">Integrate with keyboard</string>
|
||||
<string name="InlineSuggestions_summary">Shows the autofill suggestions as inline options in the keyboard (if supported by the input method)</string>
|
||||
|
||||
<string name="LogAutofillView_title">Log autofill view</string>
|
||||
<string name="LogAutofillView_summary">Write details about the autofill view to debug log (if debug logging is enabled). These details can be sent to the developer if autofill does not work as expected.</string>
|
||||
<string name="requires_android11">Requires Android 11 or later</string>
|
||||
|
||||
<string name="kp2a_findUrl">Find password</string>
|
||||
@@ -850,6 +853,7 @@
|
||||
<item>Bug fix to crashes and unexpected log-outs</item>
|
||||
<item>Switch to new SFTP implementation, supporting modern public key algorithms such as rsa-sha2-256</item>
|
||||
<item>Mark passwords as sensitive when copying to clipboard (Android 13)</item>
|
||||
<item>Autofill improvements</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="ChangeLog_1_09d">
|
||||
|
||||
@@ -454,6 +454,15 @@
|
||||
android:title="@string/InlineSuggestions_title"
|
||||
android:key="@string/InlineSuggestions_key" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:enabled="true"
|
||||
android:persistent="true"
|
||||
android:summary="@string/LogAutofillView_summary"
|
||||
android:defaultValue="false"
|
||||
android:title="@string/LogAutofillView_title"
|
||||
android:key="@string/LogAutofillView_key" />
|
||||
|
||||
|
||||
<CheckBoxPreference
|
||||
android:enabled="true"
|
||||
android:persistent="true"
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace keepass2android.services.AutofillBase
|
||||
AutofillOptions = view.GetAutofillOptions();
|
||||
Focused = view.IsFocused;
|
||||
var supportedHints = AutofillHintsHelper.FilterForSupportedHints(autofillHints);
|
||||
var canonicalHints = AutofillHintsHelper.ConvertToCanonicalHints(supportedHints);
|
||||
var canonicalHints = AutofillHintsHelper.ConvertToCanonicalLowerCaseHints(supportedHints);
|
||||
SetHints(canonicalHints.ToArray());
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ namespace keepass2android.services.AutofillBase
|
||||
{
|
||||
foreach (AutofillFieldMetadata autofillFieldMetadata in autofillFieldMetadataCollection.GetFieldsForHint(hint))
|
||||
{
|
||||
FilledAutofillField<ViewNodeInputField> filledAutofillField;
|
||||
FilledAutofillField filledAutofillField;
|
||||
if (!filledAutofillFieldCollection.HintMap.TryGetValue(hint, out filledAutofillField) || (filledAutofillField == null))
|
||||
{
|
||||
continue;
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
|
||||
using Android.App.Assist;
|
||||
using Android.Content;
|
||||
using Android.Preferences;
|
||||
using Android.Views.Autofill;
|
||||
using DomainNameParser;
|
||||
using Kp2aAutofillParser;
|
||||
@@ -23,12 +24,12 @@ namespace keepass2android.services.AutofillBase
|
||||
InputType = (Kp2aAutofillParser.InputTypes) ((int)viewNode.InputType);
|
||||
HtmlInfoTag = viewNode.HtmlInfo?.Tag;
|
||||
HtmlInfoTypeAttribute = viewNode.HtmlInfo?.Attributes?.FirstOrDefault(p => p.First?.ToString() == "type")?.Second?.ToString();
|
||||
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public AssistStructure.ViewNode ViewNode { get; set; }
|
||||
|
||||
public void FillFilledAutofillValue(FilledAutofillField<ViewNodeInputField> filledField)
|
||||
public override void FillFilledAutofillValue(FilledAutofillField filledField)
|
||||
{
|
||||
AutofillValue autofillValue = ViewNode.AutofillValue;
|
||||
if (autofillValue != null)
|
||||
@@ -152,13 +153,18 @@ namespace keepass2android.services.AutofillBase
|
||||
_context = context;
|
||||
_structure = structure;
|
||||
AutofillFields = new AutofillFieldMetadataCollection();
|
||||
|
||||
LogAutofillView = PreferenceManager.GetDefaultSharedPreferences(context).GetBoolean(context.GetString(Resource.String.LogAutofillView_key), false);
|
||||
|
||||
}
|
||||
|
||||
protected override AutofillTargetId Parse(bool forFill, bool isManualRequest, AutofillView<ViewNodeInputField> autofillView)
|
||||
{
|
||||
if (autofillView == null)
|
||||
Kp2aLog.Log("Received null autofill view!");
|
||||
var result = base.Parse(forFill, isManualRequest, autofillView);
|
||||
|
||||
Kp2aLog.Log("Parsing done");
|
||||
|
||||
if (forFill)
|
||||
{
|
||||
foreach (var p in FieldsMappedToHints)
|
||||
@@ -166,8 +172,9 @@ namespace keepass2android.services.AutofillBase
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientFormData = new FilledAutofillFieldCollection<ViewNodeInputField>();
|
||||
foreach (var p in FieldsMappedToHints)
|
||||
ClientFormData.Add(new FilledAutofillField<ViewNodeInputField>(p.Key, p.Value));
|
||||
ClientFormData.Add(new FilledAutofillField(p.Key, p.Value));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -61,8 +61,8 @@ namespace keepass2android.services.Kp2aAutofill
|
||||
foreach (string key in pwEntryOutput.OutputStrings.GetKeys())
|
||||
{
|
||||
|
||||
FilledAutofillField<ViewNodeInputField> field =
|
||||
new FilledAutofillField<ViewNodeInputField>
|
||||
FilledAutofillField field =
|
||||
new FilledAutofillField
|
||||
{
|
||||
AutofillHints = GetCanonicalHintsFromKp2aField(key).ToArray(),
|
||||
TextValue = pwEntryOutput.OutputStrings.ReadSafe(key)
|
||||
@@ -73,8 +73,8 @@ namespace keepass2android.services.Kp2aAutofill
|
||||
if (IsCreditCard(pwEntry, context) && pwEntry.Expires)
|
||||
{
|
||||
DateTime expTime = pwEntry.ExpiryTime;
|
||||
FilledAutofillField<ViewNodeInputField> field =
|
||||
new FilledAutofillField<ViewNodeInputField>
|
||||
FilledAutofillField field =
|
||||
new FilledAutofillField
|
||||
{
|
||||
AutofillHints = new[] {View.AutofillHintCreditCardExpirationDate},
|
||||
DateValue = (long) (1000 * TimeUtil.SerializeUnix(expTime))
|
||||
@@ -82,7 +82,7 @@ namespace keepass2android.services.Kp2aAutofill
|
||||
fieldCollection.Add(field);
|
||||
|
||||
field =
|
||||
new FilledAutofillField<ViewNodeInputField>
|
||||
new FilledAutofillField
|
||||
{
|
||||
AutofillHints = new[] {View.AutofillHintCreditCardExpirationDay},
|
||||
TextValue = expTime.Day.ToString()
|
||||
@@ -90,7 +90,7 @@ namespace keepass2android.services.Kp2aAutofill
|
||||
fieldCollection.Add(field);
|
||||
|
||||
field =
|
||||
new FilledAutofillField<ViewNodeInputField>
|
||||
new FilledAutofillField
|
||||
{
|
||||
AutofillHints = new[] {View.AutofillHintCreditCardExpirationMonth},
|
||||
TextValue = expTime.Month.ToString()
|
||||
@@ -98,7 +98,7 @@ namespace keepass2android.services.Kp2aAutofill
|
||||
fieldCollection.Add(field);
|
||||
|
||||
field =
|
||||
new FilledAutofillField<ViewNodeInputField>
|
||||
new FilledAutofillField
|
||||
{
|
||||
AutofillHints = new[] {View.AutofillHintCreditCardExpirationYear},
|
||||
TextValue = expTime.Year.ToString()
|
||||
|
||||
@@ -1,956 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Formatting = System.Xml.Formatting;
|
||||
|
||||
namespace Kp2aAutofillParser
|
||||
{
|
||||
public class W3cHints
|
||||
{
|
||||
|
||||
// Supported W3C autofill tokens (https://html.spec.whatwg.org/multipage/forms.html#autofill)
|
||||
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 const string PREFIX_SECTION = "section-";
|
||||
public const string SHIPPING = "shipping";
|
||||
public const string BILLING = "billing";
|
||||
// W3C prefixes below...
|
||||
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 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()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// FilledAutofillFieldCollection is the model that holds all of the data on a client app's page,
|
||||
/// plus the dataset name associated with it.
|
||||
/// </summary>
|
||||
public class FilledAutofillFieldCollection<FieldT> where FieldT:InputField
|
||||
{
|
||||
public Dictionary<string, FilledAutofillField<FieldT>> HintMap { get; }
|
||||
public string DatasetName { get; set; }
|
||||
|
||||
public FilledAutofillFieldCollection(Dictionary<string, FilledAutofillField<FieldT>> hintMap, string datasetName = "")
|
||||
{
|
||||
//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(BuildHintMap())
|
||||
{ }
|
||||
|
||||
private static Dictionary<string, FilledAutofillField<FieldT>> BuildHintMap()
|
||||
{
|
||||
return new Dictionary<string, FilledAutofillField<FieldT>>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a filledAutofillField to the collection, indexed by all of its hints.
|
||||
/// </summary>
|
||||
/// <returns>The add.</returns>
|
||||
/// <param name="filledAutofillField">Filled autofill field.</param>
|
||||
public void Add(FilledAutofillField<FieldT> filledAutofillField)
|
||||
{
|
||||
foreach (string hint in filledAutofillField.AutofillHints)
|
||||
{
|
||||
if (AutofillHintsHelper.IsSupportedHint(hint))
|
||||
{
|
||||
HintMap.TryAdd(hint, filledAutofillField);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if with hints was helpsed, <c>false</c> otherwise.</returns>
|
||||
/// <param name="autofillHints">Autofill hints.</param>
|
||||
public bool HelpsWithHints(List<string> autofillHints)
|
||||
{
|
||||
for (int i = 0; i < autofillHints.Count; i++)
|
||||
{
|
||||
var autofillHint = autofillHints[i];
|
||||
if (HintMap.ContainsKey(autofillHint) && !HintMap[autofillHint].IsNull())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public class AutofillHintsHelper
|
||||
{
|
||||
public const string AutofillHint2faAppOtp = "2faAppOTPCode";
|
||||
public const string AutofillHintBirthDateDay = "birthDateDay";
|
||||
public const string AutofillHintBirthDateFull = "birthDateFull";
|
||||
public const string AutofillHintBirthDateMonth = "birthDateMonth";
|
||||
public const string AutofillHintBirthDateYear = "birthDateYear";
|
||||
public const string AutofillHintCreditCardExpirationDate = "creditCardExpirationDate";
|
||||
public const string AutofillHintCreditCardExpirationDay = "creditCardExpirationDay";
|
||||
public const string AutofillHintCreditCardExpirationMonth = "creditCardExpirationMonth";
|
||||
public const string AutofillHintCreditCardExpirationYear = "creditCardExpirationYear";
|
||||
public const string AutofillHintCreditCardNumber = "creditCardNumber";
|
||||
public const string AutofillHintCreditCardSecurityCode = "creditCardSecurityCode";
|
||||
public const string AutofillHintEmailAddress = "emailAddress";
|
||||
public const string AutofillHintEmailOtp = "emailOTPCode";
|
||||
public const string AutofillHintGender = "gender";
|
||||
public const string AutofillHintName = "name";
|
||||
public const string AutofillHintNewPassword = "newPassword";
|
||||
public const string AutofillHintNewUsername = "newUsername";
|
||||
public const string AutofillHintNotApplicable = "notApplicable";
|
||||
public const string AutofillHintPassword = "password";
|
||||
public const string AutofillHintPersonName = "personName";
|
||||
public const string AutofillHintPersonNameFAMILY = "personFamilyName";
|
||||
public const string AutofillHintPersonNameGIVEN = "personGivenName";
|
||||
public const string AutofillHintPersonNameMIDDLE = "personMiddleName";
|
||||
public const string AutofillHintPersonNameMIDDLE_INITIAL = "personMiddleInitial";
|
||||
public const string AutofillHintPersonNamePREFIX = "personNamePrefix";
|
||||
public const string AutofillHintPersonNameSUFFIX = "personNameSuffix";
|
||||
public const string AutofillHintPhone = "phone";
|
||||
public const string AutofillHintPhoneContryCode = "phoneCountryCode";
|
||||
public const string AutofillHintPostalAddressAPT_NUMBER = "aptNumber";
|
||||
public const string AutofillHintPostalAddressCOUNTRY = "addressCountry";
|
||||
public const string AutofillHintPostalAddressDEPENDENT_LOCALITY = "dependentLocality";
|
||||
public const string AutofillHintPostalAddressEXTENDED_ADDRESS = "extendedAddress";
|
||||
public const string AutofillHintPostalAddressEXTENDED_POSTAL_CODE = "extendedPostalCode";
|
||||
public const string AutofillHintPostalAddressLOCALITY = "addressLocality";
|
||||
public const string AutofillHintPostalAddressREGION = "addressRegion";
|
||||
public const string AutofillHintPostalAddressSTREET_ADDRESS = "streetAddress";
|
||||
public const string AutofillHintPostalCode = "postalCode";
|
||||
public const string AutofillHintPromoCode = "promoCode";
|
||||
public const string AutofillHintSMS_OTP = "smsOTPCode";
|
||||
public const string AutofillHintUPI_VPA = "upiVirtualPaymentAddress";
|
||||
public const string AutofillHintUsername = "username";
|
||||
public const string AutofillHintWifiPassword = "wifiPassword";
|
||||
public const string AutofillHintPhoneNational = "phoneNational";
|
||||
public const string AutofillHintPhoneNumber = "phoneNumber";
|
||||
public const string AutofillHintPhoneNumberDevice = "phoneNumberDevice";
|
||||
public const string AutofillHintPostalAddress = "postalAddress";
|
||||
|
||||
private static readonly HashSet<string> _allSupportedHints = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
AutofillHintCreditCardExpirationDate,
|
||||
AutofillHintCreditCardExpirationDay,
|
||||
AutofillHintCreditCardExpirationMonth,
|
||||
AutofillHintCreditCardExpirationYear,
|
||||
AutofillHintCreditCardNumber,
|
||||
AutofillHintCreditCardSecurityCode,
|
||||
AutofillHintEmailAddress,
|
||||
AutofillHintPhone,
|
||||
AutofillHintName,
|
||||
AutofillHintPassword,
|
||||
AutofillHintPostalAddress,
|
||||
AutofillHintPostalCode,
|
||||
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.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 List<HashSet<string>> partitionsOfCanonicalHints = new List<HashSet<string>>()
|
||||
{
|
||||
|
||||
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
AutofillHintEmailAddress,
|
||||
AutofillHintPhone,
|
||||
AutofillHintName,
|
||||
AutofillHintPassword,
|
||||
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<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
AutofillHintPostalAddress,
|
||||
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<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
AutofillHintCreditCardExpirationDate,
|
||||
AutofillHintCreditCardExpirationDay,
|
||||
AutofillHintCreditCardExpirationMonth,
|
||||
AutofillHintCreditCardExpirationYear,
|
||||
AutofillHintCreditCardNumber,
|
||||
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<string, string> hintToCanonicalReplacement = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{W3cHints.EMAIL, AutofillHintEmailAddress},
|
||||
{W3cHints.USERNAME, AutofillHintUsername},
|
||||
{W3cHints.CURRENT_PASSWORD, AutofillHintPassword},
|
||||
{W3cHints.NEW_PASSWORD, AutofillHintPassword},
|
||||
{W3cHints.CC_EXPIRATION_MONTH, AutofillHintCreditCardExpirationMonth },
|
||||
{W3cHints.CC_EXPIRATION_YEAR, AutofillHintCreditCardExpirationYear },
|
||||
{W3cHints.CC_EXPIRATION, AutofillHintCreditCardExpirationDate },
|
||||
{W3cHints.CC_NUMBER, AutofillHintCreditCardNumber },
|
||||
{W3cHints.CC_CSC, AutofillHintCreditCardSecurityCode },
|
||||
{W3cHints.POSTAL_CODE, AutofillHintPostalCode },
|
||||
|
||||
|
||||
};
|
||||
|
||||
public static bool IsSupportedHint(string hint)
|
||||
{
|
||||
return _allSupportedHints.Contains(hint);
|
||||
}
|
||||
|
||||
|
||||
public static string[] FilterForSupportedHints(string[] hints)
|
||||
{
|
||||
if (hints == null)
|
||||
return Array.Empty<string>();
|
||||
var filteredHints = new string[hints.Length];
|
||||
int i = 0;
|
||||
foreach (var hint in hints)
|
||||
{
|
||||
if (IsSupportedHint(hint))
|
||||
{
|
||||
filteredHints[i++] = hint;
|
||||
}
|
||||
|
||||
}
|
||||
var finalFilteredHints = new string[i];
|
||||
Array.Copy(filteredHints, 0, finalFilteredHints, 0, i);
|
||||
return finalFilteredHints;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// transforms hints by replacing some W3cHints by their Android counterparts and transforming everything to lowercase
|
||||
/// </summary>
|
||||
public static List<string> ConvertToCanonicalHints(string[] supportedHints)
|
||||
{
|
||||
List<string> result = new List<string>();
|
||||
foreach (string hint in supportedHints)
|
||||
{
|
||||
string canonicalHint;
|
||||
if (!hintToCanonicalReplacement.TryGetValue(hint, out canonicalHint))
|
||||
canonicalHint = hint;
|
||||
result.Add(canonicalHint.ToLower());
|
||||
}
|
||||
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<FieldT> FilterForPartition<FieldT>(FilledAutofillFieldCollection<FieldT> autofillFields, int partitionIndex) where FieldT: InputField
|
||||
{
|
||||
FilledAutofillFieldCollection<FieldT> filteredCollection =
|
||||
new FilledAutofillFieldCollection<FieldT> { 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;
|
||||
}
|
||||
|
||||
public static FilledAutofillFieldCollection<FieldT> FilterForPartition<FieldT>(FilledAutofillFieldCollection<FieldT> filledAutofillFieldCollection, List<string> autofillFieldsFocusedAutofillCanonicalHints) where FieldT: InputField
|
||||
{
|
||||
|
||||
//only apply partition data if we have FocusedAutofillCanonicalHints. This may be empty on buggy Firefox.
|
||||
if (autofillFieldsFocusedAutofillCanonicalHints.Any())
|
||||
{
|
||||
int partitionIndex = AutofillHintsHelper.GetPartitionIndex(autofillFieldsFocusedAutofillCanonicalHints.FirstOrDefault());
|
||||
return AutofillHintsHelper.FilterForPartition(filledAutofillFieldCollection, partitionIndex);
|
||||
}
|
||||
|
||||
return filledAutofillFieldCollection;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// This enum represents the Android.Text.InputTypes values. For testability, this is duplicated here.
|
||||
/// </summary>
|
||||
public enum InputTypes
|
||||
{
|
||||
ClassDatetime = 4,
|
||||
ClassNumber = 2,
|
||||
ClassPhone = 3,
|
||||
ClassText = 1,
|
||||
DatetimeVariationDate = 16,
|
||||
DatetimeVariationNormal = 0,
|
||||
DatetimeVariationTime = 32,
|
||||
MaskClass = 15,
|
||||
MaskFlags = 16773120,
|
||||
MaskVariation = 4080,
|
||||
Null = 0,
|
||||
NumberFlagDecimal = 8192,
|
||||
NumberFlagSigned = 4096,
|
||||
NumberVariationNormal = 0,
|
||||
NumberVariationPassword = 16,
|
||||
TextFlagAutoComplete = 65536,
|
||||
TextFlagAutoCorrect = 32768,
|
||||
TextFlagCapCharacters = 4096,
|
||||
TextFlagCapSentences = 16384,
|
||||
TextFlagCapWords = 8192,
|
||||
TextFlagEnableTextConversionSuggestions = 1048576,
|
||||
TextFlagImeMultiLine = 262144,
|
||||
TextFlagMultiLine = 131072,
|
||||
TextFlagNoSuggestions = 524288,
|
||||
TextVariationEmailAddress = 32,
|
||||
TextVariationEmailSubject = 48,
|
||||
TextVariationFilter = 176,
|
||||
TextVariationLongMessage = 80,
|
||||
TextVariationNormal = 0,
|
||||
TextVariationPassword = 128,
|
||||
TextVariationPersonName = 96,
|
||||
TextVariationPhonetic = 192,
|
||||
TextVariationPostalAddress = 112,
|
||||
TextVariationShortMessage = 64,
|
||||
TextVariationUri = 16,
|
||||
TextVariationVisiblePassword = 144,
|
||||
TextVariationWebEditText = 160,
|
||||
TextVariationWebEmailAddress = 208,
|
||||
TextVariationWebPassword = 224
|
||||
}
|
||||
|
||||
public interface IKp2aDigitalAssetLinksDataSource
|
||||
{
|
||||
bool IsTrustedApp(string packageName);
|
||||
bool IsTrustedLink(string domain, string targetPackage);
|
||||
bool IsEnabled();
|
||||
|
||||
}
|
||||
|
||||
class TimeUtil
|
||||
{
|
||||
private static DateTime? m_dtUnixRoot = null;
|
||||
public static DateTime ConvertUnixTime(double dtUnix)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!m_dtUnixRoot.HasValue)
|
||||
m_dtUnixRoot = (new DateTime(1970, 1, 1, 0, 0, 0, 0,
|
||||
DateTimeKind.Utc)).ToLocalTime();
|
||||
|
||||
return m_dtUnixRoot.Value.AddSeconds(dtUnix);
|
||||
}
|
||||
catch (Exception) { Debug.Assert(false); }
|
||||
|
||||
return DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
public class FilledAutofillField<FieldT> where FieldT : InputField
|
||||
{
|
||||
private string[] _autofillHints;
|
||||
public string TextValue { get; set; }
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns the autofill hints for the filled field. These are always lowercased for simpler string comparison.
|
||||
/// </summary>
|
||||
public string[] AutofillHints
|
||||
{
|
||||
get
|
||||
{
|
||||
return _autofillHints;
|
||||
}
|
||||
set
|
||||
{
|
||||
_autofillHints = value;
|
||||
for (int i = 0; i < _autofillHints.Length; i++)
|
||||
_autofillHints[i] = _autofillHints[i].ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public FilledAutofillField()
|
||||
{ }
|
||||
|
||||
public FilledAutofillField(FieldT inputField)
|
||||
: this(inputField, inputField.AutofillHints)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public FilledAutofillField(FieldT inputField, string[] hints)
|
||||
{
|
||||
|
||||
string[] rawHints = AutofillHintsHelper.FilterForSupportedHints(hints);
|
||||
List<string> hintList = new List<string>();
|
||||
|
||||
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];
|
||||
|
||||
if (i < rawHints.Length - 1)
|
||||
{
|
||||
nextHint = rawHints[i + 1];
|
||||
}
|
||||
}
|
||||
if (W3cHints.isW3cTypePrefix(hint) && nextHint != null && W3cHints.isW3cTypeHint(nextHint))
|
||||
{
|
||||
hint = nextHint;
|
||||
i++;
|
||||
|
||||
}
|
||||
if (W3cHints.isW3cAddressType(hint) && nextHint != null)
|
||||
{
|
||||
hint = nextHint;
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
// Then check if the "actual" hint is supported.
|
||||
if (AutofillHintsHelper.IsSupportedHint(hint))
|
||||
{
|
||||
hintList.Add(hint);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
AutofillHints = AutofillHintsHelper.ConvertToCanonicalHints(hintList.ToArray()).ToArray();
|
||||
|
||||
|
||||
}
|
||||
|
||||
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<FieldT> that = (FilledAutofillField<FieldT>)obj;
|
||||
|
||||
if (!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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for everything that is a input field which might (or might not) be autofilled.
|
||||
/// For testability, this is independent from Android classes like ViewNode
|
||||
/// </summary>
|
||||
public abstract class InputField
|
||||
{
|
||||
public string IdEntry { get; set; }
|
||||
public string Hint { get; set; }
|
||||
public string ClassName { get; set; }
|
||||
public string[] AutofillHints { get; set; }
|
||||
public bool IsFocused { get; set; }
|
||||
|
||||
public InputTypes InputType { get; set; }
|
||||
|
||||
public string HtmlInfoTag { get; set; }
|
||||
public string HtmlInfoTypeAttribute { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class AutofillView<TField> where TField : InputField
|
||||
{
|
||||
public List<TField> InputFields { get; set; } = new List<TField>();
|
||||
|
||||
public string PackageId { get; set; } = null;
|
||||
public string WebDomain { get; set; } = null;
|
||||
}
|
||||
|
||||
public interface ILogger
|
||||
{
|
||||
void Log(string x);
|
||||
}
|
||||
|
||||
public class StructureParserBase<FieldT> where FieldT: InputField
|
||||
{
|
||||
private readonly ILogger _log;
|
||||
private readonly IKp2aDigitalAssetLinksDataSource _digitalAssetLinksDataSource;
|
||||
|
||||
private readonly List<string> _autofillHintsForLogin = new List<string>
|
||||
{
|
||||
AutofillHintsHelper.AutofillHintPassword,
|
||||
AutofillHintsHelper.AutofillHintUsername,
|
||||
AutofillHintsHelper.AutofillHintEmailAddress
|
||||
};
|
||||
|
||||
public string PackageId { get; set; }
|
||||
|
||||
public Dictionary<FieldT, string[]> FieldsMappedToHints = new Dictionary<FieldT, string[]>();
|
||||
|
||||
public StructureParserBase(ILogger logger, IKp2aDigitalAssetLinksDataSource digitalAssetLinksDataSource)
|
||||
{
|
||||
_log = logger;
|
||||
_digitalAssetLinksDataSource = digitalAssetLinksDataSource;
|
||||
}
|
||||
|
||||
public class AutofillTargetId
|
||||
{
|
||||
public string PackageName { get; set; }
|
||||
|
||||
public string PackageNameWithPseudoSchema
|
||||
{
|
||||
get { return AndroidAppScheme + PackageName; }
|
||||
}
|
||||
|
||||
public const string AndroidAppScheme = "androidapp://";
|
||||
|
||||
public string WebDomain { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If PackageName and WebDomain are not compatible (by DAL or because PackageName is a trusted browser in which case we treat all domains as "compatible"
|
||||
/// we need to issue a warning. If we would fill credentials for the package, a malicious website could try to get credentials for the app.
|
||||
/// If we would fill credentials for the domain, a malicious app could get credentials for the domain.
|
||||
/// </summary>
|
||||
public bool IncompatiblePackageAndDomain { get; set; }
|
||||
|
||||
public string DomainOrPackage
|
||||
{
|
||||
get
|
||||
{
|
||||
return WebDomain ?? PackageNameWithPseudoSchema;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AutofillTargetId ParseForFill(bool isManual, AutofillView<FieldT> autofillView)
|
||||
{
|
||||
return Parse(true, isManual, autofillView);
|
||||
}
|
||||
|
||||
public AutofillTargetId ParseForSave(AutofillView<FieldT> autofillView)
|
||||
{
|
||||
return Parse(false, true, autofillView);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traverse AssistStructure and add ViewNode metadata to a flat list.
|
||||
/// </summary>
|
||||
/// <returns>The parse.</returns>
|
||||
/// <param name="forFill">If set to <c>true</c> for fill.</param>
|
||||
/// <param name="isManualRequest"></param>
|
||||
protected virtual AutofillTargetId Parse(bool forFill, bool isManualRequest, AutofillView<FieldT> autofillView)
|
||||
{
|
||||
AutofillTargetId result = new AutofillTargetId()
|
||||
{
|
||||
PackageName = autofillView.PackageId,
|
||||
WebDomain = autofillView.WebDomain
|
||||
};
|
||||
|
||||
_log.Log("parsing autofillStructure...");
|
||||
|
||||
//TODO remove from production
|
||||
_log.Log("will log the autofillStructure...");
|
||||
string debugInfo = JsonConvert.SerializeObject(autofillView, Newtonsoft.Json.Formatting.Indented);
|
||||
_log.Log("will log the autofillStructure... size is " + debugInfo.Length);
|
||||
_log.Log("This is the autofillStructure: \n\n " + debugInfo);
|
||||
|
||||
//go through each input field and determine username/password fields.
|
||||
//Depending on the target this can require more or less heuristics.
|
||||
// * if there is a valid & supported autofill hint, we assume that all fields which should be filled do have an appropriate Autofill hint
|
||||
// * if there is no such autofill hint, we use IsPassword to
|
||||
|
||||
HashSet<string> autofillHintsOfAllFields = autofillView.InputFields.Where(f => f.AutofillHints != null)
|
||||
.SelectMany(f => f.AutofillHints).ToHashSet();
|
||||
bool hasLoginAutofillHints = autofillHintsOfAllFields.Intersect(_autofillHintsForLogin).Any();
|
||||
|
||||
if (hasLoginAutofillHints)
|
||||
{
|
||||
foreach (var viewNode in autofillView.InputFields)
|
||||
{
|
||||
string[] viewHints = viewNode.AutofillHints;
|
||||
if (viewHints == null)
|
||||
continue;
|
||||
if (viewHints.Intersect(_autofillHintsForLogin).Any())
|
||||
{
|
||||
FieldsMappedToHints.Add(viewNode, viewHints);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//determine password fields, first by type, then by hint:
|
||||
List<FieldT> passwordFields = autofillView.InputFields.Where(f => IsEditText(f) && IsPassword(f)).ToList();
|
||||
if (!passwordFields.Any())
|
||||
{
|
||||
passwordFields = autofillView.InputFields.Where(f => IsEditText(f) && HasPasswordHint(f)).ToList();
|
||||
}
|
||||
|
||||
//determine username fields. Try by hint, if that fails use the one before the password
|
||||
List<FieldT> usernameFields = autofillView.InputFields.Where(f => IsEditText(f) && HasUsernameHint(f)).ToList();
|
||||
if (!usernameFields.Any())
|
||||
{
|
||||
foreach (var passwordField in passwordFields)
|
||||
{
|
||||
var lastInputBeforePassword = autofillView.InputFields
|
||||
.TakeWhile(f => IsEditText(f) && f != passwordField && !passwordFields.Contains(f)).LastOrDefault();
|
||||
if (lastInputBeforePassword != null)
|
||||
usernameFields.Add(lastInputBeforePassword);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//for "heuristic determination" we demand that one of the filled fields is focused:
|
||||
if (passwordFields.Concat(usernameFields).Any(f => f.IsFocused))
|
||||
{
|
||||
foreach (var uf in usernameFields)
|
||||
FieldsMappedToHints.Add(uf, new string[] { AutofillHintsHelper.AutofillHintUsername });
|
||||
foreach (var pf in passwordFields)
|
||||
FieldsMappedToHints.Add(pf, new string[] { AutofillHintsHelper.AutofillHintPassword });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!string.IsNullOrEmpty(autofillView.WebDomain) && _digitalAssetLinksDataSource.IsEnabled())
|
||||
{
|
||||
result.IncompatiblePackageAndDomain = !_digitalAssetLinksDataSource.IsTrustedLink(autofillView.WebDomain, result.PackageName);
|
||||
if (result.IncompatiblePackageAndDomain)
|
||||
{
|
||||
_log.Log($"DAL verification failed for {result.PackageName}/{result.WebDomain}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.IncompatiblePackageAndDomain = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool IsEditText(FieldT f)
|
||||
{
|
||||
return (f.ClassName == "android.widget.EditText"
|
||||
|| f.ClassName == "android.widget.AutoCompleteTextView"
|
||||
|| f.HtmlInfoTag == "input");
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> _passwordHints = new HashSet<string> { "password", "passwort"
|
||||
/*, "passwordAuto", "pswd"*/ };
|
||||
private static bool HasPasswordHint(InputField f)
|
||||
{
|
||||
return IsAny(f.IdEntry, _passwordHints) ||
|
||||
IsAny(f.Hint, _passwordHints);
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> _usernameHints = new HashSet<string> { "email", "e-mail", "username" };
|
||||
|
||||
private static bool HasUsernameHint(InputField f)
|
||||
{
|
||||
return IsAny(f.IdEntry, _usernameHints) ||
|
||||
IsAny(f.Hint, _usernameHints);
|
||||
}
|
||||
|
||||
private static bool IsAny(string value, IEnumerable<string> terms)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var lowerValue = value.ToLowerInvariant();
|
||||
return terms.Any(t => lowerValue == t);
|
||||
}
|
||||
|
||||
private static bool IsInputTypeClass(InputTypes inputType, InputTypes inputTypeClass)
|
||||
{
|
||||
if (!InputTypes.MaskClass.HasFlag(inputTypeClass))
|
||||
throw new Exception("invalid inputTypeClass");
|
||||
return (((int)inputType) & (int)InputTypes.MaskClass) == (int)(inputTypeClass);
|
||||
}
|
||||
private static bool IsInputTypeVariation(InputTypes inputType, InputTypes inputTypeVariation)
|
||||
{
|
||||
if (!InputTypes.MaskVariation.HasFlag(inputTypeVariation))
|
||||
throw new Exception("invalid inputTypeVariation");
|
||||
return (((int)inputType) & (int)InputTypes.MaskVariation) == (int)(inputTypeVariation);
|
||||
}
|
||||
|
||||
private static bool IsPassword(InputField f)
|
||||
{
|
||||
InputTypes inputType = f.InputType;
|
||||
|
||||
return
|
||||
(!f.IdEntry?.ToLowerInvariant().Contains("search") ?? true) &&
|
||||
(!f.Hint?.ToLowerInvariant().Contains("search") ?? true) &&
|
||||
(
|
||||
(IsInputTypeClass(inputType, InputTypes.ClassText)
|
||||
&&
|
||||
(
|
||||
IsInputTypeVariation(inputType, InputTypes.TextVariationPassword)
|
||||
|| IsInputTypeVariation(inputType, InputTypes.TextVariationVisiblePassword)
|
||||
|| IsInputTypeVariation(inputType, InputTypes.TextVariationWebPassword)
|
||||
)
|
||||
)
|
||||
|| (f.AutofillHints != null && f.AutofillHints.First() == "passwordAuto")
|
||||
|| (f.HtmlInfoTypeAttribute == "password")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user