Compare commits

..

63 Commits

Author SHA1 Message Date
Philipp Crocoll
554f88c723 add make target "clean_rm" to remove build artifacts using rm. Created a new release based on this clean to see if it helps with #2263. 2023-03-20 09:51:58 +01:00
Philipp Crocoll
4cd32d30c6 removing unused duplicated files 2023-03-20 09:47:38 +01:00
Philipp Crocoll
d0da83182f manifest for 1.09e-r5 2023-03-19 10:01:25 +01:00
Philipp Crocoll
ec5f26e0cd reduce log output 2023-03-18 20:34:45 +01:00
Philipp Crocoll
6110166af8 code simplification and fix for Autofill not being able to save credentials, closes https://github.com/PhilippC/keepass2android/issues/2269 2023-03-16 20:27:22 +01:00
Philipp Crocoll
6f10a04589 revert most of the added debug outputs 2023-03-16 20:25:57 +01:00
Philipp Crocoll
e0c003fcb2 add debugging output 2023-03-14 20:19:08 +01:00
Philipp Crocoll
944f44bc4b Manifest for v1.09e-r4 2023-03-09 20:45:25 +01:00
Philipp Crocoll
58047d5386 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2023-03-09 20:44:29 +01:00
PhilippC
c0a06c9f3a Merge pull request #2264 from PhilippC/l10n_master2
New Crowdin updates
2023-03-09 20:44:02 +01:00
Philipp Crocoll
d0c041a0e2 remove debugging code 2023-03-09 20:16:24 +01:00
PhilippC
df060e2f4b New translations strings.xml (Portuguese, Brazilian) 2023-03-09 19:56:04 +01:00
PhilippC
aea55dad45 New translations strings.xml (Chinese Traditional) 2023-03-09 19:56:01 +01:00
PhilippC
5442dbf441 New translations strings.xml (Chinese Simplified) 2023-03-09 19:56:00 +01:00
PhilippC
317476d9b5 New translations strings.xml (Ukrainian) 2023-03-09 19:55:59 +01:00
PhilippC
ad0acb7a69 New translations strings.xml (Slovenian) 2023-03-09 19:55:56 +01:00
PhilippC
b66ae5d264 New translations strings.xml (Polish) 2023-03-09 19:55:53 +01:00
PhilippC
d87706fa43 New translations strings.xml (Japanese) 2023-03-09 19:55:51 +01:00
PhilippC
cb25d12709 New translations strings.xml (Greek) 2023-03-09 19:55:46 +01:00
PhilippC
dce536009e New translations strings.xml (German) 2023-03-09 19:55:45 +01:00
PhilippC
656e785214 New translations strings.xml (Danish) 2023-03-09 19:55:44 +01:00
PhilippC
35d50a6eb0 New translations strings.xml (French) 2023-03-09 19:55:39 +01:00
PhilippC
786bb646c2 New translations strings.xml (Slovak) 2023-03-09 19:55:37 +01:00
Philipp Crocoll
72cc6ff768 wrap adding fields and hints to dictionary, avoiding to add a duplicate key. Should close #2262 (but I can't reproduce) 2023-03-09 19:46:28 +01:00
Philipp Crocoll
404e07e5c0 Merge remote-tracking branch 'remotes/origin/l10n_master2' 2023-03-09 19:40:36 +01:00
Philipp Crocoll
1c7159ede9 changelog and manifest for 1.09e-r3 2023-03-09 19:39:51 +01:00
PhilippC
2378cd0d7c New translations strings.xml (Greek) 2023-03-08 16:54:54 +01:00
PhilippC
b149bab761 New translations strings.xml (Chinese Simplified) 2023-03-07 03:36:36 +01:00
PhilippC
5ebd8e5e33 New translations strings.xml (Portuguese, Brazilian) 2023-03-06 14:12:17 +01:00
Philipp Crocoll
db6b266a59 add more autofill tests and change AutofillParser to make them pass 2023-03-06 10:26:58 +01:00
Philipp Crocoll
7de28c5aba add preference to control if autofill view details are written to log 2023-03-06 10:26:33 +01:00
PhilippC
ed79df0c6d Merge pull request #2254 from PhilippC/PhilippC-autofill-testing-and-improvements
Autofill testing and improvements
2023-03-06 08:28:42 +01:00
PhilippC
bddef6442c Merge pull request #2251 from WreckingBANG/master
Added Monochrome Icon
2023-03-06 07:59:11 +01:00
PhilippC
ac5f3c9ca5 Merge pull request #2244 from PhilippC/l10n_master2
New Crowdin updates
2023-03-04 09:08:30 +01:00
PhilippC
93e1cf1147 Merge pull request #2240 from hyproman/java-file-storage-android-studio
Get JavaFileStorage working in Android Studio
2023-03-04 09:01:19 +01:00
PhilippC
a805787a95 Merge pull request #2248 from hyproman/build-doc-update
Update build documentation
2023-03-04 09:00:56 +01:00
PhilippC
ca5f6dc43c New translations strings.xml (Slovak) 2023-02-24 20:38:56 +01:00
WreckingBANG
0d4955622d Delete main.yml 2023-02-23 15:04:55 +01:00
WreckingBANG
886daa6b27 Update main.yml 2023-02-23 15:04:18 +01:00
WreckingBANG
8fa0803474 Create main.yml 2023-02-23 15:03:48 +01:00
WreckingBANG
4cad70e750 Update ic_launcher_offline_round.xml 2023-02-23 14:59:54 +01:00
WreckingBANG
c29b789a2b Update ic_launcher_offline.xml 2023-02-23 14:59:40 +01:00
WreckingBANG
cd34896661 Update ic_launcher_online_round.xml 2023-02-23 14:59:31 +01:00
WreckingBANG
1e02db86d6 Update ic_launcher_online.xml 2023-02-23 14:59:13 +01:00
Rick Brown
994741cbf5 Update build documentation based on my experience 2023-02-19 22:47:29 -05:00
PhilippC
58844be6eb New translations strings.xml (Danish) 2023-02-19 23:50:16 +01:00
PhilippC
2d899fa067 New translations strings.xml (Danish) 2023-02-19 22:32:16 +01:00
PhilippC
060bf6a6ee New translations strings.xml (Danish) 2023-02-19 21:31:30 +01:00
PhilippC
890f1bd704 New translations strings.xml (Danish) 2023-02-19 20:33:39 +01:00
PhilippC
139abcaec6 New translations strings.xml (German) 2023-02-18 23:26:56 +01:00
PhilippC
78a48b75b8 New translations strings.xml (German) 2023-02-18 22:13:41 +01:00
PhilippC
3918b06b1f New translations strings.xml (German) 2023-02-18 21:04:46 +01:00
PhilippC
40847ebe31 New translations strings.xml (German) 2023-02-18 19:55:30 +01:00
PhilippC
34cac86a9b New translations strings.xml (German) 2023-02-18 18:58:40 +01:00
PhilippC
d8598a53e0 New translations strings.xml (Japanese) 2023-02-18 17:41:17 +01:00
PhilippC
92d9eb1512 New translations strings.xml (German) 2023-02-18 11:15:28 +01:00
PhilippC
1be7b33f6b New translations strings.xml (German) 2023-02-18 10:09:38 +01:00
PhilippC
8464fa4f29 New translations strings.xml (German) 2023-02-18 09:11:16 +01:00
PhilippC
eff9a96bd5 New translations strings.xml (German) 2023-02-18 07:44:12 +01:00
PhilippC
bd4e321b0e New translations strings.xml (German) 2023-02-18 06:48:57 +01:00
PhilippC
47aaedbfb5 New translations strings.xml (German) 2023-02-18 06:48:56 +01:00
PhilippC
3043f8981d New translations strings.xml (German) 2023-02-18 06:48:55 +01:00
Rick Brown
632121f3ec Get JavaFileStorage working in Android Studio
Resolve issue where AS would fail to import Android API jar
2023-02-11 18:34:20 -05:00
41 changed files with 3101 additions and 1358 deletions

View File

@@ -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:

View File

@@ -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

View File

@@ -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))
{

View File

@@ -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();

View File

@@ -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>

File diff suppressed because it is too large Load Diff

View 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
}

View File

@@ -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
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -0,0 +1 @@
include ':app'

View File

@@ -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 -->

View File

@@ -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);

View File

@@ -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&#8230;</item>
<item quantity="one">Ordner wählen …</item>
<item quantity="other">Verzeichnisse wählen&#8230;</item>
</plurals>
<plurals name="afc_title_choose_files">
<item quantity="one">Datei wählen&#8230;</item>
<item quantity="other">Dateien wählen&#8230;</item>
<item quantity="other">Dateien wählen</item>
</plurals>
<plurals name="afc_title_choose_files_directories">
<item quantity="one">Datei/Ordner wählen&#8230;</item>
<item quantity="other">Dateien/Ordner wählen&#8230;</item>
<item quantity="one">Datei/Ordner wählen</item>
<item quantity="other">Dateien/Ordner wählen</item>
</plurals>
</resources>

View File

@@ -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">

View File

@@ -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">

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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">

View File

@@ -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"

View File

@@ -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());
}

View File

@@ -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;

View File

@@ -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));
}

View File

@@ -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()

View File

@@ -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")
);
}
}
}

View File

@@ -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>